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
Deploy, rollback, retry and cancel deployments from the web console #4177
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) { | ||
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 :- 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 — 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 — 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. 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 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.", | ||
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? 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:
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:
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= — 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) { | ||
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 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 :) 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
DataService
yet, but this one might be a good conversation starter.DataService
provides 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.