mirror of https://github.com/portainer/portainer
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 confirmationpull/2193/head
parent
7e08227ddb
commit
102e63e1e5
|
@ -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();
|
||||
|
|
|
@ -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 -->
|
||||
|
|
Loading…
Reference in New Issue