mirror of
https://github.com/portainer/portainer.git
synced 2025-07-20 13:59:40 +02:00
feat(container-details): add the ability to re-create, duplicate and edit a container (#855)
This commit is contained in:
parent
d814f3aaa4
commit
c85aa0739d
9 changed files with 386 additions and 20 deletions
|
@ -1,14 +1,13 @@
|
|||
// @@OLD_SERVICE_CONTROLLER: this service should be rewritten to use services.
|
||||
// See app/components/templates/templatesController.js as a reference.
|
||||
angular.module('createContainer', [])
|
||||
.controller('CreateContainerController', ['$q', '$scope', '$state', '$stateParams', '$filter', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'NetworkService', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'FormValidator',
|
||||
function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, NetworkService, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator) {
|
||||
.controller('CreateContainerController', ['$q', '$scope', '$state', '$stateParams', '$filter', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'NetworkService', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'FormValidator', 'ModalService', 'RegistryService',
|
||||
function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, NetworkService, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator, ModalService, RegistryService) {
|
||||
|
||||
$scope.formValues = {
|
||||
alwaysPull: true,
|
||||
Console: 'none',
|
||||
Volumes: [],
|
||||
Registry: '',
|
||||
NetworkContainer: '',
|
||||
Labels: [],
|
||||
ExtraHosts: [],
|
||||
|
@ -92,6 +91,8 @@ function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper,
|
|||
$scope.config.HostConfig.Devices.splice(index, 1);
|
||||
};
|
||||
|
||||
$scope.fromContainerMultipleNetworks = false;
|
||||
|
||||
function prepareImageConfig(config) {
|
||||
var image = config.Image;
|
||||
var registry = $scope.formValues.Registry;
|
||||
|
@ -179,6 +180,7 @@ function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper,
|
|||
var networkMode = mode;
|
||||
if (containerName) {
|
||||
networkMode += ':' + containerName;
|
||||
config.Hostname = '';
|
||||
}
|
||||
config.HostConfig.NetworkMode = networkMode;
|
||||
|
||||
|
@ -233,6 +235,213 @@ function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper,
|
|||
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);
|
||||
} else {
|
||||
$scope.config.Cmd = '';
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromContainerPortBindings(d) {
|
||||
var bindings = [];
|
||||
for (var p in $scope.config.HostConfig.PortBindings) {
|
||||
if ({}.hasOwnProperty.call($scope.config.HostConfig.PortBindings, p)) {
|
||||
var hostPort = '';
|
||||
if ($scope.config.HostConfig.PortBindings[p][0].HostIp) {
|
||||
hostPort = $scope.config.HostConfig.PortBindings[p][0].HostIp + ':';
|
||||
}
|
||||
hostPort += $scope.config.HostConfig.PortBindings[p][0].HostPort;
|
||||
var b = {
|
||||
'hostPort': hostPort,
|
||||
'containerPort': p.split('/')[0],
|
||||
'protocol': p.split('/')[1]
|
||||
};
|
||||
bindings.push(b);
|
||||
}
|
||||
}
|
||||
$scope.config.HostConfig.PortBindings = bindings;
|
||||
}
|
||||
|
||||
function loadFromContainerVolumes(d) {
|
||||
for (var v in d.Mounts) {
|
||||
if ({}.hasOwnProperty.call(d.Mounts, v)) {
|
||||
var mount = d.Mounts[v];
|
||||
var volume = {
|
||||
'type': mount.Type,
|
||||
'name': mount.Name || mount.Source,
|
||||
'containerPath': mount.Destination,
|
||||
'readOnly': mount.RW === false
|
||||
};
|
||||
$scope.formValues.Volumes.push(volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromContainerNetworkConfig(d) {
|
||||
$scope.config.NetworkingConfig = {
|
||||
EndpointsConfig: {}
|
||||
};
|
||||
var networkMode = d.HostConfig.NetworkMode;
|
||||
if (networkMode === 'default') {
|
||||
$scope.config.HostConfig.NetworkMode = 'bridge';
|
||||
if (!_.find($scope.availableNetworks, {'Name': 'bridge'})) {
|
||||
$scope.config.HostConfig.NetworkMode = 'nat';
|
||||
}
|
||||
}
|
||||
if ($scope.config.HostConfig.NetworkMode.indexOf('container:') === 0) {
|
||||
var netContainer = $scope.config.HostConfig.NetworkMode.split(/^container:/)[1];
|
||||
$scope.config.HostConfig.NetworkMode = 'container';
|
||||
for (var c in $scope.runningContainers) {
|
||||
if ($scope.runningContainers[c].Names && $scope.runningContainers[c].Names[0] === '/' + netContainer) {
|
||||
$scope.formValues.NetworkContainer = $scope.runningContainers[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
$scope.fromContainerMultipleNetworks = Object.keys(d.NetworkSettings.Networks).length >= 2;
|
||||
if (d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode]) {
|
||||
if (d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode].IPAMConfig) {
|
||||
if (d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode].IPAMConfig.IPv4Address) {
|
||||
$scope.formValues.IPv4 = d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode].IPAMConfig.IPv4Address;
|
||||
}
|
||||
if (d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode].IPAMConfig.IPv6Address) {
|
||||
$scope.formValues.IPv6 = d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode].IPAMConfig.IPv6Address;
|
||||
}
|
||||
}
|
||||
}
|
||||
$scope.config.NetworkingConfig.EndpointsConfig[$scope.config.HostConfig.NetworkMode] = d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode];
|
||||
// ExtraHosts
|
||||
for (var h in $scope.config.HostConfig.ExtraHosts) {
|
||||
if ({}.hasOwnProperty.call($scope.config.HostConfig.ExtraHosts, h)) {
|
||||
$scope.formValues.ExtraHosts.push({'value': $scope.config.HostConfig.ExtraHosts[h]});
|
||||
$scope.config.HostConfig.ExtraHosts = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromContainerEnvrionmentVariables(d) {
|
||||
var envArr = [];
|
||||
for (var e in $scope.config.Env) {
|
||||
if ({}.hasOwnProperty.call($scope.config.Env, e)) {
|
||||
var arr = $scope.config.Env[e].split(/\=(.+)/);
|
||||
envArr.push({'name': arr[0], 'value': arr[1]});
|
||||
}
|
||||
}
|
||||
$scope.config.Env = envArr;
|
||||
}
|
||||
|
||||
function loadFromContainerLabels(d) {
|
||||
for (var l in $scope.config.Labels) {
|
||||
if ({}.hasOwnProperty.call($scope.config.Labels, l)) {
|
||||
$scope.formValues.Labels.push({ name: l, value: $scope.config.Labels[l]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromContainerConsole(d) {
|
||||
if ($scope.config.OpenStdin && $scope.config.Tty) {
|
||||
$scope.formValues.Console = 'both';
|
||||
} else if (!$scope.config.OpenStdin && $scope.config.Tty) {
|
||||
$scope.formValues.Console = 'tty';
|
||||
} else if ($scope.config.OpenStdin && !$scope.config.Tty) {
|
||||
$scope.formValues.Console = 'interactive';
|
||||
} else if (!$scope.config.OpenStdin && !$scope.config.Tty) {
|
||||
$scope.formValues.Console = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromContainerDevices(d) {
|
||||
var path = [];
|
||||
for (var dev in $scope.config.HostConfig.Devices) {
|
||||
if ({}.hasOwnProperty.call($scope.config.HostConfig.Devices, dev)) {
|
||||
var device = $scope.config.HostConfig.Devices[dev];
|
||||
path.push({'pathOnHost': device.PathOnHost, 'pathInContainer': device.PathInContainer});
|
||||
}
|
||||
}
|
||||
$scope.config.HostConfig.Devices = path;
|
||||
}
|
||||
|
||||
function loadFromContainerImageConfig(d) {
|
||||
// If no registry found, we let default DockerHub and let full image path
|
||||
var imageInfo = ImageHelper.extractImageAndRegistryFromRepository($scope.config.Image);
|
||||
RegistryService.retrieveRegistryFromRepository($scope.config.Image)
|
||||
.then(function success(data) {
|
||||
if (data) {
|
||||
$scope.config.Image = imageInfo.image;
|
||||
$scope.formValues.Registry = data;
|
||||
}
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrive registry');
|
||||
});
|
||||
}
|
||||
|
||||
function loadFromContainerSpec() {
|
||||
// Get container
|
||||
Container.get({ id: $stateParams.from }).$promise
|
||||
.then(function success(d) {
|
||||
var fromContainer = new ContainerDetailsViewModel(d);
|
||||
if (!fromContainer.ResourceControl) {
|
||||
$scope.formValues.AccessControlData.AccessControlEnabled = false;
|
||||
}
|
||||
$scope.fromContainer = fromContainer;
|
||||
$scope.config = ContainerHelper.configFromContainer(fromContainer.Model);
|
||||
loadFromContainerCmd(d);
|
||||
loadFromContainerPortBindings(d);
|
||||
loadFromContainerVolumes(d);
|
||||
loadFromContainerNetworkConfig(d);
|
||||
loadFromContainerEnvrionmentVariables(d);
|
||||
loadFromContainerLabels(d);
|
||||
loadFromContainerConsole(d);
|
||||
loadFromContainerDevices(d);
|
||||
loadFromContainerImageConfig(d);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve container');
|
||||
});
|
||||
}
|
||||
|
||||
function initView() {
|
||||
Volume.query({}, function (d) {
|
||||
$scope.availableVolumes = d.Volumes;
|
||||
|
@ -264,6 +473,12 @@ function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper,
|
|||
Container.query({}, function (d) {
|
||||
var containers = d;
|
||||
$scope.runningContainers = containers;
|
||||
if ($stateParams.from !== '') {
|
||||
loadFromContainerSpec();
|
||||
} else {
|
||||
$scope.fromContainer = {};
|
||||
$scope.formValues.Registry = {};
|
||||
}
|
||||
}, function(e) {
|
||||
Notifications.error('Failure', e, 'Unable to retrieve running containers');
|
||||
});
|
||||
|
@ -283,19 +498,27 @@ function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper,
|
|||
}
|
||||
|
||||
$scope.create = function () {
|
||||
$('#createContainerSpinner').show();
|
||||
confirmCreateContainer()
|
||||
.then(function success(confirm) {
|
||||
if (!confirm) {
|
||||
return false;
|
||||
}
|
||||
$('#createContainerSpinner').show();
|
||||
var accessControlData = $scope.formValues.AccessControlData;
|
||||
var userDetails = Authentication.getUserDetails();
|
||||
var isAdmin = userDetails.role === 1 ? true : false;
|
||||
|
||||
var accessControlData = $scope.formValues.AccessControlData;
|
||||
var userDetails = Authentication.getUserDetails();
|
||||
var isAdmin = userDetails.role === 1 ? true : false;
|
||||
if (!validateForm(accessControlData, isAdmin)) {
|
||||
$('#createContainerSpinner').hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateForm(accessControlData, isAdmin)) {
|
||||
$('#createContainerSpinner').hide();
|
||||
return;
|
||||
}
|
||||
|
||||
var config = prepareConfiguration();
|
||||
createContainer(config, accessControlData);
|
||||
var config = prepareConfiguration();
|
||||
createContainer(config, accessControlData);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to create container');
|
||||
});
|
||||
};
|
||||
|
||||
function createContainer(config, accessControlData) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue