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
pull/2193/head
Chaim Lev-Ari 2018-08-18 09:27:24 +01:00 committed by Anthony Lapenna
parent 7e08227ddb
commit 102e63e1e5
2 changed files with 170 additions and 90 deletions

View File

@ -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();

View File

@ -130,6 +130,7 @@
<span ng-hide="state.actionInProgress">Deploy the container</span>
<span ng-show="state.actionInProgress">Deployment in progress...</span>
</button>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
</div>
</div>
<!-- !actions -->