From 102e63e1e5741cb7a651f0e92147ea48c0811865 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 18 Aug 2018 09:27:24 +0100 Subject: [PATCH] refactor(container-creation): change order of container re-creation/duplication steps * refactor(container-creation): change order of container creation steps * refactor(container-creation): remove nested methods * fix(container-creation): skip actions if old container missing * fix(container-creation): reject if user is not authorized * fix(container-creation): remove rejection on invalid form * refactor(container-creation): start container after duplicate * fix(container-creation): add form validation error message * fix(container-creation): pass correct id to create resource control * fix(container-creation): set action in progress after confirmation --- .../create/createContainerController.js | 259 ++++++++++++------ .../containers/create/createcontainer.html | 1 + 2 files changed, 170 insertions(+), 90 deletions(-) diff --git a/app/docker/views/containers/create/createContainerController.js b/app/docker/views/containers/create/createContainerController.js index 0e017c6eb..6580485f9 100644 --- a/app/docker/views/containers/create/createContainerController.js +++ b/app/docker/views/containers/create/createContainerController.js @@ -2,6 +2,8 @@ angular.module('portainer.docker') .controller('CreateContainerController', ['$q', '$scope', '$state', '$timeout', '$transition$', '$filter', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'NetworkService', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'FormValidator', 'ModalService', 'RegistryService', 'SystemService', 'SettingsService', 'HttpRequestHelper', function ($q, $scope, $state, $timeout, $transition$, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, NetworkService, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator, ModalService, RegistryService, SystemService, SettingsService, HttpRequestHelper) { + $scope.create = create; + $scope.formValues = { alwaysPull: true, Console: 'none', @@ -280,47 +282,7 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai return config; } - function confirmCreateContainer() { - var deferred = $q.defer(); - Container.query({ all: 1, filters: {name: ['^/' + $scope.config.name + '$'] }}).$promise - .then(function success(data) { - var existingContainer = data[0]; - if (existingContainer) { - ModalService.confirm({ - title: 'Are you sure ?', - message: 'A container with the same name already exists. Portainer can automatically remove it and re-create one. Do you want to replace it?', - buttons: { - confirm: { - label: 'Replace', - className: 'btn-danger' - } - }, - callback: function onConfirm(confirmed) { - if(!confirmed) { deferred.resolve(false); } - else { - // Remove old container - ContainerService.remove(existingContainer, true) - .then(function success(data) { - Notifications.success('Container Removed', existingContainer.Id); - deferred.resolve(true); - }) - .catch(function error(err) { - deferred.reject({ msg: 'Unable to remove container', err: err }); - }); - } - } - }); - } else { - deferred.resolve(true); - } - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to retrieve containers'); - return undefined; - }); - return deferred.promise; - } - + function loadFromContainerCmd(d) { if ($scope.config.Cmd) { $scope.config.Cmd = ContainerHelper.commandArrayToString($scope.config.Cmd); @@ -628,62 +590,179 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai return true; } - $scope.create = function () { - confirmCreateContainer() - .then(function success(confirm) { - if (!confirm) { - return false; + + function create() { + var oldContainer = null; + + + HttpRequestHelper.setPortainerAgentTargetHeader($scope.formValues.NodeName); + return findCurrentContainer() + .then(confirmCreateContainer) + .then(startCreationProcess) + .catch(notifyOnError) + .finally(final); + + function final() { + $scope.state.actionInProgress = false; + } + + function findCurrentContainer() { + return Container.query({ all: 1, filters: { name: ['^/' + $scope.config.name + '$'] } }) + .$promise + .then(function onQuerySuccess(containers) { + if (!containers.length) { + return; + } + oldContainer = containers[0]; + return oldContainer; + }) + .catch(notifyOnError); + + function notifyOnError(err) { + Notifications.error('Failure', err, 'Unable to retrieve containers'); + } + } + + function startCreationProcess(confirmed) { + if (!confirmed) { + return $q.when(); + } + if (!validateAccessControl()) { + return $q.when(); + } + $scope.state.actionInProgress = true; + return stopAndRenameContainer(oldContainer) + .then(pullImageIfNeeded) + .then(createNewContainer) + .then(applyResourceControl) + .then(connectToExtraNetworks) + .then(removeOldContainer) + .then(onSuccess); + } + + function confirmCreateContainer(container) { + if (!container) { + return $q.when(true); } + return showConfirmationModal(); + + function showConfirmationModal() { + var deferred = $q.defer(); + + ModalService.confirm({ + title: 'Are you sure ?', + message: 'A container with the same name already exists. Portainer can automatically remove it and re-create one. Do you want to replace it?', + buttons: { + confirm: { + label: 'Replace', + className: 'btn-danger' + } + }, + callback: function onConfirm(confirmed) { + deferred.resolve(confirmed); + } + }); + + return deferred.promise; + } + } + + function stopAndRenameContainer(oldContainer) { + if (!oldContainer) { + return $q.when(); + } + return stopContainerIfNeeded(oldContainer) + .then(renameContainer); + } + + function stopContainerIfNeeded(oldContainer) { + if (oldContainer.State !== 'running') { + return $q.when(); + } + return ContainerService.stopContainer(oldContainer.Id); + } + + function renameContainer() { + return ContainerService.renameContainer(oldContainer.Id, oldContainer.Names[0].substring(1) + '-old'); + } + + function pullImageIfNeeded() { + return $q.when($scope.formValues.alwaysPull && + ImageService.pullImage($scope.config.Image, $scope.formValues.Registry, true)); + } + + function createNewContainer() { + var config = prepareConfiguration(); + return ContainerService.createAndStartContainer(config); + } + + function applyResourceControl(newContainer) { + var containerIdentifier = newContainer.Id; + var userId = Authentication.getUserDetails().ID; + + return $q.when(ResourceControlService.applyResourceControl( + 'container', + containerIdentifier, + userId, + $scope.formValues.AccessControlData, [] + )).then(function onApplyResourceControlSuccess() { + return containerIdentifier; + }); + } + + function connectToExtraNetworks(newContainerId) { + if (!$scope.extraNetworks) { + return $q.when(); + } + + var connectionPromises = Object.keys($scope.extraNetworks).map(function (networkName) { + return NetworkService.connectContainer(networkName, newContainerId); + }); + + return $q.all(connectionPromises); + } + + function removeOldContainer() { + var deferred = $q.defer(); + + if (!oldContainer) { + deferred.resolve(); + return; + } + + ContainerService.remove(oldContainer, true) + .then(notifyOnRemoval) + .catch(notifyOnRemoveError); + + return deferred.promise; + + function notifyOnRemoval() { + Notifications.success('Container Removed', oldContainer.Id); + deferred.resolve(); + } + + function notifyOnRemoveError(err) { + deferred.reject({ msg: 'Unable to remove container', err: err }); + } + } + + function notifyOnError(err) { + Notifications.error('Failure', err, 'Unable to create container'); + } + + function validateAccessControl() { var accessControlData = $scope.formValues.AccessControlData; var userDetails = Authentication.getUserDetails(); var isAdmin = userDetails.role === 1; - if (!validateForm(accessControlData, isAdmin)) { - return; - } + return validateForm(accessControlData, isAdmin); + } - $scope.state.actionInProgress = true; - var config = prepareConfiguration(); - var nodeName = $scope.formValues.NodeName; - HttpRequestHelper.setPortainerAgentTargetHeader(nodeName); - createContainer(config, accessControlData); - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to create container'); - }); - }; - - function createContainer(config, accessControlData) { - var containerIdentifier; - $q.when(!$scope.formValues.alwaysPull || ImageService.pullImage($scope.config.Image, $scope.formValues.Registry, true)) - .finally(function final() { - ContainerService.createAndStartContainer(config) - .then(function success(data) { - containerIdentifier = data.Id; - var userId = Authentication.getUserDetails().ID; - return ResourceControlService.applyResourceControl('container', containerIdentifier, userId, accessControlData, []); - }) - .then(function success() { - if($scope.extraNetworks) { - return $q.all( - Object.keys($scope.extraNetworks).map(function(networkName) { - return NetworkService.connectContainer(networkName, containerIdentifier); - }) - ); - } - }) - .then(function success() { - Notifications.success('Container successfully created'); - $state.go('docker.containers', {}, {reload: true}); - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to create container'); - }) - .finally(function final() { - $scope.state.actionInProgress = false; - }); - }); + function onSuccess() { + Notifications.success('Container successfully created'); + $state.go('docker.containers', {}, { reload: true }); + } } initView(); diff --git a/app/docker/views/containers/create/createcontainer.html b/app/docker/views/containers/create/createcontainer.html index 4ed3f6e4d..e946afa17 100644 --- a/app/docker/views/containers/create/createcontainer.html +++ b/app/docker/views/containers/create/createcontainer.html @@ -130,6 +130,7 @@ Deploy the container Deployment in progress... + {{ state.formValidationError }}