Refactor PlansActions to use startWorkflow
Change-Id: I417e20d00c060ae8c0424e976c520a27d0c92b7b Closes-Bug: #1753474
This commit is contained in:
parent
cbfb4fac9c
commit
a9778fa6d4
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixes `Bug 1753474 <https://bugs.launchpad.net/tripleo/+bug/1753474>`__
|
||||
Running Mistral workflows is now more robust. In case when UI does not receive
|
||||
a message about a workflow being finished, it will start periodically checking
|
||||
the workflow execution to proactively get the result
|
|
@ -15,17 +15,22 @@
|
|||
*/
|
||||
|
||||
import MistralApiService from '../../js/services/MistralApiService';
|
||||
import MistralConstants from '../../js/constants/MistralConstants';
|
||||
import { mockStore } from './utils';
|
||||
import mockHistory from '../mocks/history';
|
||||
import PlansActions from '../../js/actions/PlansActions';
|
||||
import SwiftApiService from '../../js/services/SwiftApiService';
|
||||
import storage from '../mocks/storage';
|
||||
import * as WorkflowActions from '../../js/actions/WorkflowActions';
|
||||
|
||||
window.localStorage = window.sessionStorage = storage;
|
||||
|
||||
describe('PlansActions', () => {
|
||||
describe('updatePlan', () => {
|
||||
const store = mockStore({});
|
||||
const execution = {
|
||||
id: 'some-uuid'
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
MistralApiService.runWorkflow = jest
|
||||
|
@ -34,6 +39,9 @@ describe('PlansActions', () => {
|
|||
SwiftApiService.createObject = jest
|
||||
.fn()
|
||||
.mockReturnValue(() => Promise.resolve());
|
||||
WorkflowActions.startWorkflow = jest
|
||||
.fn()
|
||||
.mockReturnValue(() => Promise.resolve(execution));
|
||||
});
|
||||
|
||||
it('dispatches actions', () =>
|
||||
|
@ -44,7 +52,14 @@ describe('PlansActions', () => {
|
|||
})
|
||||
)
|
||||
.then(() => {
|
||||
expect(MistralApiService.runWorkflow).toHaveBeenCalled();
|
||||
expect(WorkflowActions.startWorkflow).toHaveBeenCalledWith(
|
||||
MistralConstants.PLAN_UPDATE,
|
||||
{
|
||||
container: 'somecloud'
|
||||
},
|
||||
expect.any(Function),
|
||||
2 * 60 * 1000
|
||||
);
|
||||
expect(store.getActions()).toEqual([
|
||||
PlansActions.updatePlanPending('somecloud')
|
||||
]);
|
||||
|
@ -53,21 +68,29 @@ describe('PlansActions', () => {
|
|||
|
||||
describe('createPlan', () => {
|
||||
const store = mockStore({});
|
||||
const execution = {
|
||||
id: 'some-uuid'
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
MistralApiService.runAction = jest
|
||||
.fn()
|
||||
.mockReturnValue(() => Promise.resolve());
|
||||
MistralApiService.runWorkflow = jest
|
||||
WorkflowActions.startWorkflow = jest
|
||||
.fn()
|
||||
.mockReturnValue(() => Promise.resolve());
|
||||
SwiftApiService.createObject = jest
|
||||
.fn()
|
||||
.mockReturnValue(() => Promise.resolve());
|
||||
.mockReturnValue(() => Promise.resolve(execution));
|
||||
});
|
||||
|
||||
it('dispatches actions', () =>
|
||||
store.dispatch(PlansActions.createPlan('somecloud', {})).then(() => {
|
||||
expect(WorkflowActions.startWorkflow).toHaveBeenCalledWith(
|
||||
MistralConstants.PLAN_CREATE,
|
||||
{
|
||||
container: 'somecloud'
|
||||
},
|
||||
expect.any(Function),
|
||||
2 * 60 * 1000
|
||||
);
|
||||
expect(store.getActions()).toEqual([PlansActions.createPlanPending()]);
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -20,6 +20,7 @@ import when from 'when';
|
|||
import yaml from 'js-yaml';
|
||||
|
||||
import { handleErrors } from './ErrorActions';
|
||||
import history from '../utils/history';
|
||||
import MistralApiService from '../services/MistralApiService';
|
||||
import NotificationActions from '../actions/NotificationActions';
|
||||
import PlansConstants from '../constants/PlansConstants';
|
||||
|
@ -29,6 +30,7 @@ import SwiftApiService from '../services/SwiftApiService';
|
|||
import MistralConstants from '../constants/MistralConstants';
|
||||
import { PLAN_ENVIRONMENT } from '../constants/PlansConstants';
|
||||
import { getServiceUrl } from '../selectors/auth';
|
||||
import { startWorkflow } from './WorkflowActions';
|
||||
|
||||
const messages = defineMessages({
|
||||
planCreatedNotificationTitle: {
|
||||
|
@ -177,16 +179,21 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
updatePlan(planName, planFiles, history) {
|
||||
updatePlan(planName, planFiles) {
|
||||
return (dispatch, getState, { getIntl }) => {
|
||||
const { formatMessage } = getIntl(getState());
|
||||
dispatch(this.updatePlanPending(planName));
|
||||
return dispatch(uploadFilesToContainer(planName, planFiles))
|
||||
.then(response =>
|
||||
dispatch(
|
||||
MistralApiService.runWorkflow(MistralConstants.PLAN_UPDATE, {
|
||||
container: planName
|
||||
})
|
||||
startWorkflow(
|
||||
MistralConstants.PLAN_UPDATE,
|
||||
{
|
||||
container: planName
|
||||
},
|
||||
execution => dispatch(this.updatePlanFinished(execution)),
|
||||
2 * 60 * 1000
|
||||
)
|
||||
)
|
||||
)
|
||||
.catch(error => {
|
||||
|
@ -203,16 +210,21 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
updatePlanFromTarball(planName, file, history) {
|
||||
updatePlanFromTarball(planName, file) {
|
||||
return (dispatch, getState, { getIntl }) => {
|
||||
const { formatMessage } = getIntl(getState());
|
||||
dispatch(this.updatePlanPending(planName));
|
||||
return dispatch(SwiftApiService.uploadTarball(planName, file))
|
||||
.then(response => {
|
||||
dispatch(
|
||||
MistralApiService.runWorkflow(MistralConstants.PLAN_UPDATE, {
|
||||
container: planName
|
||||
})
|
||||
startWorkflow(
|
||||
MistralConstants.PLAN_UPDATE,
|
||||
{
|
||||
container: planName
|
||||
},
|
||||
execution => dispatch(this.updatePlanFinished(execution)),
|
||||
2 * 60 * 1000
|
||||
)
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
|
@ -229,17 +241,21 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
updatePlanFinished(payload, history) {
|
||||
updatePlanFinished(execution) {
|
||||
return (dispatch, getState, { getIntl }) => {
|
||||
const { formatMessage } = getIntl(getState());
|
||||
const planName = payload.execution.input.container;
|
||||
if (payload.status === 'SUCCESS') {
|
||||
const {
|
||||
input: { container: planName },
|
||||
output: { message },
|
||||
state
|
||||
} = execution;
|
||||
if (state === 'SUCCESS') {
|
||||
dispatch(this.updatePlanSuccess(planName));
|
||||
dispatch(
|
||||
NotificationActions.notify({
|
||||
title: formatMessage(messages.planUpdatedNotificationTitle),
|
||||
message: formatMessage(messages.planUpdatedNotificationMessage, {
|
||||
planName: planName
|
||||
planName
|
||||
}),
|
||||
type: 'success'
|
||||
})
|
||||
|
@ -249,10 +265,7 @@ export default {
|
|||
} else {
|
||||
dispatch(
|
||||
this.updatePlanFailed(planName, [
|
||||
{
|
||||
title: formatMessage(messages.planUpdateFailed),
|
||||
message: payload.message
|
||||
}
|
||||
{ title: formatMessage(messages.planUpdateFailed), message }
|
||||
])
|
||||
);
|
||||
}
|
||||
|
@ -295,9 +308,14 @@ export default {
|
|||
.then(response => dispatch(uploadFilesToContainer(planName, planFiles)))
|
||||
.then(response =>
|
||||
dispatch(
|
||||
MistralApiService.runWorkflow(MistralConstants.PLAN_CREATE, {
|
||||
container: planName
|
||||
})
|
||||
startWorkflow(
|
||||
MistralConstants.PLAN_CREATE,
|
||||
{
|
||||
container: planName
|
||||
},
|
||||
execution => dispatch(this.createPlanFinished(execution)),
|
||||
2 * 60 * 1000
|
||||
)
|
||||
)
|
||||
)
|
||||
.catch(error => {
|
||||
|
@ -324,9 +342,14 @@ export default {
|
|||
)
|
||||
.then(response =>
|
||||
dispatch(
|
||||
MistralApiService.runWorkflow(MistralConstants.PLAN_CREATE, {
|
||||
container: planName
|
||||
})
|
||||
startWorkflow(
|
||||
MistralConstants.PLAN_CREATE,
|
||||
{
|
||||
container: planName
|
||||
},
|
||||
execution => dispatch(this.createPlanFinished(execution)),
|
||||
2 * 60 * 1000
|
||||
)
|
||||
)
|
||||
)
|
||||
.catch(error => {
|
||||
|
@ -340,18 +363,22 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
createPlanFinished(payload, history) {
|
||||
createPlanFinished(execution) {
|
||||
return (dispatch, getState, { getIntl }) => {
|
||||
const { formatMessage } = getIntl(getState());
|
||||
if (payload.status === 'SUCCESS') {
|
||||
const planName = payload.execution.input.container;
|
||||
const {
|
||||
input: { container: planName },
|
||||
output: { message },
|
||||
state
|
||||
} = execution;
|
||||
if (state === 'SUCCESS') {
|
||||
dispatch(this.createPlanSuccess());
|
||||
dispatch(
|
||||
NotificationActions.notify({
|
||||
type: 'success',
|
||||
title: formatMessage(messages.planCreatedNotificationTitle),
|
||||
message: formatMessage(messages.planCreatedNotificationMessage, {
|
||||
planName: planName
|
||||
planName
|
||||
})
|
||||
})
|
||||
);
|
||||
|
@ -359,9 +386,7 @@ export default {
|
|||
history.push('/plans/manage');
|
||||
} else {
|
||||
dispatch(
|
||||
this.createPlanFailed([
|
||||
{ title: 'Plan creation failed', message: payload.message }
|
||||
])
|
||||
this.createPlanFailed([{ title: 'Plan creation failed', message }])
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -444,10 +469,14 @@ export default {
|
|||
return dispatch => {
|
||||
dispatch(this.deployPlanPending(planName));
|
||||
dispatch(
|
||||
MistralApiService.runWorkflow(MistralConstants.DEPLOYMENT_DEPLOY_PLAN, {
|
||||
container: planName,
|
||||
timeout: 240
|
||||
})
|
||||
startWorkflow(
|
||||
MistralConstants.DEPLOYMENT_DEPLOY_PLAN,
|
||||
{
|
||||
container: planName,
|
||||
timeout: 240
|
||||
},
|
||||
execution => dispatch(this.deployPlanFinished(execution))
|
||||
)
|
||||
)
|
||||
.then(response => {
|
||||
dispatch(StackActions.fetchStacks());
|
||||
|
@ -461,20 +490,24 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
deployPlanFinished(payload) {
|
||||
deployPlanFinished(execution) {
|
||||
return (dispatch, getState, { getIntl }) => {
|
||||
const { formatMessage } = getIntl(getState());
|
||||
if (payload.status === 'FAILED') {
|
||||
dispatch(this.deployPlanFailed(payload.execution.input.container));
|
||||
const {
|
||||
input: { container: planName },
|
||||
output: { message },
|
||||
state
|
||||
} = execution;
|
||||
if (state === 'ERROR') {
|
||||
dispatch(this.deployPlanFailed(planName));
|
||||
dispatch(
|
||||
NotificationActions.notify({
|
||||
title: formatMessage(messages.deploymentFailedNotificationTitle),
|
||||
message: payload.message,
|
||||
type: 'error'
|
||||
message
|
||||
})
|
||||
);
|
||||
} else {
|
||||
dispatch(this.deployPlanSuccess(payload.execution.input.container));
|
||||
dispatch(this.deployPlanSuccess(planName));
|
||||
dispatch(StackActions.fetchStacks());
|
||||
}
|
||||
};
|
||||
|
@ -505,9 +538,13 @@ export default {
|
|||
return dispatch => {
|
||||
dispatch(this.exportPlanPending(planName));
|
||||
dispatch(
|
||||
MistralApiService.runWorkflow(MistralConstants.PLAN_EXPORT, {
|
||||
plan: planName
|
||||
})
|
||||
startWorkflow(
|
||||
MistralConstants.PLAN_EXPORT,
|
||||
{
|
||||
plan: planName
|
||||
},
|
||||
execution => dispatch(this.exportPlanFinished(execution))
|
||||
)
|
||||
).catch(error => {
|
||||
dispatch(handleErrors(error, `Plan ${planName} could not be exported`));
|
||||
dispatch(this.exportPlanFailed(planName));
|
||||
|
@ -515,27 +552,29 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
exportPlanFinished(payload) {
|
||||
exportPlanFinished(execution) {
|
||||
return (dispatch, getState, { getIntl }) => {
|
||||
const { formatMessage } = getIntl(getState());
|
||||
if (payload.status === 'FAILED' || !payload.tempurl) {
|
||||
dispatch(this.exportPlanFailed(payload.execution.input.plan));
|
||||
const {
|
||||
input: { plan },
|
||||
output: { message, tempurl },
|
||||
state
|
||||
} = execution;
|
||||
if (state === 'ERROR' || !tempurl) {
|
||||
dispatch(this.exportPlanFailed(plan));
|
||||
dispatch(
|
||||
NotificationActions.notify({
|
||||
title: formatMessage(messages.exportFailedNotificationTitle),
|
||||
message: payload.message,
|
||||
type: 'error'
|
||||
message
|
||||
})
|
||||
);
|
||||
} else {
|
||||
let urlParser = document.createElement('a');
|
||||
urlParser.href = payload.tempurl;
|
||||
urlParser.href = tempurl;
|
||||
let url = urlParser.hostname;
|
||||
urlParser.href = getServiceUrl(getState(), 'swift');
|
||||
let swiftUrl = urlParser.hostname;
|
||||
dispatch(
|
||||
this.exportPlanSuccess(payload.tempurl.replace(url, swiftUrl))
|
||||
);
|
||||
dispatch(this.exportPlanSuccess(tempurl.replace(url, swiftUrl)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -98,22 +98,38 @@ export default {
|
|||
}
|
||||
|
||||
case MistralConstants.PLAN_CREATE: {
|
||||
dispatch(PlansActions.createPlanFinished(payload, history));
|
||||
dispatch(
|
||||
handleWorkflowMessage(payload.execution.id, execution =>
|
||||
dispatch(PlansActions.createPlanFinished(execution))
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case MistralConstants.PLAN_UPDATE: {
|
||||
dispatch(PlansActions.updatePlanFinished(payload, history));
|
||||
dispatch(
|
||||
handleWorkflowMessage(payload.execution.id, execution =>
|
||||
dispatch(PlansActions.updatePlanFinished(execution))
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case MistralConstants.DEPLOYMENT_DEPLOY_PLAN: {
|
||||
dispatch(PlansActions.deployPlanFinished(payload));
|
||||
dispatch(
|
||||
handleWorkflowMessage(payload.execution.id, execution =>
|
||||
dispatch(PlansActions.deployPlanFinished(execution))
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case MistralConstants.PLAN_EXPORT: {
|
||||
dispatch(PlansActions.exportPlanFinished(payload));
|
||||
dispatch(
|
||||
handleWorkflowMessage(payload.execution.id, execution =>
|
||||
dispatch(PlansActions.exportPlanFinished(execution))
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -159,7 +159,6 @@ class EditPlan extends React.Component {
|
|||
|
||||
EditPlan.propTypes = {
|
||||
fetchPlan: PropTypes.func,
|
||||
history: PropTypes.object,
|
||||
intl: PropTypes.object,
|
||||
isTransitioningPlan: PropTypes.bool,
|
||||
match: PropTypes.object,
|
||||
|
@ -184,12 +183,10 @@ function mapDispatchToProps(dispatch, ownProps) {
|
|||
dispatch(PlansActions.fetchPlan(planName));
|
||||
},
|
||||
updatePlan: (planName, files) => {
|
||||
dispatch(PlansActions.updatePlan(planName, files, ownProps.history));
|
||||
dispatch(PlansActions.updatePlan(planName, files));
|
||||
},
|
||||
updatePlanFromTarball: (planName, files) => {
|
||||
dispatch(
|
||||
PlansActions.updatePlanFromTarball(planName, files, ownProps.history)
|
||||
);
|
||||
dispatch(PlansActions.updatePlanFromTarball(planName, files));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue