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',
|
.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) {
|
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 = {
|
$scope.formValues = {
|
||||||
alwaysPull: true,
|
alwaysPull: true,
|
||||||
Console: 'none',
|
Console: 'none',
|
||||||
|
@ -280,47 +282,7 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
|
||||||
return config;
|
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) {
|
function loadFromContainerCmd(d) {
|
||||||
if ($scope.config.Cmd) {
|
if ($scope.config.Cmd) {
|
||||||
$scope.config.Cmd = ContainerHelper.commandArrayToString($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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.create = function () {
|
|
||||||
confirmCreateContainer()
|
function create() {
|
||||||
.then(function success(confirm) {
|
var oldContainer = null;
|
||||||
if (!confirm) {
|
|
||||||
return false;
|
|
||||||
|
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 accessControlData = $scope.formValues.AccessControlData;
|
||||||
var userDetails = Authentication.getUserDetails();
|
var userDetails = Authentication.getUserDetails();
|
||||||
var isAdmin = userDetails.role === 1;
|
var isAdmin = userDetails.role === 1;
|
||||||
|
|
||||||
if (!validateForm(accessControlData, isAdmin)) {
|
return validateForm(accessControlData, isAdmin);
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$scope.state.actionInProgress = true;
|
function onSuccess() {
|
||||||
var config = prepareConfiguration();
|
Notifications.success('Container successfully created');
|
||||||
var nodeName = $scope.formValues.NodeName;
|
$state.go('docker.containers', {}, { reload: true });
|
||||||
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;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initView();
|
initView();
|
||||||
|
|
|
@ -130,6 +130,7 @@
|
||||||
<span ng-hide="state.actionInProgress">Deploy the container</span>
|
<span ng-hide="state.actionInProgress">Deploy the container</span>
|
||||||
<span ng-show="state.actionInProgress">Deployment in progress...</span>
|
<span ng-show="state.actionInProgress">Deployment in progress...</span>
|
||||||
</button>
|
</button>
|
||||||
|
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !actions -->
|
<!-- !actions -->
|
||||||
|
|
Loading…
Reference in New Issue