Run undeploy_plan workflow to delete deployment
* create separate UndeployProgress components to track deletion of deployment * unify deploy/undeploy transition when issuing the workflow using UI state rather than custom UI deployment state (STARTING_DEPLOYMENT) Depends-On: Ibd1da1aee8415712b9a418c6738f6ea9a2828a55 Implements: blueprint config-download-ui Change-Id: Idec2534a583f6bea7c74bbdb8a08b9a81b7aa3f8
This commit is contained in:
parent
3691bab67d
commit
e81f96469b
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Added integration of config-download deployment as a default deployment way
|
||||
using TripleO-UI. Config Download feature splits deployment into 2 parts, at
|
||||
first, Heat creates all the deployment data necessary via SoftwareDeployment
|
||||
resources to perform the Overcloud installation and configuration. In second
|
||||
part, using downloaded data from Heat, Ansible playbooks and tasks are
|
||||
generated and are then used by the Undercloud to complete the configuration
|
||||
of the Overcloud.
|
|
@ -25,7 +25,12 @@ import {
|
|||
START_DEPLOYMENT_PENDING,
|
||||
START_DEPLOYMENT_SUCCESS,
|
||||
DEPLOYMENT_FAILED,
|
||||
DEPLOYMENT_SUCCESS
|
||||
DEPLOYMENT_SUCCESS,
|
||||
START_UNDEPLOY_FAILED,
|
||||
START_UNDEPLOY_PENDING,
|
||||
START_UNDEPLOY_SUCCESS,
|
||||
UNDEPLOY_FAILED,
|
||||
UNDEPLOY_SUCCESS
|
||||
} from '../constants/DeploymentConstants';
|
||||
import { handleErrors } from './ErrorActions';
|
||||
import MistralConstants from '../constants/MistralConstants';
|
||||
|
@ -91,9 +96,9 @@ export const startDeploymentSuccess = planName => ({
|
|||
payload: planName
|
||||
});
|
||||
|
||||
export const startDeploymentFailed = (planName, message) => ({
|
||||
export const startDeploymentFailed = planName => ({
|
||||
type: START_DEPLOYMENT_FAILED,
|
||||
payload: { planName, message }
|
||||
payload: planName
|
||||
});
|
||||
|
||||
export const startDeployment = planName => dispatch => {
|
||||
|
@ -143,3 +148,67 @@ export const deploymentFinished = execution => (
|
|||
dispatch(deploymentSuccess(planName, message));
|
||||
}
|
||||
};
|
||||
|
||||
export const startUndeployPending = planName => ({
|
||||
type: START_UNDEPLOY_PENDING,
|
||||
payload: planName
|
||||
});
|
||||
|
||||
export const startUndeploySuccess = planName => ({
|
||||
type: START_UNDEPLOY_SUCCESS,
|
||||
payload: planName
|
||||
});
|
||||
|
||||
export const startUndeployFailed = planName => ({
|
||||
type: START_UNDEPLOY_FAILED,
|
||||
payload: planName
|
||||
});
|
||||
|
||||
export const startUndeploy = planName => dispatch => {
|
||||
dispatch(startUndeployPending(planName));
|
||||
dispatch(
|
||||
startWorkflow(
|
||||
MistralConstants.UNDEPLOY_PLAN,
|
||||
{
|
||||
container: planName,
|
||||
timeout: 240
|
||||
},
|
||||
execution => dispatch(undeployFinished(execution)),
|
||||
10 * 60 * 1000
|
||||
)
|
||||
)
|
||||
.then(execution => dispatch(startUndeploySuccess(planName)))
|
||||
.catch(error => {
|
||||
dispatch(
|
||||
handleErrors(error, `Plan ${planName} deployment could not be deleted`)
|
||||
);
|
||||
dispatch(startUndeployFailed(planName));
|
||||
});
|
||||
};
|
||||
|
||||
export const undeploySuccess = (planName, message) => ({
|
||||
type: UNDEPLOY_SUCCESS,
|
||||
payload: { planName, message }
|
||||
});
|
||||
|
||||
export const undeployFailed = (planName, message) => ({
|
||||
type: UNDEPLOY_FAILED,
|
||||
payload: { planName, message }
|
||||
});
|
||||
|
||||
export const undeployFinished = execution => (
|
||||
dispatch,
|
||||
getState,
|
||||
{ getIntl }
|
||||
) => {
|
||||
const {
|
||||
input: { container: planName },
|
||||
output: { message },
|
||||
state
|
||||
} = execution;
|
||||
if (state === 'ERROR') {
|
||||
dispatch(undeployFailed(planName, message));
|
||||
} else {
|
||||
dispatch(undeploySuccess(planName, message));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -174,41 +174,5 @@ export default {
|
|||
dispatch(this.fetchEnvironmentFailed(stack));
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
deleteStackSuccess(stackName) {
|
||||
return {
|
||||
type: StacksConstants.DELETE_STACK_SUCCESS,
|
||||
payload: stackName
|
||||
};
|
||||
},
|
||||
|
||||
deleteStackFailed() {
|
||||
return {
|
||||
type: StacksConstants.DELETE_STACK_FAILED
|
||||
};
|
||||
},
|
||||
|
||||
deleteStackPending() {
|
||||
return {
|
||||
type: StacksConstants.DELETE_STACK_PENDING
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts a delete request for a stack.
|
||||
*/
|
||||
deleteStack(stack) {
|
||||
return dispatch => {
|
||||
dispatch(this.deleteStackPending());
|
||||
dispatch(HeatApiService.deleteStack(stack.stack_name, stack.id))
|
||||
.then(response => {
|
||||
dispatch(this.deleteStackSuccess(stack.stack_name));
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch(handleErrors(error, 'Stack could not be deleted'));
|
||||
dispatch(this.deleteStackFailed());
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,20 +14,25 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { deploymentStates } from '../constants/DeploymentConstants';
|
||||
import { get } from 'lodash';
|
||||
import { normalize } from 'normalizr';
|
||||
|
||||
import { deploymentStates } from '../constants/DeploymentConstants';
|
||||
import { getCurrentStack } from '../selectors/stacks';
|
||||
import LoggerActions from './LoggerActions';
|
||||
import NodesActions from './NodesActions';
|
||||
import PlansActions from './PlansActions';
|
||||
import RegisterNodesActions from './RegisterNodesActions';
|
||||
import RolesActions from './RolesActions';
|
||||
import StacksActions from './StacksActions';
|
||||
import { stackSchema } from '../normalizrSchemas/stacks';
|
||||
import MistralConstants from '../constants/MistralConstants';
|
||||
import ZaqarWebSocketService from '../services/ZaqarWebSocketService';
|
||||
import { handleWorkflowMessage } from './WorkflowActions';
|
||||
import {
|
||||
getDeploymentStatusSuccess,
|
||||
deploymentFinished,
|
||||
undeployFinished,
|
||||
configDownloadMessage
|
||||
} from './DeploymentActions';
|
||||
import NetworksActions from './NetworksActions';
|
||||
|
@ -142,12 +147,19 @@ export default {
|
|||
break;
|
||||
}
|
||||
|
||||
case MistralConstants.HEAT_STACKS_GET: {
|
||||
const { stack, stack: { stack_name, id } } = payload;
|
||||
dispatch(StacksActions.fetchStackSuccess(stack));
|
||||
!getState().stacks.isFetchingResources &&
|
||||
case MistralConstants.HEAT_STACKS_LIST: {
|
||||
const stacks =
|
||||
normalize(payload.stacks, [stackSchema]).entities.stacks || {};
|
||||
dispatch(StacksActions.fetchStacksSuccess(stacks));
|
||||
|
||||
// TODO(jtomasek): It would be nicer if we could identify that
|
||||
// stack has changed in the component and fetch resources there
|
||||
const { isFetchingResources } = getState().stacks;
|
||||
const currentStack = getCurrentStack(getState());
|
||||
if (!isFetchingResources && currentStack) {
|
||||
const { stack_name, id } = currentStack;
|
||||
dispatch(StacksActions.fetchResources(stack_name, id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case MistralConstants.CONFIG_DOWNLOAD_DEPLOY: {
|
||||
|
@ -170,6 +182,25 @@ export default {
|
|||
break;
|
||||
}
|
||||
|
||||
case MistralConstants.UNDEPLOY_PLAN: {
|
||||
if (payload.deployment_status === deploymentStates.UNDEPLOYING) {
|
||||
const { message, plan_name, deployment_status } = payload;
|
||||
dispatch(
|
||||
getDeploymentStatusSuccess(plan_name, {
|
||||
status: deployment_status,
|
||||
message
|
||||
})
|
||||
);
|
||||
} else {
|
||||
dispatch(
|
||||
handleWorkflowMessage(payload.execution_id, execution =>
|
||||
dispatch(undeployFinished(execution))
|
||||
)
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MistralConstants.PLAN_EXPORT: {
|
||||
dispatch(
|
||||
handleWorkflowMessage(payload.execution.id, execution =>
|
||||
|
|
|
@ -27,7 +27,6 @@ import {
|
|||
CloseModalXButton,
|
||||
RoutedModalPanel
|
||||
} from '../ui/Modals';
|
||||
import { deploymentStates } from '../../constants/DeploymentConstants';
|
||||
import { getCurrentPlanName } from '../../selectors/plans';
|
||||
import {
|
||||
getCurrentPlanDeploymentStatus,
|
||||
|
@ -91,14 +90,11 @@ class DeploymentConfirmation extends React.Component {
|
|||
const {
|
||||
allValidationsSuccessful,
|
||||
currentPlanName,
|
||||
deploymentStatus,
|
||||
startDeployment,
|
||||
environmentSummary
|
||||
environmentSummary,
|
||||
isPendingDeploymentRequest
|
||||
} = this.props;
|
||||
|
||||
const buttonDisabled =
|
||||
deploymentStatus.status === deploymentStates.STARTING_DEPLOYMENT;
|
||||
|
||||
return (
|
||||
<RoutedModalPanel redirectPath={`/plans/${currentPlanName}`}>
|
||||
<ModalHeader>
|
||||
|
@ -132,7 +128,7 @@ class DeploymentConfirmation extends React.Component {
|
|||
<FormattedMessage {...messages.deploymentConfirmation} />
|
||||
</p>
|
||||
<DeployButton
|
||||
disabled={buttonDisabled}
|
||||
disabled={isPendingDeploymentRequest}
|
||||
deploy={startDeployment.bind(this, currentPlanName)}
|
||||
/>
|
||||
</BlankSlate>
|
||||
|
@ -153,6 +149,7 @@ DeploymentConfirmation.propTypes = {
|
|||
deploymentStatus: PropTypes.object.isRequired,
|
||||
environmentSummary: PropTypes.string.isRequired,
|
||||
intl: PropTypes.object,
|
||||
isPendingDeploymentRequest: PropTypes.bool.isRequired,
|
||||
runPreDeploymentValidations: PropTypes.func.isRequired,
|
||||
startDeployment: PropTypes.func.isRequired
|
||||
};
|
||||
|
@ -163,7 +160,9 @@ const mapStateToProps = (state, props) => ({
|
|||
deploymentStatusLoaded: getCurrentPlanDeploymentStatusUI(state).isLoaded,
|
||||
deploymentStatus: getCurrentPlanDeploymentStatus(state),
|
||||
deploymentStatusUIError: getCurrentPlanDeploymentStatusUI(state).error,
|
||||
environmentSummary: getEnvironmentConfigurationSummary(state)
|
||||
environmentSummary: getEnvironmentConfigurationSummary(state),
|
||||
isPendingDeploymentRequest: getCurrentPlanDeploymentStatusUI(state)
|
||||
.isPendingRequest
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
|
|
@ -21,6 +21,7 @@ import PropTypes from 'prop-types';
|
|||
import React from 'react';
|
||||
|
||||
import DeploymentProgress from './DeploymentProgress';
|
||||
import UndeployProgress from './UndeployProgress';
|
||||
import DeploymentFailure from './DeploymentFailure';
|
||||
import { deploymentStates } from '../../constants/DeploymentConstants';
|
||||
import { getCurrentPlanName } from '../../selectors/plans';
|
||||
|
@ -56,21 +57,12 @@ class DeploymentDetail extends React.Component {
|
|||
|
||||
switch (deploymentStatus.status) {
|
||||
case deploymentStates.DEPLOYING:
|
||||
case deploymentStates.UNDEPLOYING:
|
||||
return <DeploymentProgress planName={currentPlanName} />;
|
||||
case deploymentStates.DEPLOY_SUCCESS:
|
||||
return (
|
||||
<div>
|
||||
{deploymentStatus.status}
|
||||
{deploymentStatus.message}
|
||||
</div>
|
||||
);
|
||||
case deploymentStates.UNDEPLOYING:
|
||||
return <UndeployProgress planName={currentPlanName} />;
|
||||
case deploymentStates.UNDEPLOY_FAILED:
|
||||
case deploymentStates.DEPLOY_FAILED:
|
||||
return <DeploymentFailure planName={currentPlanName} />;
|
||||
case deploymentStates.UNDEPLOY_FAILED:
|
||||
// TODO(jtomasek): handle undeploy failure
|
||||
return 'undeploy failed';
|
||||
case deploymentStates.UNKNOWN:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -24,10 +24,14 @@ import React from 'react';
|
|||
import DeleteStackButton from '../deployment_plan/DeleteStackButton';
|
||||
import { deploymentStatusMessages } from '../../constants/DeploymentConstants';
|
||||
import { getCurrentStack } from '../../selectors/stacks';
|
||||
import { getCurrentPlanDeploymentStatus } from '../../selectors/deployment';
|
||||
import {
|
||||
getCurrentPlanDeploymentStatus,
|
||||
getCurrentPlanDeploymentStatusUI
|
||||
} from '../../selectors/deployment';
|
||||
import InlineNotification from '../ui/InlineNotification';
|
||||
import { sanitizeMessage } from '../../utils';
|
||||
import StacksActions from '../../actions/StacksActions';
|
||||
import { startUndeploy } from '../../actions/DeploymentActions';
|
||||
|
||||
class DeploymentFailure extends React.Component {
|
||||
componentDidMount() {
|
||||
|
@ -38,9 +42,9 @@ class DeploymentFailure extends React.Component {
|
|||
render() {
|
||||
const {
|
||||
deploymentStatus: { status, message },
|
||||
deleteStack,
|
||||
undeployPlan,
|
||||
intl: { formatMessage },
|
||||
isRequestingStackDelete,
|
||||
isPendingRequest,
|
||||
planName,
|
||||
stack
|
||||
} = this.props;
|
||||
|
@ -53,36 +57,37 @@ class DeploymentFailure extends React.Component {
|
|||
>
|
||||
<p>{sanitizeMessage(message)}</p>
|
||||
</InlineNotification>
|
||||
<DeleteStackButton
|
||||
deleteStack={deleteStack.bind(this, stack)}
|
||||
disabled={isRequestingStackDelete || !stack}
|
||||
loaded={!isRequestingStackDelete}
|
||||
/>
|
||||
{stack && (
|
||||
<DeleteStackButton
|
||||
deleteStack={undeployPlan.bind(this, planName)}
|
||||
disabled={isPendingRequest}
|
||||
/>
|
||||
)}
|
||||
</ModalBody>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DeploymentFailure.propTypes = {
|
||||
deleteStack: PropTypes.func.isRequired,
|
||||
deploymentStatus: PropTypes.object.isRequired,
|
||||
fetchStacks: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object,
|
||||
isFetchingStacks: PropTypes.bool.isRequired,
|
||||
isRequestingStackDelete: PropTypes.bool.isRequired,
|
||||
isPendingRequest: PropTypes.bool.isRequired,
|
||||
planName: PropTypes.string.isRequired,
|
||||
stack: ImmutablePropTypes.record
|
||||
stack: ImmutablePropTypes.record,
|
||||
undeployPlan: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
deploymentStatus: getCurrentPlanDeploymentStatus(state),
|
||||
isFetchingStacks: state.stacks.isFetching,
|
||||
isRequestingStackDelete: state.stacks.isRequestingStackDelete,
|
||||
isPendingRequest: getCurrentPlanDeploymentStatusUI(state).isPendingRequest,
|
||||
stack: getCurrentStack(state)
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
deleteStack: stack => dispatch(StacksActions.deleteStack(stack)),
|
||||
undeployPlan: planName => dispatch(startUndeploy(planName)),
|
||||
fetchStacks: () => dispatch(StacksActions.fetchStacks())
|
||||
});
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
getCreateCompleteResources
|
||||
} from '../../selectors/stacks';
|
||||
import { getCurrentPlanDeploymentStatus } from '../../selectors/deployment';
|
||||
import { InlineLoader } from '../ui/Loader';
|
||||
import { InlineLoader, Loader } from '../ui/Loader';
|
||||
import StackResourcesTable from './StackResourcesTable';
|
||||
import { stackStates } from '../../constants/StacksConstants';
|
||||
|
||||
|
@ -37,14 +37,6 @@ const messages = defineMessages({
|
|||
id: 'DeploymentSuccess.resources',
|
||||
defaultMessage: 'Resources'
|
||||
},
|
||||
cancelDeployment: {
|
||||
id: 'DeploymentProgress.cancelDeployment',
|
||||
defaultMessage: 'Cancel Deployment'
|
||||
},
|
||||
requestingDeletion: {
|
||||
id: 'DeploymentProgress.requestingDeletion',
|
||||
defaultMessage: 'Requesting Deletion of Deployment'
|
||||
},
|
||||
initializingDeployment: {
|
||||
id: 'DeploymentProgress.initializingDeployment',
|
||||
defaultMessage: 'Initializing {planName} plan deployment'
|
||||
|
@ -70,77 +62,83 @@ class DeploymentProgress extends React.Component {
|
|||
resources,
|
||||
resourcesLoaded,
|
||||
stackDeploymentProgress,
|
||||
stack
|
||||
stack,
|
||||
stacksLoaded
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<ModalBody className="flex-container">
|
||||
{!stack && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.initializingDeployment}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
now={0}
|
||||
label={<span>0%</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{stack &&
|
||||
stack.stack_status !== stackStates.CREATE_COMPLETE && (
|
||||
<Loader
|
||||
loaded={stacksLoaded}
|
||||
componentProps={{ className: 'flex-container' }}
|
||||
>
|
||||
{!stack && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.deployingPlan}
|
||||
values={{
|
||||
planName: <strong>{planName}</strong>,
|
||||
resourcesCount,
|
||||
completeResourcesCount
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
now={stackDeploymentProgress}
|
||||
label={<span>{stackDeploymentProgress + '%'}</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
{message && <pre>{message}</pre>}
|
||||
<h2>
|
||||
<FormattedMessage {...messages.resources} />
|
||||
</h2>
|
||||
<div className="flex-container">
|
||||
<div className="flex-column">
|
||||
<StackResourcesTable
|
||||
isFetchingResources={!resourcesLoaded}
|
||||
resources={resources.reverse()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
{stack &&
|
||||
stack.stack_status === stackStates.CREATE_COMPLETE && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.configuringPlan}
|
||||
{...messages.initializingDeployment}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar active striped now={100} />
|
||||
{message && <pre>{message}</pre>}
|
||||
<ConfigDownloadMessagesList
|
||||
messages={configDownloadMessages.toJS()}
|
||||
<ProgressBar
|
||||
now={0}
|
||||
label={<span>0%</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{stack &&
|
||||
stack.stack_status !== stackStates.CREATE_COMPLETE && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.deployingPlan}
|
||||
values={{
|
||||
planName: <strong>{planName}</strong>,
|
||||
resourcesCount,
|
||||
completeResourcesCount
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
now={stackDeploymentProgress}
|
||||
label={<span>{stackDeploymentProgress + '%'}</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
{message && <pre>{message}</pre>}
|
||||
<h2>
|
||||
<FormattedMessage {...messages.resources} />
|
||||
</h2>
|
||||
<div className="flex-container">
|
||||
<div className="flex-column">
|
||||
<StackResourcesTable
|
||||
isFetchingResources={!resourcesLoaded}
|
||||
resources={resources.reverse()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
{stack &&
|
||||
stack.stack_status === stackStates.CREATE_COMPLETE && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.configuringPlan}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar active striped now={100} />
|
||||
{message && <pre>{message}</pre>}
|
||||
<ConfigDownloadMessagesList
|
||||
messages={configDownloadMessages.toJS()}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
</Loader>
|
||||
</ModalBody>
|
||||
);
|
||||
}
|
||||
|
@ -158,22 +156,23 @@ DeploymentProgress.propTypes = {
|
|||
resourcesCount: PropTypes.number,
|
||||
resourcesLoaded: PropTypes.bool.isRequired,
|
||||
stack: ImmutablePropTypes.record,
|
||||
stackDeploymentProgress: PropTypes.number.isRequired
|
||||
stackDeploymentProgress: PropTypes.number.isRequired,
|
||||
stacksLoaded: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
completeResourcesCount: getCreateCompleteResources(state).size,
|
||||
stack: getCurrentStack(state),
|
||||
deploymentStatus: getCurrentPlanDeploymentStatus(state),
|
||||
isFetchingStacks: state.stacks.isFetching,
|
||||
stackDeploymentProgress: getCurrentStackDeploymentProgress(state),
|
||||
resourcesCount: state.stacks.resources.size,
|
||||
resources: state.stacks.resources,
|
||||
resourcesLoaded: state.stacks.resourcesLoaded
|
||||
resourcesLoaded: state.stacks.resourcesLoaded,
|
||||
stack: getCurrentStack(state),
|
||||
stackDeploymentProgress: getCurrentStackDeploymentProgress(state),
|
||||
stacksLoaded: state.stacks.isLoaded
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { planName }) => ({
|
||||
deleteStack: () => dispatch(StacksActions.deleteStack(planName, '')),
|
||||
fetchStacks: () => dispatch(StacksActions.fetchStacks()),
|
||||
fetchResources: (stackName, stackId) =>
|
||||
dispatch(StacksActions.fetchResources(stackName, stackId))
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
/**
|
||||
* Copyright 2018 Red Hat Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { ModalBody, ProgressBar } from 'react-bootstrap';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Fragment } from 'react';
|
||||
|
||||
import {
|
||||
getCurrentStack,
|
||||
getCurrentStackDeletionProgress
|
||||
} from '../../selectors/stacks';
|
||||
import { getCurrentPlanDeploymentStatus } from '../../selectors/deployment';
|
||||
import { InlineLoader, Loader } from '../ui/Loader';
|
||||
import StackResourcesTable from './StackResourcesTable';
|
||||
import { stackStates } from '../../constants/StacksConstants';
|
||||
|
||||
const messages = defineMessages({
|
||||
resources: {
|
||||
id: 'UndeployProgress.resources',
|
||||
defaultMessage: 'Resources'
|
||||
},
|
||||
initializingUndeploy: {
|
||||
id: 'UndeployProgress.initializingDeployment',
|
||||
defaultMessage: 'Initializing {planName} plan deployment deletion'
|
||||
},
|
||||
undeployingPlan: {
|
||||
id: 'UndeployProgress.undeployingPlan',
|
||||
defaultMessage: 'Deleting {planName} plan deployment'
|
||||
}
|
||||
});
|
||||
|
||||
class UndeployProgress extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
deploymentStatus: { message },
|
||||
planName,
|
||||
resources,
|
||||
resourcesLoaded,
|
||||
stackDeletionProgress,
|
||||
stack,
|
||||
stacksLoaded
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<ModalBody className="flex-container">
|
||||
<Loader
|
||||
loaded={stacksLoaded}
|
||||
componentProps={{ className: 'flex-container' }}
|
||||
>
|
||||
{stack &&
|
||||
stack.stack_status !== stackStates.DELETE_IN_PROGRESS && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.initializingUndeploy}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
now={0}
|
||||
label={<span>0%</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{stack &&
|
||||
stack.stack_status === stackStates.DELETE_IN_PROGRESS && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.undeployingPlan}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
bsStyle="danger"
|
||||
now={stackDeletionProgress}
|
||||
label={<span>{stackDeletionProgress + '%'}</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
{message && <pre>{message}</pre>}
|
||||
<h2>
|
||||
<FormattedMessage {...messages.resources} />
|
||||
</h2>
|
||||
<div className="flex-container">
|
||||
<div className="flex-column">
|
||||
<StackResourcesTable
|
||||
isFetchingResources={!resourcesLoaded}
|
||||
resources={resources.reverse()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
{!stack && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.undeployingPlan}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
bsStyle="danger"
|
||||
now={100}
|
||||
label={<span>{100 + '%'}</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
{message && <pre>{message}</pre>}
|
||||
</Fragment>
|
||||
)}
|
||||
</Loader>
|
||||
</ModalBody>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
UndeployProgress.propTypes = {
|
||||
deploymentStatus: PropTypes.object.isRequired,
|
||||
fetchResources: PropTypes.func.isRequired,
|
||||
fetchStacks: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object,
|
||||
isFetchingStacks: PropTypes.bool.isRequired,
|
||||
planName: PropTypes.string.isRequired,
|
||||
resources: ImmutablePropTypes.list,
|
||||
resourcesLoaded: PropTypes.bool.isRequired,
|
||||
stack: ImmutablePropTypes.record,
|
||||
stackDeletionProgress: PropTypes.number.isRequired,
|
||||
stacksLoaded: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
deploymentStatus: getCurrentPlanDeploymentStatus(state),
|
||||
isFetchingStacks: state.stacks.isFetching,
|
||||
resources: state.stacks.resources,
|
||||
resourcesLoaded: state.stacks.resourcesLoaded,
|
||||
stack: getCurrentStack(state),
|
||||
stackDeletionProgress: getCurrentStackDeletionProgress(state),
|
||||
stacksLoaded: state.stacks.isLoaded
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { planName }) => ({
|
||||
fetchStacks: () => dispatch(StacksActions.fetchStacks()),
|
||||
fetchResources: (stackName, stackId) =>
|
||||
dispatch(StacksActions.fetchResources(stackName, stackId))
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UndeployProgress);
|
|
@ -62,7 +62,7 @@ class DeleteStackButton extends React.Component {
|
|||
className="link btn btn-danger"
|
||||
>
|
||||
<InlineLoader
|
||||
loaded={this.props.loaded}
|
||||
loaded={!this.props.disabled}
|
||||
content={formatMessage(messages.requestingDeletion)}
|
||||
inverse
|
||||
>
|
||||
|
@ -85,8 +85,7 @@ class DeleteStackButton extends React.Component {
|
|||
DeleteStackButton.propTypes = {
|
||||
deleteStack: PropTypes.func.isRequired,
|
||||
disabled: PropTypes.bool.isRequired,
|
||||
intl: PropTypes.object,
|
||||
loaded: PropTypes.bool.isRequired
|
||||
intl: PropTypes.object
|
||||
};
|
||||
|
||||
export default injectIntl(DeleteStackButton);
|
||||
|
|
|
@ -24,6 +24,7 @@ import React from 'react';
|
|||
import DeploymentSuccess from './DeploymentSuccess';
|
||||
import DeploymentFailure from './DeploymentFailure';
|
||||
import DeploymentProgress from './DeploymentProgress';
|
||||
import UndeployProgress from './UndeployProgress';
|
||||
import {
|
||||
deploymentStates,
|
||||
deploymentStatusMessages
|
||||
|
@ -49,20 +50,20 @@ const messages = defineMessages({
|
|||
export const DeployStep = ({
|
||||
currentPlan,
|
||||
deploymentStatus,
|
||||
deploymentStatusUIError,
|
||||
intl: { formatMessage },
|
||||
deploymentStatusUIError
|
||||
isPendingDeploymentRequest
|
||||
}) => {
|
||||
switch (deploymentStatus.status) {
|
||||
case deploymentStates.DEPLOYING:
|
||||
case deploymentStates.UNDEPLOYING:
|
||||
return <DeploymentProgress planName={currentPlan.name} />;
|
||||
case deploymentStates.UNDEPLOYING:
|
||||
return <UndeployProgress planName={currentPlan.name} />;
|
||||
case deploymentStates.DEPLOY_SUCCESS:
|
||||
return <DeploymentSuccess />;
|
||||
case deploymentStates.DEPLOY_FAILED:
|
||||
return <DeploymentFailure planName={currentPlan.name} />;
|
||||
case deploymentStates.UNDEPLOY_FAILED:
|
||||
// TODO(jtomasek): handle undeploy failure
|
||||
return 'undeploy failed';
|
||||
return <DeploymentFailure planName={currentPlan.name} />;
|
||||
case deploymentStates.UNKNOWN:
|
||||
return (
|
||||
<InlineNotification
|
||||
|
@ -74,16 +75,14 @@ export const DeployStep = ({
|
|||
</InlineNotification>
|
||||
);
|
||||
default:
|
||||
const disabled =
|
||||
deploymentStatus.status === deploymentStates.STARTING_DEPLOYMENT;
|
||||
return (
|
||||
<Link
|
||||
to={`/plans/${currentPlan.name}/deployment-confirmation`}
|
||||
className="btn btn-primary btn-lg link"
|
||||
disabled={disabled}
|
||||
disabled={isPendingDeploymentRequest}
|
||||
>
|
||||
<InlineLoader
|
||||
loaded={!disabled}
|
||||
loaded={!isPendingDeploymentRequest}
|
||||
content={formatMessage(messages.requestingDeploy)}
|
||||
>
|
||||
<FormattedMessage {...messages.validateAndDeploy} />
|
||||
|
@ -97,12 +96,15 @@ DeployStep.propTypes = {
|
|||
currentPlan: ImmutablePropTypes.record.isRequired,
|
||||
deploymentStatus: PropTypes.object.isRequired,
|
||||
deploymentStatusUIError: PropTypes.string,
|
||||
intl: PropTypes.object
|
||||
intl: PropTypes.object,
|
||||
isPendingDeploymentRequest: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
deploymentStatus: getCurrentPlanDeploymentStatus(state),
|
||||
deploymentStatusUIError: getCurrentPlanDeploymentStatusUI(state).error
|
||||
deploymentStatusUIError: getCurrentPlanDeploymentStatusUI(state).error,
|
||||
isPendingDeploymentRequest: getCurrentPlanDeploymentStatusUI(state)
|
||||
.isPendingRequest
|
||||
});
|
||||
|
||||
export default injectIntl(connect(mapStateToProps)(DeployStep));
|
||||
|
|
|
@ -22,9 +22,7 @@ import DeploymentConfirmation from '../deployment/DeploymentConfirmation';
|
|||
import { deploymentStates as ds } from '../../constants/DeploymentConstants';
|
||||
|
||||
const DeploymentConfirmationRoute = ({ currentPlanName, deploymentStatus }) =>
|
||||
[ds.UNDEPLOYED, ds.UNKNOWN, ds.STARTING_DEPLOYMENT].includes(
|
||||
deploymentStatus
|
||||
) ? (
|
||||
[ds.UNDEPLOYED, ds.UNKNOWN].includes(deploymentStatus) ? (
|
||||
<DeploymentConfirmation />
|
||||
) : (
|
||||
<Redirect to={`/plans/${currentPlanName}`} />
|
||||
|
|
|
@ -22,7 +22,7 @@ import DeploymentDetail from '../deployment/DeploymentDetail';
|
|||
import { deploymentStates as ds } from '../../constants/DeploymentConstants';
|
||||
|
||||
const DeploymentDetailRoute = ({ currentPlanName, deploymentStatus }) =>
|
||||
[ds.DEPLOYING, ds.UNDEPLOYING, ds.DEPLOY_FAILED].includes(
|
||||
[ds.DEPLOYING, ds.UNDEPLOYING, ds.DEPLOY_FAILED, ds.UNDEPLOY_FAILED].includes(
|
||||
deploymentStatus
|
||||
) ? (
|
||||
<DeploymentDetail />
|
||||
|
|
|
@ -30,18 +30,10 @@ import {
|
|||
getCurrentStackDeploymentProgress,
|
||||
getCreateCompleteResources
|
||||
} from '../../selectors/stacks';
|
||||
import { InlineLoader } from '../ui/Loader';
|
||||
import { InlineLoader, Loader } from '../ui/Loader';
|
||||
import StacksActions from '../../actions/StacksActions';
|
||||
|
||||
const messages = defineMessages({
|
||||
cancelDeployment: {
|
||||
id: 'DeploymentProgress.cancelDeployment',
|
||||
defaultMessage: 'Cancel Deployment'
|
||||
},
|
||||
requestingDeletion: {
|
||||
id: 'DeploymentProgress.requestingDeletion',
|
||||
defaultMessage: 'Requesting Deletion of Deployment'
|
||||
},
|
||||
initializingDeployment: {
|
||||
id: 'DeploymentProgress.initializingDeployment',
|
||||
defaultMessage: 'Initializing {planName} plan deployment'
|
||||
|
@ -66,10 +58,10 @@ class DeploymentProgress extends React.Component {
|
|||
this.fetchStacks();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
if (!this.props.stack && newProps.stack) {
|
||||
const { stack: { stack_name, id } } = newProps;
|
||||
this.props.fetchResources(stack_name, id);
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!prevProps.stack && this.props.stack) {
|
||||
const { stack: { stack_name, id }, fetchResources } = this.props;
|
||||
fetchResources(stack_name, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,7 +77,8 @@ class DeploymentProgress extends React.Component {
|
|||
resourcesCount,
|
||||
completeResourcesCount,
|
||||
stackDeploymentProgress,
|
||||
stack
|
||||
stack,
|
||||
stacksLoaded
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
@ -101,58 +94,60 @@ class DeploymentProgress extends React.Component {
|
|||
<FormattedMessage {...messages.viewInformation} />
|
||||
</Link>
|
||||
</p>
|
||||
{!stack && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.initializingDeployment}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
now={0}
|
||||
label={<span>0%</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{stack &&
|
||||
stack.stack_status !== stackStates.CREATE_COMPLETE && (
|
||||
<Loader loaded={stacksLoaded}>
|
||||
{!stack && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.deployingPlan}
|
||||
values={{
|
||||
planName: <strong>{planName}</strong>,
|
||||
resourcesCount,
|
||||
completeResourcesCount
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
now={stackDeploymentProgress}
|
||||
label={<span>{stackDeploymentProgress + '%'}</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
{message && <pre>{message}</pre>}
|
||||
</Fragment>
|
||||
)}
|
||||
{stack &&
|
||||
stack.stack_status === stackStates.CREATE_COMPLETE && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.configuringPlan}
|
||||
{...messages.initializingDeployment}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar active striped now={100} />
|
||||
{message && <pre>{message}</pre>}
|
||||
<ProgressBar
|
||||
now={0}
|
||||
label={<span>0%</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{stack &&
|
||||
stack.stack_status !== stackStates.CREATE_COMPLETE && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.deployingPlan}
|
||||
values={{
|
||||
planName: <strong>{planName}</strong>,
|
||||
resourcesCount,
|
||||
completeResourcesCount
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
now={stackDeploymentProgress}
|
||||
label={<span>{stackDeploymentProgress + '%'}</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
{message && <pre>{message}</pre>}
|
||||
</Fragment>
|
||||
)}
|
||||
{stack &&
|
||||
stack.stack_status === stackStates.CREATE_COMPLETE && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.configuringPlan}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar active striped now={100} />
|
||||
{message && <pre>{message}</pre>}
|
||||
</Fragment>
|
||||
)}
|
||||
</Loader>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -168,7 +163,8 @@ DeploymentProgress.propTypes = {
|
|||
planName: PropTypes.string.isRequired,
|
||||
resourcesCount: PropTypes.number,
|
||||
stack: ImmutablePropTypes.record,
|
||||
stackDeploymentProgress: PropTypes.number.isRequired
|
||||
stackDeploymentProgress: PropTypes.number.isRequired,
|
||||
stacksLoaded: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
|
@ -176,12 +172,12 @@ const mapStateToProps = (state, props) => ({
|
|||
stack: getCurrentStack(state),
|
||||
deploymentStatus: getCurrentPlanDeploymentStatus(state),
|
||||
isFetchingStacks: state.stacks.isFetching,
|
||||
stacksLoaded: state.stacks.isLoaded,
|
||||
stackDeploymentProgress: getCurrentStackDeploymentProgress(state),
|
||||
resourcesCount: state.stacks.resources.size
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { planName }) => ({
|
||||
deleteStack: () => dispatch(StacksActions.deleteStack(planName, '')),
|
||||
fetchStacks: () => dispatch(StacksActions.fetchStacks()),
|
||||
fetchResources: (stackName, stackId) =>
|
||||
dispatch(StacksActions.fetchResources(stackName, stackId))
|
||||
|
|
|
@ -22,11 +22,16 @@ import React, { Fragment } from 'react';
|
|||
|
||||
import DeleteStackButton from './DeleteStackButton';
|
||||
import { deploymentStatusMessages } from '../../constants/DeploymentConstants';
|
||||
import { getCurrentPlanDeploymentStatus } from '../../selectors/deployment';
|
||||
import {
|
||||
getCurrentPlanDeploymentStatus,
|
||||
getCurrentPlanDeploymentStatusUI
|
||||
} from '../../selectors/deployment';
|
||||
import { getCurrentStack, getOvercloudInfo } from '../../selectors/stacks';
|
||||
import { getCurrentPlanName } from '../../selectors/plans';
|
||||
import InlineNotification from '../ui/InlineNotification';
|
||||
import OvercloudInfo from '../deployment/OvercloudInfo';
|
||||
import { Loader } from '../ui/Loader';
|
||||
import { startUndeploy } from '../../actions/DeploymentActions';
|
||||
import StacksActions from '../../actions/StacksActions';
|
||||
|
||||
class DeploymentSuccess extends React.Component {
|
||||
|
@ -43,12 +48,13 @@ class DeploymentSuccess extends React.Component {
|
|||
render() {
|
||||
const {
|
||||
intl: { formatMessage },
|
||||
isPendingRequest,
|
||||
stack,
|
||||
stacksLoaded,
|
||||
overcloudInfo,
|
||||
deleteStack,
|
||||
deploymentStatus: { status, message },
|
||||
isRequestingStackDelete
|
||||
planName,
|
||||
undeployPlan,
|
||||
deploymentStatus: { status, message }
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
@ -67,9 +73,8 @@ class DeploymentSuccess extends React.Component {
|
|||
fetchOvercloudInfo={this.fetchOvercloudInfo.bind(this)}
|
||||
/>
|
||||
<DeleteStackButton
|
||||
deleteStack={deleteStack.bind(this, stack)}
|
||||
disabled={isRequestingStackDelete}
|
||||
loaded={!isRequestingStackDelete}
|
||||
deleteStack={undeployPlan.bind(this, planName)}
|
||||
disabled={isPendingRequest}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
|
@ -79,28 +84,30 @@ class DeploymentSuccess extends React.Component {
|
|||
}
|
||||
|
||||
DeploymentSuccess.propTypes = {
|
||||
deleteStack: PropTypes.func.isRequired,
|
||||
deploymentStatus: ImmutablePropTypes.record.isRequired,
|
||||
fetchStackEnvironment: PropTypes.func.isRequired,
|
||||
fetchStackResource: PropTypes.func.isRequired,
|
||||
fetchStacks: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object,
|
||||
isRequestingStackDelete: PropTypes.bool,
|
||||
isPendingRequest: PropTypes.bool.isRequired,
|
||||
overcloudInfo: ImmutablePropTypes.map.isRequired,
|
||||
planName: PropTypes.string.isRequired,
|
||||
stack: ImmutablePropTypes.record,
|
||||
stacksLoaded: PropTypes.bool.isRequired
|
||||
stacksLoaded: PropTypes.bool.isRequired,
|
||||
undeployPlan: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
deploymentStatus: getCurrentPlanDeploymentStatus(state),
|
||||
isRequestingStackDelete: state.stacks.isRequestingStackDelete,
|
||||
planName: getCurrentPlanName(state),
|
||||
overcloudInfo: getOvercloudInfo(state),
|
||||
stack: getCurrentStack(state),
|
||||
stacksLoaded: state.stacks.isLoaded
|
||||
stacksLoaded: state.stacks.isLoaded,
|
||||
isPendingRequest: getCurrentPlanDeploymentStatusUI(state).isPendingRequest
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
deleteStack: planName => dispatch(StacksActions.deleteStack(planName, '')),
|
||||
undeployPlan: planName => dispatch(startUndeploy(planName)),
|
||||
fetchStacks: () => dispatch(StacksActions.fetchStacks()),
|
||||
fetchStackEnvironment: stack =>
|
||||
dispatch(StacksActions.fetchEnvironment(stack)),
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
/**
|
||||
* Copyright 2018 Red Hat Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ProgressBar } from 'react-bootstrap';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Fragment } from 'react';
|
||||
|
||||
import { deploymentStatusMessages } from '../../constants/DeploymentConstants';
|
||||
import { stackStates } from '../../constants/StacksConstants';
|
||||
import { getCurrentPlanDeploymentStatus } from '../../selectors/deployment';
|
||||
import {
|
||||
getCurrentStack,
|
||||
getCurrentStackDeletionProgress,
|
||||
getDeleteCompleteResources
|
||||
} from '../../selectors/stacks';
|
||||
import { InlineLoader, Loader } from '../ui/Loader';
|
||||
import StacksActions from '../../actions/StacksActions';
|
||||
|
||||
const messages = defineMessages({
|
||||
initializingUndeploy: {
|
||||
id: 'UndeployProgress.initializingUndeploy',
|
||||
defaultMessage: 'Initializing {planName} plan deployment deletion'
|
||||
},
|
||||
viewInformation: {
|
||||
id: 'UndeployProgress.viewInformation',
|
||||
defaultMessage: 'View detailed information'
|
||||
},
|
||||
undeployingPlan: {
|
||||
id: 'UndeployProgress.undeployingPlan',
|
||||
defaultMessage: 'Deleting {planName} plan deployment'
|
||||
}
|
||||
});
|
||||
|
||||
class UndeployProgress extends React.Component {
|
||||
componentDidMount() {
|
||||
this.fetchStacks();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!prevProps.stack && this.props.stack) {
|
||||
const { stack: { stack_name, id }, fetchResources } = this.props;
|
||||
fetchResources(stack_name, id);
|
||||
}
|
||||
}
|
||||
|
||||
fetchStacks() {
|
||||
const { fetchStacks, isFetchingStacks } = this.props;
|
||||
!isFetchingStacks && fetchStacks();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
deploymentStatus: { status, message },
|
||||
planName,
|
||||
stackDeletionProgress,
|
||||
stack,
|
||||
stacksLoaded
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
{...deploymentStatusMessages[status]}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</span>{' '}
|
||||
<Link to={`/plans/${planName}/deployment-detail`}>
|
||||
<FormattedMessage {...messages.viewInformation} />
|
||||
</Link>
|
||||
</p>
|
||||
<Loader loaded={stacksLoaded}>
|
||||
{stack &&
|
||||
stack.stack_status !== stackStates.DELETE_IN_PROGRESS && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.initializingUndeploy}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
now={0}
|
||||
label={<span>0%</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{stack &&
|
||||
stack.stack_status === stackStates.DELETE_IN_PROGRESS && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.undeployingPlan}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
bsStyle="danger"
|
||||
now={stackDeletionProgress}
|
||||
label={<span>{stackDeletionProgress + '%'}</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
{message && <pre>{message}</pre>}
|
||||
</Fragment>
|
||||
)}
|
||||
{!stack && (
|
||||
<Fragment>
|
||||
<div className="progress-description">
|
||||
<InlineLoader />
|
||||
<FormattedMessage
|
||||
{...messages.undeployingPlan}
|
||||
values={{ planName: <strong>{planName}</strong> }}
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
bsStyle="danger"
|
||||
now={100}
|
||||
label={<span>{100 + '%'}</span>}
|
||||
className="progress-label-top-right"
|
||||
/>
|
||||
{message && <pre>{message}</pre>}
|
||||
</Fragment>
|
||||
)}
|
||||
</Loader>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
UndeployProgress.propTypes = {
|
||||
deletedResourcesCount: PropTypes.number,
|
||||
deploymentStatus: PropTypes.object.isRequired,
|
||||
fetchResources: PropTypes.func.isRequired,
|
||||
fetchStacks: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object,
|
||||
isFetchingStacks: PropTypes.bool.isRequired,
|
||||
planName: PropTypes.string.isRequired,
|
||||
resourcesCount: PropTypes.number,
|
||||
stack: ImmutablePropTypes.record,
|
||||
stackDeletionProgress: PropTypes.number.isRequired,
|
||||
stacksLoaded: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
deletedResourcesCount: getDeleteCompleteResources(state).size,
|
||||
deploymentStatus: getCurrentPlanDeploymentStatus(state),
|
||||
isFetchingStacks: state.stacks.isFetching,
|
||||
stack: getCurrentStack(state),
|
||||
stacksLoaded: state.stacks.isLoaded,
|
||||
stackDeletionProgress: getCurrentStackDeletionProgress(state),
|
||||
resourcesCount: state.stacks.resources.size
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { planName }) => ({
|
||||
fetchStacks: () => dispatch(StacksActions.fetchStacks()),
|
||||
fetchResources: (stackName, stackId) =>
|
||||
dispatch(StacksActions.fetchResources(stackName, stackId))
|
||||
});
|
||||
|
||||
export default injectIntl(
|
||||
connect(mapStateToProps, mapDispatchToProps)(UndeployProgress)
|
||||
);
|
|
@ -26,11 +26,15 @@ export const START_DEPLOYMENT_SUCCESS = 'START_DEPLOYMENT_SUCCESS';
|
|||
export const START_DEPLOYMENT_PENDING = 'START_DEPLOYMENT_PENDING';
|
||||
export const DEPLOYMENT_FAILED = 'DEPLOYMENT_FAILED';
|
||||
export const DEPLOYMENT_SUCCESS = 'DEPLOYMENT_SUCCESS';
|
||||
export const START_UNDEPLOY_FAILED = 'START_UNDEPLOY_FAILED';
|
||||
export const START_UNDEPLOY_SUCCESS = 'START_UNDEPLOY_SUCCESS';
|
||||
export const START_UNDEPLOY_PENDING = 'START_UNDEPLOY_PENDING';
|
||||
export const UNDEPLOY_FAILED = 'UNDEPLOY_FAILED';
|
||||
export const UNDEPLOY_SUCCESS = 'UNDEPLOY_SUCCESS';
|
||||
|
||||
export const deploymentStates = keyMirror({
|
||||
UNDEPLOYED: null,
|
||||
DEPLOY_SUCCESS: null,
|
||||
STARTING_DEPLOYMENT: null,
|
||||
DEPLOYING: null,
|
||||
UNDEPLOYING: null,
|
||||
DEPLOY_FAILED: null,
|
||||
|
@ -53,7 +57,8 @@ export const deploymentStatusMessages = defineMessages({
|
|||
},
|
||||
UNDEPLOYING: {
|
||||
id: 'DeploymentStatus.undeploying',
|
||||
defaultMessage: 'Undeploy in progress'
|
||||
defaultMessage:
|
||||
'Deletion of {planName} plan deployment is currently in progress'
|
||||
},
|
||||
DEPLOY_FAILED: {
|
||||
id: 'DeploymentStatus.deploymentFailed',
|
||||
|
|
|
@ -30,7 +30,7 @@ export default {
|
|||
DEPLOYMENT_DEPLOY_PLAN: 'tripleo.deployment.v1.deploy_plan',
|
||||
UNDEPLOY_PLAN: 'tripleo.deployment.v1.undeploy_plan',
|
||||
DOWNLOAD_LOGS: 'tripleo.plan_management.v1.download_logs',
|
||||
HEAT_STACKS_GET: 'tripleo.stack.v1._heat_stacks_get',
|
||||
HEAT_STACKS_LIST: 'tripleo.stack.v1._heat_stacks_list',
|
||||
NETWORK_LIST: 'tripleo.plan_management.v1.list_networks',
|
||||
PARAMETERS_GET: 'tripleo.parameters.get_flatten',
|
||||
PARAMETERS_UPDATE: 'tripleo.parameters.update',
|
||||
|
|
|
@ -18,9 +18,6 @@ import keyMirror from 'keymirror';
|
|||
import { defineMessages } from 'react-intl';
|
||||
|
||||
export default keyMirror({
|
||||
DELETE_STACK_PENDING: null,
|
||||
DELETE_STACK_FAILED: null,
|
||||
DELETE_STACK_SUCCESS: null,
|
||||
FETCH_STACK_ENVIRONMENT_SUCCESS: null,
|
||||
FETCH_STACK_ENVIRONMENT_PENDING: null,
|
||||
FETCH_STACK_ENVIRONMENT_FAILED: null,
|
||||
|
|
|
@ -28,5 +28,6 @@ export const DeploymentStatus = Record({
|
|||
export const DeploymentStatusUI = Record({
|
||||
error: undefined,
|
||||
isLoaded: false,
|
||||
isFetching: false
|
||||
isFetching: false,
|
||||
isPendingRequest: false
|
||||
});
|
||||
|
|
|
@ -18,7 +18,6 @@ import { List, Map, Record } from 'immutable';
|
|||
|
||||
export const StacksState = Record({
|
||||
currentStackEnvironment: Map(),
|
||||
isRequestingStackDelete: false,
|
||||
isLoaded: false,
|
||||
isFetching: false,
|
||||
isFetchingResources: false,
|
||||
|
|
|
@ -27,6 +27,11 @@ import {
|
|||
START_DEPLOYMENT_FAILED,
|
||||
START_DEPLOYMENT_PENDING,
|
||||
START_DEPLOYMENT_SUCCESS,
|
||||
START_UNDEPLOY_FAILED,
|
||||
START_UNDEPLOY_PENDING,
|
||||
START_UNDEPLOY_SUCCESS,
|
||||
UNDEPLOY_FAILED,
|
||||
UNDEPLOY_SUCCESS,
|
||||
deploymentStates
|
||||
} from '../constants/DeploymentConstants';
|
||||
import {
|
||||
|
@ -51,24 +56,11 @@ export const deploymentStatusByPlan = (state = Map(), { type, payload }) => {
|
|||
messages.push(payload.message)
|
||||
)
|
||||
);
|
||||
case START_DEPLOYMENT_PENDING:
|
||||
return state.set(
|
||||
payload,
|
||||
new DeploymentStatus({ status: deploymentStates.STARTING_DEPLOYMENT })
|
||||
);
|
||||
case START_DEPLOYMENT_SUCCESS:
|
||||
return state.set(
|
||||
payload,
|
||||
new DeploymentStatus({ status: deploymentStates.DEPLOYING })
|
||||
);
|
||||
case START_DEPLOYMENT_FAILED:
|
||||
return state.set(
|
||||
payload.planName,
|
||||
new DeploymentStatus({
|
||||
status: deploymentStates.UNDEPLOYED,
|
||||
message: payload.message
|
||||
})
|
||||
);
|
||||
case DEPLOYMENT_SUCCESS:
|
||||
return state.set(
|
||||
payload.planName,
|
||||
|
@ -85,6 +77,27 @@ export const deploymentStatusByPlan = (state = Map(), { type, payload }) => {
|
|||
message: payload.message
|
||||
})
|
||||
);
|
||||
case START_UNDEPLOY_SUCCESS:
|
||||
return state.set(
|
||||
payload,
|
||||
new DeploymentStatus({ status: deploymentStates.UNDEPLOYING })
|
||||
);
|
||||
case UNDEPLOY_SUCCESS:
|
||||
return state.set(
|
||||
payload.planName,
|
||||
new DeploymentStatus({
|
||||
status: deploymentStates.UNDEPLOYED,
|
||||
message: payload.message
|
||||
})
|
||||
);
|
||||
case UNDEPLOY_FAILED:
|
||||
return state.set(
|
||||
payload.planName,
|
||||
new DeploymentStatus({
|
||||
status: deploymentStates.UNDEPLOY_FAILED,
|
||||
message: payload.message
|
||||
})
|
||||
);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -112,6 +125,14 @@ export const deploymentStatusUI = (state = Map(), { type, payload }) => {
|
|||
error: undefined
|
||||
})
|
||||
);
|
||||
case START_DEPLOYMENT_PENDING:
|
||||
case START_UNDEPLOY_PENDING:
|
||||
return state.setIn([payload, 'isPendingRequest'], true);
|
||||
case START_DEPLOYMENT_SUCCESS:
|
||||
case START_DEPLOYMENT_FAILED:
|
||||
case START_UNDEPLOY_SUCCESS:
|
||||
case START_UNDEPLOY_FAILED:
|
||||
return state.setIn([payload, 'isPendingRequest'], false);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import { fromJS, Map } from 'immutable';
|
||||
|
||||
import { Stack, StackResource, StacksState } from '../immutableRecords/stacks';
|
||||
import StacksConstants, { stackStates } from '../constants/StacksConstants';
|
||||
import StacksConstants from '../constants/StacksConstants';
|
||||
import PlansConstants from '../constants/PlansConstants';
|
||||
|
||||
const initialState = new StacksState();
|
||||
|
@ -90,20 +90,6 @@ export default function stacksReducer(state = initialState, action) {
|
|||
case PlansConstants.PLAN_CHOSEN:
|
||||
return initialState;
|
||||
|
||||
case StacksConstants.DELETE_STACK_SUCCESS:
|
||||
return state
|
||||
.set('isRequestingStackDelete', false)
|
||||
.setIn(
|
||||
['stacks', action.payload, 'stack_status'],
|
||||
stackStates.DELETE_IN_PROGRESS
|
||||
);
|
||||
|
||||
case StacksConstants.DELETE_STACK_FAILED:
|
||||
return state.set('isRequestingStackDelete', false);
|
||||
|
||||
case StacksConstants.DELETE_STACK_PENDING:
|
||||
return state.set('isRequestingStackDelete', true);
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -57,6 +57,11 @@ export const getCreateCompleteResources = createSelector(
|
|||
resources => resources.filter(r => r.resource_status === 'CREATE_COMPLETE')
|
||||
);
|
||||
|
||||
export const getDeleteCompleteResources = createSelector(
|
||||
[stackResourcesSelector],
|
||||
resources => resources.filter(r => r.resource_status === 'DELETE_COMPLETE')
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns calculated percentage of deployment progress
|
||||
*/
|
||||
|
@ -71,6 +76,20 @@ export const getCurrentStackDeploymentProgress = createSelector(
|
|||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns calculated percentage of deletion progress
|
||||
*/
|
||||
export const getCurrentStackDeletionProgress = createSelector(
|
||||
[stackResourcesSelector, getDeleteCompleteResources],
|
||||
(resources, completeResources) => {
|
||||
let allResources = resources.size;
|
||||
if (allResources > 0) {
|
||||
return Math.ceil(completeResources.size / allResources * 100);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns a Map containing the overcloud information.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue