-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Deploy, rollback, retry and cancel deployments from the web console #4177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,7 +30,7 @@ angular.module('openshiftConsole') | |
| }); | ||
| } | ||
|
|
||
| watches.push(DataService.watch("replicationcontrollers", $scope, function(deployments) { | ||
| watches.push(DataService.watch("replicationcontrollers", $scope, function(deployments, action, deployment) { | ||
| $scope.unfilteredDeployments = deployments.by("metadata.name"); | ||
| LabelFilter.addLabelSuggestionsFromResources($scope.unfilteredDeployments, $scope.labelSuggestions); | ||
| LabelFilter.setLabelSuggestions($scope.labelSuggestions); | ||
|
|
@@ -41,6 +41,27 @@ angular.module('openshiftConsole') | |
| associateDeploymentsToDeploymentConfig(); | ||
| updateFilterWarning(); | ||
|
|
||
| var deploymentConfigName; | ||
| var deploymentName; | ||
| if (deployment) { | ||
| deploymentConfigName = $filter('annotation')(deployment, 'deploymentConfig'); | ||
| deploymentName = deployment.metadata.name; | ||
| } | ||
| if (!action) { | ||
| // Loading of the page that will create deploymentConfigDeploymentsInProgress structure, which will associate running deployment to his deploymentConfig. | ||
| $scope.deploymentConfigDeploymentsInProgress = associateRunningDeploymentToDeploymentConfig($scope.deploymentsByDeploymentConfig); | ||
| } else if (action === 'ADDED' || (action === 'MODIFIED' && ['New', 'Pending', 'Running'].indexOf($scope.deploymentStatus(deployment)) > -1)) { | ||
| // When new deployment id instantiated/cloned, or in case of a retry, associate him to his deploymentConfig and add him into deploymentConfigDeploymentsInProgress structure. | ||
| $scope.deploymentConfigDeploymentsInProgress[deploymentConfigName] = $scope.deploymentConfigDeploymentsInProgress[deploymentConfigName] || {}; | ||
| $scope.deploymentConfigDeploymentsInProgress[deploymentConfigName][deploymentName] = deployment; | ||
| } else if (action === 'MODIFIED') { | ||
| // After the deployment ends remove him from the deploymentConfigDeploymentsInProgress structure. | ||
| var deploymentStatus = $scope.deploymentStatus(deployment); | ||
| if (deploymentStatus === "Complete" || deploymentStatus === "Failed"){ | ||
| delete $scope.deploymentConfigDeploymentsInProgress[deploymentConfigName][deploymentName]; | ||
| } | ||
| } | ||
|
|
||
| Logger.log("deployments (subscribe)", $scope.deployments); | ||
| })); | ||
|
|
||
|
|
@@ -73,6 +94,20 @@ angular.module('openshiftConsole') | |
| }); | ||
| } | ||
|
|
||
| function associateRunningDeploymentToDeploymentConfig(deploymentsByDeploymentConfig) { | ||
| var deploymentConfigDeploymentsInProgress = {}; | ||
| angular.forEach(deploymentsByDeploymentConfig, function(deploymentConfigDeployments, deploymentConfigName) { | ||
| deploymentConfigDeploymentsInProgress[deploymentConfigName] = {}; | ||
| angular.forEach(deploymentConfigDeployments, function(deployment, deploymentName) { | ||
| var deploymentStatus = $scope.deploymentStatus(deployment); | ||
| if (deploymentStatus === "New" || deploymentStatus === "Pending" || deploymentStatus === "Running") { | ||
| deploymentConfigDeploymentsInProgress[deploymentConfigName][deploymentName] = deployment; | ||
| } | ||
| }); | ||
| }); | ||
| return deploymentConfigDeploymentsInProgress; | ||
| } | ||
|
|
||
| function updateFilterWarning() { | ||
| if (!LabelFilter.getLabelSelector().isEmpty() && $.isEmptyObject($scope.deployments) && !$.isEmptyObject($scope.unfilteredDeployments)) { | ||
| $scope.alerts["deployments"] = { | ||
|
|
@@ -85,6 +120,207 @@ angular.module('openshiftConsole') | |
| } | ||
| } | ||
|
|
||
| $scope.startLatestDeployment = function(deploymentConfigName) { | ||
| var deploymentConfig = $scope.deploymentConfigs[deploymentConfigName]; | ||
|
|
||
| // increase latest version by one so starts new deployment based on latest | ||
| var req = { | ||
| kind: "DeploymentConfig", | ||
| apiVersion: "v1", | ||
| metadata: deploymentConfig.metadata, | ||
| spec: deploymentConfig.spec, | ||
| status: deploymentConfig.status | ||
| }; | ||
| if (!req.status.latestVersion) { | ||
| req.status.latestVersion = 0; | ||
| } | ||
| req.status.latestVersion++; | ||
|
|
||
| // update the deployment config | ||
| DataService.update("deploymentconfigs", deploymentConfigName, req, $scope).then( | ||
| function() { | ||
| $scope.alerts = [ | ||
| { | ||
| type: "success", | ||
| message: "Deployment #" + req.status.latestVersion + " of " + deploymentConfigName + " has started.", | ||
| } | ||
| ]; | ||
| }, | ||
| function(result) { | ||
| $scope.alerts = [ | ||
| { | ||
| type: "error", | ||
| message: "An error occurred while starting the deployment.", | ||
| details: $filter('getErrorDetails')(result) | ||
| } | ||
| ]; | ||
| } | ||
| ); | ||
| }; | ||
|
|
||
| $scope.retryFailedDeployment = function(deploymentConfigName, deploymentName) { | ||
| var deployment = $scope.deploymentsByDeploymentConfig[deploymentConfigName][deploymentName]; | ||
| var req = deployment; | ||
|
|
||
| // TODO: we need a "retry" api endpoint so we don't have to do this manually | ||
|
|
||
| // delete the deployer pod as well as the deployment hooks pods, if any | ||
| DataService.list("pods", $scope, function(list) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i hate that we have to get the whole list of pods and then find the ones that match up :-
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wait... You guys shouldn't be deleting the deployed pod... That's the On Aug 26, 2015, at 10:47 AM, Jessica Forrester notifications@github.com In assets/app/scripts/controllers/deployments.js
i hate that we have to get the whole list of pods and then find the ones —
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deployment is cancelled by setting the cancel annotation on an RC - it's On Aug 26, 2015, at 10:47 AM, Jessica Forrester notifications@github.com In assets/app/scripts/controllers/deployments.js
i hate that we have to get the whole list of pods and then find the ones —
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I definitely agree, but this is exactly what the command line tools are doing - should be fixed on the server and then we remove it from both the cli and web console, but having both doing different things will quickly lead to inconsistencies.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not only that, we can't fix bugs in one place, and users who also want to On Wed, Aug 26, 2015 at 11:55 AM, Fabiano Franz notifications@github.com
Clayton Coleman | Lead Engineer, OpenShift
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, just saw the other comments. |
||
| var pods = list.by("metadata.name"); | ||
| var deleteDeployerPod = function(pod) { | ||
| var deployerPodForAnnotation = $filter('annotationName')('deployerPodFor'); | ||
| if (pod.metadata.labels[deployerPodForAnnotation] === deploymentName) { | ||
| DataService.delete("pods", pod.metadata.name, $scope).then( | ||
| function() { | ||
| Logger.info("Deployer pod " + pod.metadata.name + " deleted"); | ||
| }, | ||
| function(result) { | ||
| $scope.alerts = [ | ||
| { | ||
| type: "error", | ||
| message: "An error occurred while deleting the deployer pod.", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't it possible that someone just went and cleaned up all their old deployer pods. Should we be popping up an error if the delete failed from a 404? It doesn't stop us from retrying the deployment. On a related note, if we fail to delete the deployer pod and it still exists, non 404 error, should we still be attempting to retry the deployment?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately, you do actually need to clean up deployers before resetting states for a retry (duplicating logic currently all bound up in the You can select all deployer pods using a label selector:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the selector would be better at least, then we arent getting ALL the On Wed, Aug 26, 2015 at 10:26 AM, Dan Mace notifications@github.com wrote:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On Aug 26, 2015, at 11:26 AM, Dan Mace notifications@github.com wrote: In assets/app/scripts/controllers/deployments.js
Unfortunately, you do actually need to clean up deployers before resetting I don't want to introduce the UI for this until an API exists. It's too You can select all deployer pods using a label selector: openshift.io/deployer-pod-for.name= —
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fine by me. The card is currently sitting at the bottom of our New list, so there's currently no timeframe for it. |
||
| details: $filter('getErrorDetails')(result) | ||
| } | ||
| ]; | ||
| } | ||
| ); | ||
| } | ||
| }; | ||
| angular.forEach(pods, deleteDeployerPod); | ||
| }); | ||
|
|
||
| // set deployment to "New" and remove statuses so we can retry | ||
| var deploymentStatusAnnotation = $filter('annotationName')('deploymentStatus'); | ||
| var deploymentStatusReasonAnnotation = $filter('annotationName')('deploymentStatusReason'); | ||
| var deploymentCancelledAnnotation = $filter('annotationName')('deploymentCancelled'); | ||
| req.metadata.annotations[deploymentStatusAnnotation] = "New"; | ||
| delete req.metadata.annotations[deploymentStatusReasonAnnotation]; | ||
| delete req.metadata.annotations[deploymentCancelledAnnotation]; | ||
|
|
||
| // update the deployment | ||
| DataService.update("replicationcontrollers", deploymentName, req, $scope).then( | ||
| function() { | ||
| $scope.alerts = [ | ||
| { | ||
| type: "success", | ||
| message: "Retrying deployment " + deploymentName + " of " + deploymentConfigName + ".", | ||
| } | ||
| ]; | ||
| }, | ||
| function(result) { | ||
| $scope.alerts = [ | ||
| { | ||
| type: "error", | ||
| message: "An error occurred while retrying the deployment.", | ||
| details: $filter('getErrorDetails')(result) | ||
| } | ||
| ]; | ||
| } | ||
| ); | ||
| }; | ||
|
|
||
| $scope.rollbackToDeployment = function(deploymentConfigName, deploymentName, changeScaleSettings, changeStrategy, changeTriggers) { | ||
| // put together a new rollback request | ||
| var req = { | ||
| kind: "DeploymentConfigRollback", | ||
| apiVersion: "v1", | ||
| spec: { | ||
| from: { | ||
| name: deploymentName | ||
| }, | ||
| includeTemplate: true, | ||
| includeReplicationMeta: changeScaleSettings, | ||
| includeStrategy: changeStrategy, | ||
| includeTriggers: changeTriggers | ||
| } | ||
| }; | ||
|
|
||
| // TODO: we need a "rollback" api endpoint so we don't have to do this manually | ||
|
|
||
| // create the deployment config rollback | ||
| DataService.create("deploymentconfigrollbacks", null, req, $scope).then( | ||
| function(newDeploymentConfig) { | ||
| // update the deployment config based on the one returned by the rollback | ||
| DataService.update("deploymentconfigs", deploymentConfigName, newDeploymentConfig, $scope).then( | ||
| function(rolledBackDeploymentConfig) { | ||
| $scope.alerts = [ | ||
| { | ||
| type: "success", | ||
| message: "Deployment #" + rolledBackDeploymentConfig.status.latestVersion + " is rolling back " + deploymentConfigName + " to " + deploymentName + ".", | ||
| } | ||
| ]; | ||
| }, | ||
| function(result) { | ||
| $scope.alerts = [ | ||
| { | ||
| type: "error", | ||
| message: "An error occurred while rolling back the deployment.", | ||
| details: $filter('getErrorDetails')(result) | ||
| } | ||
| ]; | ||
| } | ||
| ); | ||
| }, | ||
| function(result) { | ||
| $scope.alerts = [ | ||
| { | ||
| type: "error", | ||
| message: "An error occurred while rolling back the deployment.", | ||
| details: $filter('getErrorDetails')(result) | ||
| } | ||
| ]; | ||
| } | ||
| ); | ||
| }; | ||
|
|
||
| $scope.cancelRunningDeployment = function(deploymentConfigName, deploymentName) { | ||
| var deployment = $scope.deploymentsByDeploymentConfig[deploymentConfigName][deploymentName]; | ||
| var req = deployment; | ||
|
|
||
| // TODO: we need a "cancel" api endpoint so we don't have to do this manually | ||
|
|
||
| // set the cancellation annotations | ||
| var deploymentCancelledAnnotation = $filter('annotationName')('deploymentCancelled'); | ||
| var deploymentStatusReasonAnnotation = $filter('annotationName')('deploymentStatusReason'); | ||
| req.metadata.annotations[deploymentCancelledAnnotation] = "true"; | ||
| req.metadata.annotations[deploymentStatusReasonAnnotation] = "The deployment was cancelled by the user"; | ||
|
|
||
| // update the deployment with cancellation annotations | ||
| DataService.update("replicationcontrollers", deploymentName, req, $scope).then( | ||
| function() { | ||
| $scope.alerts = [ | ||
| { | ||
| type: "success", | ||
| message: "Cancelling deployment " + deploymentName + " of " + deploymentConfigName + ".", | ||
| } | ||
| ]; | ||
| }, | ||
| function(result) { | ||
| $scope.alerts = [ | ||
| { | ||
| type: "error", | ||
| message: "An error occurred while cancelling the deployment.", | ||
| details: $filter('getErrorDetails')(result) | ||
| } | ||
| ]; | ||
| } | ||
| ); | ||
| }; | ||
|
|
||
| $scope.deploymentIsLatest = function(deploymentConfig, deployment) { | ||
| var deploymentVersion = parseInt($filter('annotation')(deployment, 'deploymentVersion')); | ||
| var deploymentConfigVersion = deploymentConfig.status.latestVersion; | ||
| return deploymentVersion === deploymentConfigVersion; | ||
| }; | ||
|
|
||
| $scope.deploymentStatus = function(deployment) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dont add annotation filter functions to the controllers, either add this to filters/resources.js if you think its common enough, or just use
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually i will make an exception in this case given how much you are having to use it :)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| return $filter('annotation')(deployment, 'deploymentStatus'); | ||
| }; | ||
|
|
||
| $scope.deploymentIsInProgress = function(deployment) { | ||
| return ['New', 'Pending', 'Running'].indexOf($scope.deploymentStatus(deployment)) > -1; | ||
| }; | ||
|
|
||
| LabelFilter.onActiveFiltersChanged(function(labelSelector) { | ||
| // trigger a digest loop | ||
| $scope.$apply(function() { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if we may want functions like this moved up into a service. It may be handy to have available in other contexts & keep the controllers light-weight. We don't have many services in front of
DataServiceyet, but this one might be a good conversation starter.DataServiceprovides methods likedeployments.by("metadata.name"), but prob shouldn't provide methods specific to a particular object. If we wrapped it we could then also have methods likedeployments.associateRunningDeploymentToDeploymentConfig()on the service. We might be able to shorten names then, perhaps something likedeployments.byConfig(config).Conceptually I'm thinking something like this:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it makes sense to me. I would handle it separately (not as part of this PR) but definitely makes sense.