portainer/app/docker/views/containers/create/createContainerController.js

861 lines
27 KiB
JavaScript
Raw Normal View History

refactor(app): introduce webpack and babel (#2407) * feat(agent): add new host page * feat(agent): convert volume-browser to files-datatable * fix(agent): browse folders in file-datatable * feat(engine-details): replace engine view with host view * feat(engine-details): remove old panels * feat(engine-details): add basic engine-details-panel component * feat(engine-details): pass details to the different components * feat(engine-details): replace host-view with host-overview * feat(engine-details): add commaseperated filter * feat(engine-details): add host-view container component * feat(engine-details): add host-details component * feat(engine-details): build host details object * feat(engine-details): format engine version * feat(engine-details): get details for one node * feat(engine-details): pass is-agent from view * feat(engine-details): replace old node view with a new component * feat(engine-details): add swarm-node-details component * feat(engine-details): remove isSwarm binding * feat(engine-details): remove node-details and include in parent * feat(engine-details): add labels-table component * feat(engine-details): add update node service * feat(engine-details): add update label functionality * style(engine-details): remove whitespaces * feat(engine-details): remove old node page * feat(engine-details): pass is agent to host details * feat(host-details): hide missing info * feat(host-details): update node availability * style(host-details): remove obsolete event object * feat(host-details): fix labels not sending * feat(host-details): remove flags for hiding data * feat(host-details): create mock call to server for agent host info * style(host-details): fix spelling mistake in filter's name * feat(host-details): get info from agent * feat(host-details): hide engine labels when empty * feat(node-details): move labels table and save button * feat(host-info): add different urls for refresh * feat(host-details): show disk/devices info for agent * feat(host-view): add loading indicator to devices-panel * feat(host-details): add loading indicator to disks panel * feat(agent): fix browse volume * feat(agent): browse files * feat(agent): enable rename * feat(agent): download file * fix(agent): download file from root * feat(agent): delete file * style(agent): remove whitespaces * fix(agent): fix link on node browser * feat(agent): basic file uploader * feat(agent): add basic file upload * fix(volume-browser): move volume id to query params * feat(node-browser): moved uploader into browser * feat(node-browser): add upload spinner * feat(agent): browse files relative to root * feat(build): add webpack build config * feat(build): add missing imports * feat(webpack): add missing imports * feat(build): enable eslint on build * feat(build): add webpack notifier * feat(build): clean terminal on build * feat(build): import all globals * feat(build): add angular import * feat(build): fix styles * feat(build): load favicons * feat(build): load css before script * feat(webpack): split vendors css and js to a different bundle * feat(webpack): import angular in all files * feat(webpack): remove eslint global config * feat(webpack): add webpack clean dist * feat(webpack): fix styling issues * refactor(webpack): remove empty controllers * refactor(webpack): optimize moment * refactor(webpack): add bundle analyzer * feat(webpack): add babel * refactor(webpack): optimize lodash * refactor(toastr): update toastr * feat(webpack): create basic production and dev config * fix(webpack): fix production config * fix(webpack): fix html templates url * refactor(webpack): remove angular imports * refactor(webpack): remove more angular imports * refactor(webpack): return angular to entry file * style(webpack): remove comments from config * fix(hosts): remove browse button * fix(webpack): import lodash * fix(webpack): import missing htmls * feat(webpack): reduce lodash size * feat(webpack): config grunt to use webpack * feat(webpack): add postcss * chore(codeclimate): use eslint-5 channel * feat(deps): upgrade from lodash to lodash-es * fix(webpack): fix bug with lodash * chore(build): add build client script * fix(webpack): fix missing jsyaml reference * refactor(webpack): seperate builds of img files * chore(build): add a way to check times of webpack build * feat(webpack): add dev server * fix(webpack): fix css output name * chore(webpack): optimize images * chore(webpack): add node env * fix(build): copy templates on release * chore(webpack): set env NODE_ENV * feat(webpack): set NODE_ENV on production builds * fix(extensions): set image path * refactor(css): move vendor css to js import * style(app): remove whitespaces * fix(build-system): allow DevOps pipeline to leverage webpack (#2670) * Update devopsbuild task to use webpack & remove AppVeyor environment var * Added -Force to replace the existing dist folder * Removed Test-Path * dep(build-system): add angularjs-annotate to webpack + fix on imports * Merge branch 'develop' into webpack * refactor(app): webpack aliases for imports + async / await dep + start refactor * style(extensions): use develop version of the view * fix(app): fix several issues introduced by webpack migration * fix(webpack): fix ng-include not loading templates with webpack * Fix Windows CI with Webpack (#2782) * fix(configs): refactor broke configs creation and list views * fix(build-system): update build_binary_devops for Windows
2019-03-21 05:46:49 +00:00
import _ from 'lodash-es';
import { ContainerCapabilities, ContainerCapability } from '../../../models/containerCapabilities';
import { AccessControlFormData } from '../../../../portainer/components/accessControlForm/porAccessControlFormModel';
import { ContainerDetailsViewModel } from '../../../models/container';
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', 'PluginService', 'HttpRequestHelper',
function ($q, $scope, $state, $timeout, $transition$, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, NetworkService, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator, ModalService, RegistryService, SystemService, SettingsService, PluginService, HttpRequestHelper) {
$scope.create = create;
$scope.formValues = {
alwaysPull: true,
Console: 'none',
Volumes: [],
NetworkContainer: '',
Labels: [],
ExtraHosts: [],
MacAddress: '',
IPv4: '',
IPv6: '',
AccessControlData: new AccessControlFormData(),
CpuLimit: 0,
MemoryLimit: 0,
2018-05-06 07:15:57 +00:00
MemoryReservation: 0,
NodeName: null,
capabilities: [],
LogDriverName: '',
LogDriverOpts: []
};
$scope.extraNetworks = {};
$scope.state = {
2017-11-12 19:27:28 +00:00
formValidationError: '',
actionInProgress: false
};
$scope.refreshSlider = function () {
$timeout(function () {
$scope.$broadcast('rzSliderForceRender');
});
};
$scope.config = {
Image: '',
Env: [],
Cmd: '',
MacAddress: '',
ExposedPorts: {},
HostConfig: {
RestartPolicy: {
Name: 'no'
},
PortBindings: [],
PublishAllPorts: false,
Binds: [],
AutoRemove: false,
NetworkMode: 'bridge',
Privileged: false,
Runtime: '',
ExtraHosts: [],
Devices: [],
CapAdd: [],
CapDrop: []
},
NetworkingConfig: {
EndpointsConfig: {}
},
Labels: {}
};
$scope.addVolume = function() {
$scope.formValues.Volumes.push({ name: '', containerPath: '', readOnly: false, type: 'volume' });
};
$scope.removeVolume = function(index) {
$scope.formValues.Volumes.splice(index, 1);
};
$scope.addEnvironmentVariable = function() {
$scope.config.Env.push({ name: '', value: ''});
};
$scope.removeEnvironmentVariable = function(index) {
$scope.config.Env.splice(index, 1);
};
$scope.addPortBinding = function() {
$scope.config.HostConfig.PortBindings.push({ hostPort: '', containerPort: '', protocol: 'tcp' });
};
$scope.removePortBinding = function(index) {
$scope.config.HostConfig.PortBindings.splice(index, 1);
};
$scope.addLabel = function() {
$scope.formValues.Labels.push({ name: '', value: ''});
};
$scope.removeLabel = function(index) {
$scope.formValues.Labels.splice(index, 1);
};
$scope.addExtraHost = function() {
$scope.formValues.ExtraHosts.push({ value: '' });
};
$scope.removeExtraHost = function(index) {
$scope.formValues.ExtraHosts.splice(index, 1);
};
$scope.addDevice = function() {
$scope.config.HostConfig.Devices.push({ pathOnHost: '', pathInContainer: '' });
};
$scope.removeDevice = function(index) {
$scope.config.HostConfig.Devices.splice(index, 1);
};
$scope.addLogDriverOpt = function() {
$scope.formValues.LogDriverOpts.push({ name: '', value: ''});
};
$scope.removeLogDriverOpt = function(index) {
$scope.formValues.LogDriverOpts.splice(index, 1);
};
$scope.fromContainerMultipleNetworks = false;
function prepareImageConfig(config) {
var image = config.Image;
var registry = $scope.formValues.Registry;
var imageConfig = ImageHelper.createImageConfigForContainer(image, registry.URL);
config.Image = imageConfig.fromImage + ':' + imageConfig.tag;
$scope.imageConfig = imageConfig;
}
function preparePortBindings(config) {
var bindings = {};
if (config.ExposedPorts === undefined) {
config.ExposedPorts = {};
}
config.HostConfig.PortBindings.forEach(function (portBinding) {
if (portBinding.containerPort) {
var key = portBinding.containerPort + '/' + portBinding.protocol;
var binding = {};
if (portBinding.hostPort && portBinding.hostPort.indexOf(':') > -1) {
var hostAndPort = portBinding.hostPort.split(':');
binding.HostIp = hostAndPort[0];
binding.HostPort = hostAndPort[1];
} else {
binding.HostPort = portBinding.hostPort;
}
bindings[key] = [binding];
config.ExposedPorts[key] = {};
}
});
config.HostConfig.PortBindings = bindings;
}
function prepareConsole(config) {
var value = $scope.formValues.Console;
var openStdin = true;
var tty = true;
if (value === 'tty') {
openStdin = false;
} else if (value === 'interactive') {
tty = false;
} else if (value === 'none') {
openStdin = false;
tty = false;
}
config.OpenStdin = openStdin;
config.Tty = tty;
}
function prepareEnvironmentVariables(config) {
var env = [];
config.Env.forEach(function (v) {
if (v.name && v.value) {
env.push(v.name + '=' + v.value);
}
});
config.Env = env;
}
function prepareVolumes(config) {
var binds = [];
var volumes = {};
$scope.formValues.Volumes.forEach(function (volume) {
var name = volume.name;
var containerPath = volume.containerPath;
if (name && containerPath) {
var bind = name + ':' + containerPath;
volumes[containerPath] = {};
if (volume.readOnly) {
bind += ':ro';
}
binds.push(bind);
}
});
config.HostConfig.Binds = binds;
config.Volumes = volumes;
}
function prepareNetworkConfig(config) {
var mode = config.HostConfig.NetworkMode;
var container = $scope.formValues.NetworkContainer;
var containerName = container;
if (container && typeof container === 'object') {
containerName = $filter('trimcontainername')(container.Names[0]);
}
var networkMode = mode;
if (containerName) {
networkMode += ':' + containerName;
config.Hostname = '';
}
config.HostConfig.NetworkMode = networkMode;
config.MacAddress = $scope.formValues.MacAddress;
config.NetworkingConfig.EndpointsConfig[networkMode] = {
IPAMConfig: {
IPv4Address: $scope.formValues.IPv4,
IPv6Address: $scope.formValues.IPv6
}
};
$scope.formValues.ExtraHosts.forEach(function (v) {
if (v.value) {
config.HostConfig.ExtraHosts.push(v.value);
}
});
}
function prepareLabels(config) {
var labels = {};
$scope.formValues.Labels.forEach(function (label) {
if (label.name && label.value) {
labels[label.name] = label.value;
}
});
config.Labels = labels;
}
function prepareDevices(config) {
var path = [];
config.HostConfig.Devices.forEach(function (p) {
if (p.pathOnHost) {
if(p.pathInContainer === '') {
p.pathInContainer = p.pathOnHost;
}
path.push({PathOnHost:p.pathOnHost,PathInContainer:p.pathInContainer,CgroupPermissions:'rwm'});
}
});
config.HostConfig.Devices = path;
}
function prepareResources(config) {
// Memory Limit - Round to 0.125
var memoryLimit = (Math.round($scope.formValues.MemoryLimit * 8) / 8).toFixed(3);
memoryLimit *= 1024 * 1024;
if (memoryLimit > 0) {
config.HostConfig.Memory = memoryLimit;
}
// Memory Resevation - Round to 0.125
var memoryReservation = (Math.round($scope.formValues.MemoryReservation * 8) / 8).toFixed(3);
memoryReservation *= 1024 * 1024;
if (memoryReservation > 0) {
config.HostConfig.MemoryReservation = memoryReservation;
}
// CPU Limit
if ($scope.formValues.CpuLimit > 0) {
config.HostConfig.NanoCpus = $scope.formValues.CpuLimit * 1000000000;
}
}
function prepareLogDriver(config) {
var logOpts = {};
if ($scope.formValues.LogDriverName) {
config.HostConfig.LogConfig = { Type: $scope.formValues.LogDriverName };
if ($scope.formValues.LogDriverName !== 'none') {
$scope.formValues.LogDriverOpts.forEach(function (opt) {
if (opt.name) {
logOpts[opt.name] = opt.value;
}
});
if (Object.keys(logOpts).length !== 0 && logOpts.constructor === Object) {
config.HostConfig.LogConfig.Config = logOpts;
}
}
}
}
function prepareCapabilities(config) {
var allowed = $scope.formValues.capabilities.filter(function(item) {return item.allowed === true;});
var notAllowed = $scope.formValues.capabilities.filter(function(item) {return item.allowed === false;});
var getCapName = function(item) {return item.capability;};
config.HostConfig.CapAdd = allowed.map(getCapName);
config.HostConfig.CapDrop = notAllowed.map(getCapName);
}
function prepareConfiguration() {
var config = angular.copy($scope.config);
config.Cmd = ContainerHelper.commandStringToArray(config.Cmd);
prepareNetworkConfig(config);
prepareImageConfig(config);
preparePortBindings(config);
prepareConsole(config);
prepareEnvironmentVariables(config);
prepareVolumes(config);
prepareLabels(config);
prepareDevices(config);
prepareResources(config);
prepareLogDriver(config);
prepareCapabilities(config);
return config;
}
function loadFromContainerCmd() {
if ($scope.config.Cmd) {
$scope.config.Cmd = ContainerHelper.commandArrayToString($scope.config.Cmd);
} else {
$scope.config.Cmd = '';
}
}
function loadFromContainerPortBindings() {
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);
}
}
}
$scope.resetNetworkConfig = function() {
$scope.config.NetworkingConfig = {
EndpointsConfig: {}
};
};
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];
// Mac Address
if(Object.keys(d.NetworkSettings.Networks).length) {
var firstNetwork = d.NetworkSettings.Networks[Object.keys(d.NetworkSettings.Networks)[0]];
$scope.formValues.MacAddress = firstNetwork.MacAddress;
$scope.config.NetworkingConfig.EndpointsConfig[$scope.config.HostConfig.NetworkMode] = firstNetwork;
$scope.extraNetworks = angular.copy(d.NetworkSettings.Networks);
delete $scope.extraNetworks[Object.keys(d.NetworkSettings.Networks)[0]];
} else {
$scope.formValues.MacAddress = '';
}
// ExtraHosts
2018-05-06 07:15:57 +00:00
if ($scope.config.HostConfig.ExtraHosts) {
var extraHosts = $scope.config.HostConfig.ExtraHosts;
for (var i = 0; i < extraHosts.length; i++) {
var host = extraHosts[i];
$scope.formValues.ExtraHosts.push({ 'value': host });
}
$scope.config.HostConfig.ExtraHosts = [];
}
}
function loadFromContainerEnvironmentVariables() {
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() {
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() {
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() {
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() {
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 loadFromContainerResources(d) {
if (d.HostConfig.NanoCpus) {
$scope.formValues.CpuLimit = d.HostConfig.NanoCpus / 1000000000;
}
if (d.HostConfig.Memory) {
$scope.formValues.MemoryLimit = d.HostConfig.Memory / 1024 / 1024;
}
if (d.HostConfig.MemoryReservation) {
$scope.formValues.MemoryReservation = d.HostConfig.MemoryReservation / 1024 / 1024;
}
}
function loadFromContainerCapabilities(d) {
if (d.HostConfig.CapAdd) {
d.HostConfig.CapAdd.forEach(function(cap) {
$scope.formValues.capabilities.push(new ContainerCapability(cap, true));
});
}
if (d.HostConfig.CapDrop) {
d.HostConfig.CapDrop.forEach(function(cap) {
$scope.formValues.capabilities.push(new ContainerCapability(cap, false));
});
}
function hasCapability(item) {
return item.capability === cap.capability;
}
var capabilities = new ContainerCapabilities();
for (var i = 0; i < capabilities.length; i++) {
var cap = capabilities[i];
if (!_.find($scope.formValues.capabilities, hasCapability)) {
$scope.formValues.capabilities.push(cap);
}
}
$scope.formValues.capabilities.sort(function(a, b) {
return a.capability < b.capability ? -1 : 1;
});
}
function loadFromContainerSpec() {
// Get container
Container.get({ id: $transition$.params().from }).$promise
.then(function success(d) {
var fromContainer = new ContainerDetailsViewModel(d);
if (fromContainer.ResourceControl && fromContainer.ResourceControl.Public) {
$scope.formValues.AccessControlData.AccessControlEnabled = false;
}
$scope.fromContainer = fromContainer;
$scope.config = ContainerHelper.configFromContainer(fromContainer.Model);
loadFromContainerCmd(d);
loadFromContainerLogging(d);
loadFromContainerPortBindings(d);
loadFromContainerVolumes(d);
loadFromContainerNetworkConfig(d);
loadFromContainerEnvironmentVariables(d);
loadFromContainerLabels(d);
loadFromContainerConsole(d);
loadFromContainerDevices(d);
loadFromContainerImageConfig(d);
loadFromContainerResources(d);
loadFromContainerCapabilities(d);
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve container');
});
}
function loadFromContainerLogging(config) {
var logConfig = config.HostConfig.LogConfig;
$scope.formValues.LogDriverName = logConfig.Type;
$scope.formValues.LogDriverOpts = _.map(logConfig.Config, function (value, name) {
return {
name: name,
value: value
};
});
}
function initView() {
2018-05-06 07:15:57 +00:00
var nodeName = $transition$.params().nodeName;
$scope.formValues.NodeName = nodeName;
HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
Volume.query({}, function (d) {
$scope.availableVolumes = d.Volumes;
}, function (e) {
Notifications.error('Failure', e, 'Unable to retrieve volumes');
});
var provider = $scope.applicationState.endpoint.mode.provider;
var apiVersion = $scope.applicationState.endpoint.apiVersion;
NetworkService.networks(
provider === 'DOCKER_STANDALONE' || provider === 'DOCKER_SWARM_MODE',
false,
provider === 'DOCKER_SWARM_MODE' && apiVersion >= 1.25
)
.then(function success(data) {
var networks = data;
networks.push({ Name: 'container' });
$scope.availableNetworks = networks;
if (_.find(networks, {'Name': 'nat'})) {
$scope.config.HostConfig.NetworkMode = 'nat';
}
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve networks');
});
Container.query({}, function (d) {
var containers = d;
$scope.runningContainers = containers;
2018-05-06 07:15:57 +00:00
if ($transition$.params().from) {
loadFromContainerSpec();
} else {
$scope.fromContainer = {};
$scope.formValues.Registry = {};
$scope.formValues.capabilities = new ContainerCapabilities();
}
}, function(e) {
Notifications.error('Failure', e, 'Unable to retrieve running containers');
});
SystemService.info()
.then(function success(data) {
$scope.availableRuntimes = Object.keys(data.Runtimes);
$scope.config.HostConfig.Runtime = '';
$scope.state.sliderMaxCpu = 32;
if (data.NCPU) {
$scope.state.sliderMaxCpu = data.NCPU;
}
$scope.state.sliderMaxMemory = 32768;
if (data.MemTotal) {
$scope.state.sliderMaxMemory = Math.floor(data.MemTotal / 1000 / 1000);
}
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve engine details');
});
SettingsService.publicSettings()
.then(function success(data) {
$scope.allowBindMounts = data.AllowBindMountsForRegularUsers;
$scope.allowPrivilegedMode = data.AllowPrivilegedModeForRegularUsers;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve application settings');
});
PluginService.loggingPlugins(apiVersion < 1.25)
.then(function success(loggingDrivers) {
$scope.availableLoggingDrivers = loggingDrivers;
});
$scope.isAdmin = Authentication.isAdmin();
}
function validateForm(accessControlData, isAdmin) {
$scope.state.formValidationError = '';
var error = '';
error = FormValidator.validateAccessControl(accessControlData, isAdmin);
if (error) {
$scope.state.formValidationError = error;
return false;
}
return true;
}
2017-11-12 19:27:28 +00:00
function create() {
var oldContainer = null;
HttpRequestHelper.setPortainerAgentTargetHeader($scope.formValues.NodeName);
return findCurrentContainer()
.then(setOldContainer)
.then(confirmCreateContainer)
.then(startCreationProcess)
.catch(notifyOnError)
.finally(final);
function final() {
$scope.state.actionInProgress = false;
}
function setOldContainer(container) {
oldContainer = container;
return container;
}
function findCurrentContainer() {
return Container.query({ all: 1, filters: { name: ['^/' + $scope.config.name + '$'] } })
.$promise
.then(function onQuerySuccess(containers) {
if (!containers.length) {
return;
}
return containers[0];
})
.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 pullImageIfNeeded()
.then(stopAndRenameContainer)
.then(createNewContainer)
.then(applyResourceControl)
.then(connectToExtraNetworks)
.then(removeOldContainer)
.then(onSuccess)
.catch(onCreationProcessFail);
}
function onCreationProcessFail(error) {
var deferred = $q.defer();
removeNewContainer()
.then(restoreOldContainerName)
.then(function() {
deferred.reject(error);
})
.catch(function(restoreError) {
deferred.reject(restoreError);
});
return deferred.promise;
}
function removeNewContainer() {
return findCurrentContainer().then(function onContainerLoaded(container) {
if (container && (!oldContainer || container.Id !== oldContainer.Id)) {
return ContainerService.remove(container, true);
}
});
}
function restoreOldContainerName() {
if (!oldContainer) {
return;
}
return ContainerService.renameContainer(oldContainer.Id, oldContainer.Names[0].substring(1));
}
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() {
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;
return validateForm(accessControlData, $scope.isAdmin);
}
function onSuccess() {
Notifications.success('Container successfully created');
$state.go('docker.containers', {}, { reload: true });
}
}
initView();
}]);