wip containers es9 refactor

refactor-es9-migration
baron_l 2019-03-19 22:34:56 +01:00
parent b3bf6b6115
commit fdb44a0cc8
11 changed files with 1619 additions and 958 deletions

View File

@ -2,6 +2,7 @@ env:
browser: true
jquery: true
node: true
es6: true
globals:
angular: true
@ -20,7 +21,7 @@ rules:
# # Possible Errors
# no-await-in-loop: off
# no-cond-assign: error
# no-console: off
no-console: off
# no-constant-condition: error
# no-control-regex: error
# no-debugger: error

View File

@ -25,7 +25,7 @@ angular.module('portainer.docker', ['portainer.app'])
'content@': {
templateUrl: './views/configs/configs.html',
controller: 'ConfigsController',
controllerAs: 'ctrl'
controllerAs: 'ctrl',
}
}
};
@ -37,7 +37,7 @@ angular.module('portainer.docker', ['portainer.app'])
'content@': {
templateUrl: './views/configs/edit/config.html',
controller: 'ConfigController',
controllerAs: 'ctrl'
controllerAs: 'ctrl',
}
}
};
@ -49,7 +49,7 @@ angular.module('portainer.docker', ['portainer.app'])
'content@': {
templateUrl: './views/configs/create/createconfig.html',
controller: 'CreateConfigController',
controllerAs: 'ctrl'
controllerAs: 'ctrl',
}
}
};
@ -60,7 +60,8 @@ angular.module('portainer.docker', ['portainer.app'])
views: {
'content@': {
templateUrl: './views/containers/containers.html',
controller: 'ContainersController'
controller: 'ContainersController',
controllerAs: 'ctrl',
}
}
};
@ -92,8 +93,9 @@ angular.module('portainer.docker', ['portainer.app'])
url: '/new?nodeName&from',
views: {
'content@': {
templateUrl: './views/containers/create/createcontainer.html',
controller: 'CreateContainerController'
templateUrl: './views/containers/create/createContainer.html',
controller: 'CreateContainerController',
controllerAs: 'ctrl',
}
}
};

View File

@ -6,17 +6,17 @@
</rd-header-title>
<rd-header-content>Containers</rd-header-content>
</rd-header>
<information-panel-offline ng-if="offlineMode"></information-panel-offline>
<information-panel-offline ng-if="ctrl.offlineMode"></information-panel-offline>
<div class="row">
<div class="col-sm-12" ng-if="containers">
<div class="col-sm-12" ng-if="ctrl.containers">
<containers-datatable
title-text="Containers" title-icon="fa-server"
dataset="containers" table-key="containers"
dataset="ctrl.containers" table-key="containers"
order-by="Status"
show-ownership-column="applicationState.application.authentication"
show-host-column="applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'"
show-add-action="true"
offline-mode="offlineMode"
offline-mode="ctrl.offlineMode"
></containers-datatable>
</div>
</div>

View File

@ -1,20 +1,26 @@
angular.module('portainer.docker')
.controller('ContainersController', ['$scope', 'ContainerService', 'Notifications', 'EndpointProvider',
function ($scope, ContainerService, Notifications, EndpointProvider) {
import angular from 'angular';
$scope.offlineMode = false;
function initView() {
ContainerService.containers(1)
.then(function success(data) {
$scope.containers = data;
$scope.offlineMode = EndpointProvider.offlineMode();
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve containers');
$scope.containers = [];
});
class ContainersController {
/* @ngInject */
constructor(ContainerService, Notifications, EndpointProvider) {
this.ContainerService = ContainerService;
this.Notifications = Notifications;
this.EndpointProvider = EndpointProvider;
this.offlineMode = false;
}
initView();
}]);
async $onInit() {
try {
let data = await this.ContainerService.containers(1);
this.containers = data;
this.offlineMode = this.EndpointProvider.offlineMode();
}
catch(err) {
this.Notifications.error('Failure', err, 'Unable to retrieve containers');
this.containers = [];
}
}
}
export default ContainersController;
angular.module('portainer.docker').controller('ContainersController', ContainersController);

View File

@ -0,0 +1,55 @@
export function addVolume() {
this.formValues.Volumes.push({ name: '', containerPath: '', readOnly: false, type: 'volume' });
}
export function removeVolume(index) {
this.formValues.Volumes.splice(index, 1);
}
export function addEnvironmentVariable() {
this.config.Env.push({ name: '', value: ''});
}
export function removeEnvironmentVariable(index) {
this.config.Env.splice(index, 1);
}
export function addPortBinding() {
this.config.HostConfig.PortBindings.push({ hostPort: '', containerPort: '', protocol: 'tcp' });
}
export function removePortBinding(index) {
this.config.HostConfig.PortBindings.splice(index, 1);
}
export function addLabel() {
this.formValues.Labels.push({ name: '', value: ''});
}
export function removeLabel(index) {
this.formValues.Labels.splice(index, 1);
}
export function addExtraHost() {
this.formValues.ExtraHosts.push({ value: '' });
}
export function removeExtraHost(index) {
this.formValues.ExtraHosts.splice(index, 1);
}
export function addDevice() {
this.config.HostConfig.Devices.push({ pathOnHost: '', pathInContainer: '' });
}
export function removeDevice(index) {
this.config.HostConfig.Devices.splice(index, 1);
}
export function addLogDriverOpt() {
this.formValues.LogDriverOpts.push({ name: '', value: ''});
}
export function removeLogDriverOpt(index) {
this.formValues.LogDriverOpts.splice(index, 1);
}

View File

@ -0,0 +1,629 @@
import _ from 'lodash-es';
import { ContainerCapabilities, ContainerCapability } from 'Docker/models/containerCapabilities';
import { AccessControlFormData } from 'Portainer/components/accessControlForm/porAccessControlFormModel';
import { ContainerDetailsViewModel } from 'Docker/models/container';
import * as UIActions from './includes/createContainer.UIactions';
import { create } from './includes/create';
import angular from 'angular';
class CreateContainerController {
/* @ngInject */
constructor($q, $scope, $state, $timeout, $transition$, $filter, ContainerHelper, ImageHelper, VolumeService, NetworkService, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator, ModalService, RegistryService, SystemService, SettingsService, PluginService, HttpRequestHelper) {
this.$q = $q;
this.$scope = $scope; // TODO : Remove this temporary scope save for ApplicationState
this.$state = $state;
this.$timeout = $timeout;
this.$transition$ = $transition$;
this.$filter = $filter;
this.ContainerHelper = ContainerHelper;
this.ImageHelper = ImageHelper;
this.HttpRequestHelper = HttpRequestHelper;
this.VolumeService = VolumeService;
this.NetworkService = NetworkService;
this.ContainerService = ContainerService;
this.ImageService = ImageService;
this.RegistryService = RegistryService;
this.ResourceControlService = ResourceControlService;
this.SystemService = SystemService;
this.SettingsService = SettingsService;
this.PluginService = PluginService;
this.Authentication = Authentication;
this.Notifications = Notifications;
this.FormValidator = FormValidator;
this.ModalService = ModalService;
this.formValues = {
alwaysPull: true,
Console: 'none',
Volumes: [],
NetworkContainer: '',
Labels: [],
ExtraHosts: [],
MacAddress: '',
IPv4: '',
IPv6: '',
AccessControlData: new AccessControlFormData(),
CpuLimit: 0,
MemoryLimit: 0,
MemoryReservation: 0,
NodeName: null,
capabilities: [],
LogDriverName: '',
LogDriverOpts: []
};
this.extraNetworks = {};
this.state = {
formValidationError: '',
actionInProgress: false
};
this.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: {}
};
this.fromContainerMultipleNetworks = false;
// imports
this.create = create.bind(this);
}
initUIActions() {
// UI Actions
for (const func in UIActions) {
console.log(func);
this[func] = UIActions[func];
}
// this.addVolume = UIActions.addVolume;
}
refreshSlider() {
this.$timeout(function () {
this.$broadcast('rzSliderForceRender');
});
}
prepareImageConfig(config) {
var image = config.Image;
var registry = this.formValues.Registry;
var imageConfig = this.ImageHelper.createImageConfigForContainer(image, registry.URL);
config.Image = imageConfig.fromImage + ':' + imageConfig.tag;
this.imageConfig = imageConfig;
}
preparePortBindings(config) {
var bindings = {};
for (const portBinding of config.HostConfig.PortBindings) {
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;
}
prepareConsole(config) {
var value = this.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;
}
prepareEnvironmentVariables(config) {
var env = [];
for (const v of config.Env) {
if (v.name && v.value) {
env.push(v.name + '=' + v.value);
}
}
config.Env = env;
}
prepareVolumes(config) {
var binds = [];
var volumes = {};
for (const volume of this.formValues.Volumes) {
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;
}
prepareNetworkConfig(config) {
var mode = config.HostConfig.NetworkMode;
var container = this.formValues.NetworkContainer;
var containerName = container;
if (container && typeof container === 'object') {
containerName = this.$filter('trimcontainername')(container.Names[0]);
}
var networkMode = mode;
if (containerName) {
networkMode += ':' + containerName;
config.Hostname = '';
}
config.HostConfig.NetworkMode = networkMode;
config.MacAddress = this.formValues.MacAddress;
config.NetworkingConfig.EndpointsConfig[networkMode] = {
IPAMConfig: {
IPv4Address: this.formValues.IPv4,
IPv6Address: this.formValues.IPv6
}
};
for (const v of this.formValues.ExtraHosts) {
if (v.value) {
config.HostConfig.ExtraHosts.push(v.value);
}
}
}
prepareLabels(config) {
var labels = {};
for (const label of this.formValues.Labels) {
if (label.name && label.value) {
labels[label.name] = label.value;
}
}
config.Labels = labels;
}
prepareDevices(config) {
var path = [];
for (const p of config.HostConfig.Devices) {
if (p.pathOnHost) {
if(p.pathInContainer === '') {
p.pathInContainer = p.pathOnHost;
}
path.push({PathOnHost:p.pathOnHost,PathInContainer:p.pathInContainer,CgroupPermissions:'rwm'});
}
}
config.HostConfig.Devices = path;
}
prepareResources(config) {
// Memory Limit - Round to 0.125
var memoryLimit = (Math.round(this.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(this.formValues.MemoryReservation * 8) / 8).toFixed(3);
memoryReservation *= 1024 * 1024;
if (memoryReservation > 0) {
config.HostConfig.MemoryReservation = memoryReservation;
}
// CPU Limit
if (this.formValues.CpuLimit > 0) {
config.HostConfig.NanoCpus = this.formValues.CpuLimit * 1000000000;
}
}
prepareLogDriver(config) {
var logOpts = {};
if (this.formValues.LogDriverName) {
config.HostConfig.LogConfig = { Type: this.formValues.LogDriverName };
if (this.formValues.LogDriverName !== 'none') {
for (const opt of this.formValues.LogDriverOpts) {
if (opt.name) {
logOpts[opt.name] = opt.value;
}
}
if (Object.keys(logOpts).length !== 0 && logOpts.constructor === Object) {
config.HostConfig.LogConfig.Config = logOpts;
}
}
}
}
prepareCapabilities(config) {
console.log('prepare capabilities');
var allowed = this.formValues.capabilities.filter(function(item) {return item.allowed === true;});
var notAllowed = this.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);
}
prepareConfiguration() {
var config = angular.copy(this.config);
config.Cmd = this.ContainerHelper.commandStringToArray(config.Cmd);
this.prepareNetworkConfig(config);
this.prepareImageConfig(config);
this.preparePortBindings(config);
this.prepareConsole(config);
this.prepareEnvironmentVariables(config);
this.prepareVolumes(config);
this.prepareLabels(config);
this.prepareDevices(config);
this.prepareResources(config);
this.prepareLogDriver(config);
this.prepareCapabilities(config);
return config;
}
loadFromContainerCmd() {
if (this.config.Cmd) {
this.config.Cmd = this.ContainerHelper.commandArrayToString(this.config.Cmd);
} else {
this.config.Cmd = '';
}
}
loadFromContainerPortBindings() {
var bindings = [];
for (var p in this.config.HostConfig.PortBindings) {
if ({}.hasOwnProperty.call(this.config.HostConfig.PortBindings, p)) {
var hostPort = '';
if (this.config.HostConfig.PortBindings[p][0].HostIp) {
hostPort = this.config.HostConfig.PortBindings[p][0].HostIp + ':';
}
hostPort += this.config.HostConfig.PortBindings[p][0].HostPort;
var b = {
'hostPort': hostPort,
'containerPort': p.split('/')[0],
'protocol': p.split('/')[1]
};
bindings.push(b);
}
}
this.config.HostConfig.PortBindings = bindings;
}
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
};
this.formValues.Volumes.push(volume);
}
}
}
resetNetworkConfig() {
this.config.NetworkingConfig = {
EndpointsConfig: {}
};
}
loadFromContainerNetworkConfig(d) {
this.config.NetworkingConfig = {
EndpointsConfig: {}
};
var networkMode = d.HostConfig.NetworkMode;
if (networkMode === 'default') {
this.config.HostConfig.NetworkMode = 'bridge';
if (!_.find(this.availableNetworks, {'Name': 'bridge'})) {
this.config.HostConfig.NetworkMode = 'nat';
}
}
if (this.config.HostConfig.NetworkMode.indexOf('container:') === 0) {
var netContainer = this.config.HostConfig.NetworkMode.split(/^container:/)[1];
this.config.HostConfig.NetworkMode = 'container';
for (var c in this.runningContainers) {
if (this.runningContainers[c].Names && this.runningContainers[c].Names[0] === '/' + netContainer) {
this.formValues.NetworkContainer = this.runningContainers[c];
}
}
}
this.fromContainerMultipleNetworks = Object.keys(d.NetworkSettings.Networks).length >= 2;
if (d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode]) {
if (d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode].IPAMConfig) {
if (d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode].IPAMConfig.IPv4Address) {
this.formValues.IPv4 = d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode].IPAMConfig.IPv4Address;
}
if (d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode].IPAMConfig.IPv6Address) {
this.formValues.IPv6 = d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode].IPAMConfig.IPv6Address;
}
}
}
this.config.NetworkingConfig.EndpointsConfig[this.config.HostConfig.NetworkMode] = d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode];
// Mac Address
if(Object.keys(d.NetworkSettings.Networks).length) {
var firstNetwork = d.NetworkSettings.Networks[Object.keys(d.NetworkSettings.Networks)[0]];
this.formValues.MacAddress = firstNetwork.MacAddress;
this.config.NetworkingConfig.EndpointsConfig[this.config.HostConfig.NetworkMode] = firstNetwork;
this.extraNetworks = angular.copy(d.NetworkSettings.Networks);
delete this.extraNetworks[Object.keys(d.NetworkSettings.Networks)[0]];
} else {
this.formValues.MacAddress = '';
}
// ExtraHosts
if (this.config.HostConfig.ExtraHosts) {
var extraHosts = this.config.HostConfig.ExtraHosts;
for (var i = 0; i < extraHosts.length; i++) {
var host = extraHosts[i];
this.formValues.ExtraHosts.push({ 'value': host });
}
this.config.HostConfig.ExtraHosts = [];
}
}
loadFromContainerEnvironmentVariables() {
var envArr = [];
for (var e in this.config.Env) {
if ({}.hasOwnProperty.call(this.config.Env, e)) {
var arr = this.config.Env[e].split(/\=(.+)/);
envArr.push({'name': arr[0], 'value': arr[1]});
}
}
this.config.Env = envArr;
}
loadFromContainerLabels() {
for (var l in this.config.Labels) {
if ({}.hasOwnProperty.call(this.config.Labels, l)) {
this.formValues.Labels.push({ name: l, value: this.config.Labels[l]});
}
}
}
loadFromContainerConsole() {
if (this.config.OpenStdin && this.config.Tty) {
this.formValues.Console = 'both';
} else if (!this.config.OpenStdin && this.config.Tty) {
this.formValues.Console = 'tty';
} else if (this.config.OpenStdin && !this.config.Tty) {
this.formValues.Console = 'interactive';
} else if (!this.config.OpenStdin && !this.config.Tty) {
this.formValues.Console = 'none';
}
}
loadFromContainerDevices() {
var path = [];
for (var dev in this.config.HostConfig.Devices) {
if ({}.hasOwnProperty.call(this.config.HostConfig.Devices, dev)) {
var device = this.config.HostConfig.Devices[dev];
path.push({'pathOnHost': device.PathOnHost, 'pathInContainer': device.PathInContainer});
}
}
this.config.HostConfig.Devices = path;
}
loadFromContainerImageConfig() {
var imageInfo = this.ImageHelper.extractImageAndRegistryFromRepository(this.config.Image);
this.RegistryService.retrieveRegistryFromRepository(this.config.Image)
.then(function success(data) {
if (data) {
this.config.Image = imageInfo.image;
this.formValues.Registry = data;
}
})
.catch(function error(err) {
this.Notifications.error('Failure', err, 'Unable to retrive registry');
});
}
loadFromContainerResources(d) {
if (d.HostConfig.NanoCpus) {
this.formValues.CpuLimit = d.HostConfig.NanoCpus / 1000000000;
}
if (d.HostConfig.Memory) {
this.formValues.MemoryLimit = d.HostConfig.Memory / 1024 / 1024;
}
if (d.HostConfig.MemoryReservation) {
this.formValues.MemoryReservation = d.HostConfig.MemoryReservation / 1024 / 1024;
}
}
loadFromContainerCapabilities(d) {
if (d.HostConfig.CapAdd) {
for (const cap of d.HostConfig.CapAdd) {
this.formValues.capabilities.push(new ContainerCapability(cap, true));
}
}
if (d.HostConfig.CapDrop) {
for (const cap of d.HostConfig.CapDrop) {
this.formValues.capabilities.push(new ContainerCapability(cap, false));
}
}
var capabilities = new ContainerCapabilities();
for (var i = 0; i < capabilities.length; i++) {
var cap = capabilities[i];
if (!_.find(this.formValues.capabilities, (item) => item.capability === cap.capability)) {
this.formValues.capabilities.push(cap);
}
}
this.formValues.capabilities.sort((a, b) => a.capability < b.capability ? -1 : 1);
}
loadFromContainerSpec() {
// Get container
this.ContainerService.container(this.$transition$.params().from).$promise
.then(function success(d) {
var fromContainer = new ContainerDetailsViewModel(d);
if (fromContainer.ResourceControl && fromContainer.ResourceControl.Public) {
this.formValues.AccessControlData.AccessControlEnabled = false;
}
this.fromContainer = fromContainer;
this.config = this.ContainerHelper.configFromContainer(fromContainer.Model);
this.loadFromContainerCmd(d);
this.loadFromContainerLogging(d);
this.loadFromContainerPortBindings(d);
this.loadFromContainerVolumes(d);
this.loadFromContainerNetworkConfig(d);
this.loadFromContainerEnvironmentVariables(d);
this.loadFromContainerLabels(d);
this.loadFromContainerConsole(d);
this.loadFromContainerDevices(d);
this.loadFromContainerImageConfig(d);
this.loadFromContainerResources(d);
this.loadFromContainerCapabilities(d);
})
.catch(function error(err) {
this.Notifications.error('Failure', err, 'Unable to retrieve container');
});
}
loadFromContainerLogging(config) {
var logConfig = config.HostConfig.LogConfig;
this.formValues.LogDriverName = logConfig.Type;
this.formValues.LogDriverOpts = _.map(logConfig.Config, function (value, name) {
return {
name: name,
value: value
};
});
}
async $onInit() {
var nodeName = this.$transition$.params().nodeName;
this.formValues.NodeName = nodeName;
this.HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
try {
let data = await this.VolumeService.volumes({});
this.availableVolumes = data;
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve volumes');
}
var provider = this.$scope.applicationState.endpoint.mode.provider;
var apiVersion = this.$scope.applicationState.endpoint.apiVersion;
try {
let data = await this.NetworkService.networks(
provider === 'DOCKER_STANDALONE' || provider === 'DOCKER_SWARM_MODE',
false,
provider === 'DOCKER_SWARM_MODE' && apiVersion >= 1.25
);
var networks = data;
networks.push({ Name: 'container' });
this.availableNetworks = networks;
if (_.find(networks, {'Name': 'nat'})) {
this.config.HostConfig.NetworkMode = 'nat';
}
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve networks');
}
try {
this.runningContainers = await this.ContainerService.containers();
if (this.$transition$.params().from) {
this.loadFromContainerSpec();
} else {
this.fromContainer = {};
this.formValues.Registry = {};
this.formValues.capabilities = new ContainerCapabilities();
}
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve running containers');
}
try {
let data = await this.SystemService.info();
this.availableRuntimes = Object.keys(data.Runtimes);
this.config.HostConfig.Runtime = '';
this.state.sliderMaxCpu = 32;
if (data.NCPU) {
this.state.sliderMaxCpu = data.NCPU;
}
this.state.sliderMaxMemory = 32768;
if (data.MemTotal) {
this.state.sliderMaxMemory = Math.floor(data.MemTotal / 1000 / 1000);
}
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve engine details');
}
try {
let data = await this.SettingsService.publicSettings();
this.allowBindMounts = data.AllowBindMountsForRegularUsers;
this.allowPrivilegedMode = data.AllowPrivilegedModeForRegularUsers;
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve application settings');
}
try {
this.availableLoggingDrivers = await this.PluginService.loggingPlugins(apiVersion < 1.25);
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to load logging plugins');
}
var userDetails = this.Authentication.getUserDetails();
this.isAdmin = userDetails.role === 1;
this.initUIActions();
console.log(this);
}
validateForm(accessControlData, isAdmin) {
this.state.formValidationError = '';
var error = '';
error = this.FormValidator.validateAccessControl(accessControlData, isAdmin);
if (error) {
this.state.formValidationError = error;
return false;
}
return true;
}
}
export default CreateContainerController;
angular.module('portainer.docker').controller('tmp', CreateContainerController);

View File

@ -0,0 +1,437 @@
import angular from 'angular';
import _ from 'lodash-es';
import { ContainerCapabilities, ContainerCapability } from 'Docker/models/containerCapabilities';
import { AccessControlFormData } from 'Portainer/components/accessControlForm/porAccessControlFormModel';
import { ContainerDetailsViewModel } from 'Docker/models/container';
import * as UIActions from './createContainer.UIactions';
import createContainer from './createContainer.function';
import prepareConfiguration from './prepareConfiguration.function';
class CreateContainerController {
/* @ngInject */
constructor($q, $scope, $state, $timeout, $transition$, $filter, ContainerHelper, ImageHelper, VolumeService, NetworkService, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator, ModalService, RegistryService, SystemService, SettingsService, PluginService, HttpRequestHelper) {
this.$q = $q;
this.$scope = $scope; // TODO : Remove this temporary scope save for ApplicationState
this.$state = $state;
this.$timeout = $timeout;
this.$transition$ = $transition$;
this.$filter = $filter;
this.ContainerHelper = ContainerHelper;
this.ImageHelper = ImageHelper;
this.HttpRequestHelper = HttpRequestHelper;
this.VolumeService = VolumeService;
this.NetworkService = NetworkService;
this.ContainerService = ContainerService;
this.ImageService = ImageService;
this.RegistryService = RegistryService;
this.ResourceControlService = ResourceControlService;
this.SystemService = SystemService;
this.SettingsService = SettingsService;
this.PluginService = PluginService;
this.Authentication = Authentication;
this.Notifications = Notifications;
this.FormValidator = FormValidator;
this.ModalService = ModalService;
this.formValues = {
alwaysPull: true,
Console: 'none',
Volumes: [],
NetworkContainer: '',
Labels: [],
ExtraHosts: [],
MacAddress: '',
IPv4: '',
IPv6: '',
AccessControlData: new AccessControlFormData(),
CpuLimit: 0,
MemoryLimit: 0,
MemoryReservation: 0,
NodeName: null,
capabilities: [],
LogDriverName: '',
LogDriverOpts: []
};
this.extraNetworks = {};
this.state = {
formValidationError: '',
actionInProgress: false
};
this.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: {}
};
this.fromContainerMultipleNetworks = false;
// bind partial-class functions to the controller
this.createContainer = createContainer;
this.prepareConfiguration = prepareConfiguration;
}
refreshSlider() {
this.$timeout(function () {
this.$broadcast('rzSliderForceRender');
});
}
loadFromContainerCmd() {
if (this.config.Cmd) {
this.config.Cmd = this.ContainerHelper.commandArrayToString(this.config.Cmd);
} else {
this.config.Cmd = '';
}
}
loadFromContainerPortBindings() {
var bindings = [];
for (var p in this.config.HostConfig.PortBindings) {
if ({}.hasOwnProperty.call(this.config.HostConfig.PortBindings, p)) {
var hostPort = '';
if (this.config.HostConfig.PortBindings[p][0].HostIp) {
hostPort = this.config.HostConfig.PortBindings[p][0].HostIp + ':';
}
hostPort += this.config.HostConfig.PortBindings[p][0].HostPort;
var b = {
'hostPort': hostPort,
'containerPort': p.split('/')[0],
'protocol': p.split('/')[1]
};
bindings.push(b);
}
}
this.config.HostConfig.PortBindings = bindings;
}
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
};
this.formValues.Volumes.push(volume);
}
}
}
resetNetworkConfig() {
this.config.NetworkingConfig = {
EndpointsConfig: {}
};
}
loadFromContainerNetworkConfig(d) {
this.config.NetworkingConfig = {
EndpointsConfig: {}
};
var networkMode = d.HostConfig.NetworkMode;
if (networkMode === 'default') {
this.config.HostConfig.NetworkMode = 'bridge';
if (!_.find(this.availableNetworks, {'Name': 'bridge'})) {
this.config.HostConfig.NetworkMode = 'nat';
}
}
if (this.config.HostConfig.NetworkMode.indexOf('container:') === 0) {
var netContainer = this.config.HostConfig.NetworkMode.split(/^container:/)[1];
this.config.HostConfig.NetworkMode = 'container';
for (var c in this.runningContainers) {
if (this.runningContainers[c].Names && this.runningContainers[c].Names[0] === '/' + netContainer) {
this.formValues.NetworkContainer = this.runningContainers[c];
}
}
}
this.fromContainerMultipleNetworks = Object.keys(d.NetworkSettings.Networks).length >= 2;
if (d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode]) {
if (d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode].IPAMConfig) {
if (d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode].IPAMConfig.IPv4Address) {
this.formValues.IPv4 = d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode].IPAMConfig.IPv4Address;
}
if (d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode].IPAMConfig.IPv6Address) {
this.formValues.IPv6 = d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode].IPAMConfig.IPv6Address;
}
}
}
this.config.NetworkingConfig.EndpointsConfig[this.config.HostConfig.NetworkMode] = d.NetworkSettings.Networks[this.config.HostConfig.NetworkMode];
// Mac Address
if(Object.keys(d.NetworkSettings.Networks).length) {
var firstNetwork = d.NetworkSettings.Networks[Object.keys(d.NetworkSettings.Networks)[0]];
this.formValues.MacAddress = firstNetwork.MacAddress;
this.config.NetworkingConfig.EndpointsConfig[this.config.HostConfig.NetworkMode] = firstNetwork;
this.extraNetworks = angular.copy(d.NetworkSettings.Networks);
delete this.extraNetworks[Object.keys(d.NetworkSettings.Networks)[0]];
} else {
this.formValues.MacAddress = '';
}
// ExtraHosts
if (this.config.HostConfig.ExtraHosts) {
var extraHosts = this.config.HostConfig.ExtraHosts;
for (var i = 0; i < extraHosts.length; i++) {
var host = extraHosts[i];
this.formValues.ExtraHosts.push({ 'value': host });
}
this.config.HostConfig.ExtraHosts = [];
}
}
loadFromContainerEnvironmentVariables() {
var envArr = [];
for (var e in this.config.Env) {
if ({}.hasOwnProperty.call(this.config.Env, e)) {
var arr = this.config.Env[e].split(/\=(.+)/);
envArr.push({'name': arr[0], 'value': arr[1]});
}
}
this.config.Env = envArr;
}
loadFromContainerLabels() {
for (var l in this.config.Labels) {
if ({}.hasOwnProperty.call(this.config.Labels, l)) {
this.formValues.Labels.push({ name: l, value: this.config.Labels[l]});
}
}
}
loadFromContainerConsole() {
if (this.config.OpenStdin && this.config.Tty) {
this.formValues.Console = 'both';
} else if (!this.config.OpenStdin && this.config.Tty) {
this.formValues.Console = 'tty';
} else if (this.config.OpenStdin && !this.config.Tty) {
this.formValues.Console = 'interactive';
} else if (!this.config.OpenStdin && !this.config.Tty) {
this.formValues.Console = 'none';
}
}
loadFromContainerDevices() {
var path = [];
for (var dev in this.config.HostConfig.Devices) {
if ({}.hasOwnProperty.call(this.config.HostConfig.Devices, dev)) {
var device = this.config.HostConfig.Devices[dev];
path.push({'pathOnHost': device.PathOnHost, 'pathInContainer': device.PathInContainer});
}
}
this.config.HostConfig.Devices = path;
}
loadFromContainerImageConfig() {
var imageInfo = this.ImageHelper.extractImageAndRegistryFromRepository(this.config.Image);
this.RegistryService.retrieveRegistryFromRepository(this.config.Image)
.then(function success(data) {
if (data) {
this.config.Image = imageInfo.image;
this.formValues.Registry = data;
}
})
.catch(function error(err) {
this.Notifications.error('Failure', err, 'Unable to retrive registry');
});
}
loadFromContainerResources(d) {
if (d.HostConfig.NanoCpus) {
this.formValues.CpuLimit = d.HostConfig.NanoCpus / 1000000000;
}
if (d.HostConfig.Memory) {
this.formValues.MemoryLimit = d.HostConfig.Memory / 1024 / 1024;
}
if (d.HostConfig.MemoryReservation) {
this.formValues.MemoryReservation = d.HostConfig.MemoryReservation / 1024 / 1024;
}
}
loadFromContainerCapabilities(d) {
if (d.HostConfig.CapAdd) {
for (const cap of d.HostConfig.CapAdd) {
this.formValues.capabilities.push(new ContainerCapability(cap, true));
}
}
if (d.HostConfig.CapDrop) {
for (const cap of d.HostConfig.CapDrop) {
this.formValues.capabilities.push(new ContainerCapability(cap, false));
}
}
var capabilities = new ContainerCapabilities();
for (var i = 0; i < capabilities.length; i++) {
var cap = capabilities[i];
if (!_.find(this.formValues.capabilities, (item) => item.capability === cap.capability)) {
this.formValues.capabilities.push(cap);
}
}
this.formValues.capabilities.sort((a, b) => a.capability < b.capability ? -1 : 1);
}
loadFromContainerSpec() {
// Get container
this.Container.get({ id: this.$transition$.params().from }).$promise
.then(function success(d) {
var fromContainer = new ContainerDetailsViewModel(d);
if (fromContainer.ResourceControl && fromContainer.ResourceControl.Public) {
this.formValues.AccessControlData.AccessControlEnabled = false;
}
this.fromContainer = fromContainer;
this.config = this.ContainerHelper.configFromContainer(fromContainer.Model);
this.loadFromContainerCmd(d);
this.loadFromContainerLogging(d);
this.loadFromContainerPortBindings(d);
this.loadFromContainerVolumes(d);
this.loadFromContainerNetworkConfig(d);
this.loadFromContainerEnvironmentVariables(d);
this.loadFromContainerLabels(d);
this.loadFromContainerConsole(d);
this.loadFromContainerDevices(d);
this.loadFromContainerImageConfig(d);
this.loadFromContainerResources(d);
this.loadFromContainerCapabilities(d);
})
.catch(function error(err) {
this.Notifications.error('Failure', err, 'Unable to retrieve container');
});
}
loadFromContainerLogging(config) {
var logConfig = config.HostConfig.LogConfig;
this.formValues.LogDriverName = logConfig.Type;
this.formValues.LogDriverOpts = _.map(logConfig.Config, function (value, name) {
return {
name: name,
value: value
};
});
}
initUIActions() {
for (const func in UIActions) {
this[func] = UIActions[func];
}
}
async $onInit() {
this.initUIActions();
var nodeName = this.$transition$.params().nodeName;
this.formValues.NodeName = nodeName;
this.HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
try {
let data = await this.VolumeService.volumes({});
this.availableVolumes = data.Volumes;
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve volumes');
}
var provider = this.$scope.applicationState.endpoint.mode.provider;
var apiVersion = this.$scope.applicationState.endpoint.apiVersion;
try {
let data = await this.NetworkService.networks(
provider === 'DOCKER_STANDALONE' || provider === 'DOCKER_SWARM_MODE',
false,
provider === 'DOCKER_SWARM_MODE' && apiVersion >= 1.25
);
var networks = data;
networks.push({ Name: 'container' });
this.availableNetworks = networks;
if (_.find(networks, {'Name': 'nat'})) {
this.config.HostConfig.NetworkMode = 'nat';
}
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve networks');
}
try {
this.runningContainers = await this.ContainerService.containers();
if (this.$transition$.params().from) {
this.loadFromContainerSpec();
} else {
this.fromContainer = {};
this.formValues.Registry = {};
this.formValues.capabilities = new ContainerCapabilities();
}
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve running containers');
}
try {
let data = await this.SystemService.info();
this.availableRuntimes = Object.keys(data.Runtimes);
this.config.HostConfig.Runtime = '';
this.state.sliderMaxCpu = 32;
if (data.NCPU) {
this.state.sliderMaxCpu = data.NCPU;
}
this.state.sliderMaxMemory = 32768;
if (data.MemTotal) {
this.state.sliderMaxMemory = Math.floor(data.MemTotal / 1000 / 1000);
}
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve engine details');
}
try {
let data = await this.SettingsService.publicSettings();
this.allowBindMounts = data.AllowBindMountsForRegularUsers;
this.allowPrivilegedMode = data.AllowPrivilegedModeForRegularUsers;
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve application settings');
}
try {
this.availableLoggingDrivers = await this.PluginService.loggingPlugins(apiVersion < 1.25);
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to load logging plugins');
}
var userDetails = this.Authentication.getUserDetails();
this.isAdmin = userDetails.role === 1;
}
validateForm(accessControlData, isAdmin) {
this.state.formValidationError = '';
var error = '';
error = this.FormValidator.validateAccessControl(accessControlData, isAdmin);
if (error) {
this.state.formValidationError = error;
return false;
}
return true;
}
}
export default CreateContainerController;
angular.module('portainer.docker').controller('CreateContainerController', CreateContainerController);

View File

@ -0,0 +1,204 @@
export default async function createContainer() {
var oldContainer = null;
this.HttpRequestHelper.setPortainerAgentTargetHeader(this.formValues.NodeName);
let final = () => {
this.state.actionInProgress = false;
};
let setOldContainer = (container) => {
oldContainer = container;
return container;
};
let findCurrentContainer = () => {
let onQuerySuccess = (containers) => {
if (!containers.length) {
return;
}
return containers[0];
};
let notifyOnError = (err) => {
this.Notifications.error('Failure', err, 'Unable to retrieve containers');
}
return this.ContainerService.containers(1, { name: ['^/' + this.config.name + '$'] })
.then(onQuerySuccess)
.catch(notifyOnError);
};
let startCreationProcess = (confirmed) => {
if (!confirmed) {
return this.$q.when();
}
if (!validateAccessControl()) {
return this.$q.when();
}
this.state.actionInProgress = true;
return pullImageIfNeeded()
.then(stopAndRenameContainer)
.then(createNewContainer)
.then(applyResourceControl)
.then(connectToExtraNetworks)
.then(removeOldContainer)
.then(onSuccess)
.catch(onCreationProcessFail);
};
let onCreationProcessFail = (error) => {
var deferred = this.$q.defer();
removeNewContainer()
.then(restoreOldContainerName)
.then(() => deferred.reject(error))
.catch((restoreError) => deferred.reject(restoreError));
return deferred.promise;
};
let removeNewContainer = () => {
let onContainerLoaded = (container) => {
if (container && (!oldContainer || container.Id !== oldContainer.Id)) {
return this.ContainerService.remove(container, true);
}
};
return findCurrentContainer().then(onContainerLoaded);
};
let restoreOldContainerName = () => {
if (!oldContainer) {
return;
}
return this.ContainerService.renameContainer(oldContainer.Id, oldContainer.Names[0].substring(1));
};
let confirmCreateContainer = (container) => {
if (!container) {
return this.$q.when(true);
}
let showConfirmationModal = () => {
var deferred = this.$q.defer();
let onConfirm = (confirmed) => deferred.resolve(confirmed);
this.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: onConfirm
});
return deferred.promise;
}
return showConfirmationModal();
};
let stopAndRenameContainer = () => {
if (!oldContainer) {
return this.$q.when();
}
return stopContainerIfNeeded(oldContainer)
.then(renameContainer);
};
let stopContainerIfNeeded = (oldContainer) => {
if (oldContainer.State !== 'running') {
return this.$q.when();
}
return this.ContainerService.stopContainer(oldContainer.Id);
};
let renameContainer = () => {
return this.ContainerService.renameContainer(oldContainer.Id, oldContainer.Names[0].substring(1) + '-old');
};
let pullImageIfNeeded = () => {
return this.$q.when(this.formValues.alwaysPull &&
this.ImageService.pullImage(this.config.Image, this.formValues.Registry, true));
};
let createNewContainer = () => {
var config = this.prepareConfiguration();
return this.ContainerService.createAndStartContainer(config);
};
let applyResourceControl = (newContainer) => {
var containerIdentifier = newContainer.Id;
var userId = this.Authentication.getUserDetails().ID;
let onApplyResourceControlSuccess = () => containerIdentifier;
return this.$q.when(this.ResourceControlService.applyResourceControl(
'container',
containerIdentifier,
userId,
this.formValues.AccessControlData, []
)).then(onApplyResourceControlSuccess);
};
let connectToExtraNetworks = (newContainerId) => {
if (!this.extraNetworks) {
return this.$q.when();
}
var connectionPromises = Object.keys(this.extraNetworks).map((networkName) => {
return this.NetworkService.connectContainer(networkName, newContainerId);
});
return this.$q.all(connectionPromises);
};
let removeOldContainer = () => {
var deferred = this.$q.defer();
if (!oldContainer) {
deferred.resolve();
return;
}
let notifyOnRemoval = () => {
this.Notifications.success('Container Removed', oldContainer.Id);
deferred.resolve();
};
let notifyOnRemoveError = (err) => {
deferred.reject({ msg: 'Unable to remove container', err: err });
};
this.ContainerService.remove(oldContainer, true)
.then(notifyOnRemoval)
.catch(notifyOnRemoveError);
return deferred.promise;
};
let notifyOnError = (err) => {
this.Notifications.error('Failure', err, 'Unable to create container');
};
let validateAccessControl = () => {
var accessControlData = this.formValues.AccessControlData;
var userDetails = this.Authentication.getUserDetails();
var isAdmin = userDetails.role === 1;
return this.validateForm(accessControlData, isAdmin);
};
let onSuccess = () => {
this.Notifications.success('Container successfully created');
this.$state.go('docker.containers', {}, { reload: true });
};
return findCurrentContainer()
.then(setOldContainer)
.then(confirmCreateContainer)
.then(startCreationProcess)
.catch(notifyOnError)
.finally(final);
}

View File

@ -14,23 +14,23 @@
<div class="form-group">
<label for="container_name" class="col-sm-1 control-label text-left">Name</label>
<div class="col-sm-11">
<input type="text" class="form-control" ng-model="config.name" id="container_name" placeholder="e.g. myContainer">
<input type="text" class="form-control" ng-model="ctrl.config.name" id="container_name" placeholder="e.g. myContainer">
</div>
</div>
<!-- !name-input -->
<div class="col-sm-12 form-section-title">
Image configuration
</div>
<div ng-if="!formValues.Registry && fromContainer">
<div ng-if="!ctrl.formValues.Registry && fromContainer">
<i class="fa fa-exclamation-triangle orange-icon" aria-hidden="true"></i>
<span class="small text-danger" style="margin-left: 5px;">The Docker registry for the <code>{{ config.Image }}</code> image is not registered inside Portainer, you will not be able to create a container. Please register that registry first.</span>
<span class="small text-danger" style="margin-left: 5px;">The Docker registry for the <code>{{ ctrl.config.Image }}</code> image is not registered inside Portainer, you will not be able to create a container. Please register that registry first.</span>
</div>
<div ng-if="formValues.Registry || !fromContainer">
<div ng-if="ctrl.formValues.Registry || !fromContainer">
<!-- image-and-registry -->
<por-image-registry
image="config.Image"
registry="formValues.Registry"
ng-if="formValues.Registry"
image="ctrl.config.Image"
registry="ctrl.formValues.Registry"
ng-if="ctrl.formValues.Registry"
auto-complete="true"
label-class="col-sm-1" input-class="col-sm-11 col-md-5"
></por-image-registry>
@ -43,7 +43,7 @@
<portainer-tooltip position="bottom" message="When enabled, Portainer will automatically try to pull the specified image before creating the container."></portainer-tooltip>
</label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" ng-model="formValues.alwaysPull"><i></i>
<input type="checkbox" ng-model="ctrl.formValues.alwaysPull"><i></i>
</label>
</div>
</div>
@ -60,7 +60,7 @@
<portainer-tooltip position="bottom" message="When enabled, Portainer will let Docker automatically map a random port on the host to each one defined in the image Dockerfile."></portainer-tooltip>
</label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" ng-model="config.HostConfig.PublishAllPorts"><i></i>
<input type="checkbox" ng-model="ctrl.config.HostConfig.PublishAllPorts"><i></i>
</label>
</div>
</div>
@ -69,13 +69,13 @@
<div class="form-group">
<div class="col-sm-12">
<label class="control-label text-left">Port mapping</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="addPortBinding()">
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="ctrl.addPortBinding()">
<i class="fa fa-plus-circle" aria-hidden="true"></i> map additional port
</span>
</div>
<!-- port-mapping-input-list -->
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
<div ng-repeat="portBinding in config.HostConfig.PortBindings" style="margin-top: 2px;">
<div ng-repeat="portBinding in ctrl.config.HostConfig.PortBindings" style="margin-top: 2px;">
<!-- host-port -->
<div class="input-group col-sm-4 input-group-sm">
<span class="input-group-addon">host</span>
@ -97,7 +97,7 @@
<label class="btn btn-primary" ng-model="portBinding.protocol" uib-btn-radio="'tcp'">TCP</label>
<label class="btn btn-primary" ng-model="portBinding.protocol" uib-btn-radio="'udp'">UDP</label>
</div>
<button class="btn btn-sm btn-danger" type="button" ng-click="removePortBinding($index)">
<button class="btn btn-sm btn-danger" type="button" ng-click="ctrl.removePortBinding($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
@ -113,12 +113,12 @@
</div>
<!-- node-selection -->
<node-selector
model="formValues.NodeName">
model="ctrl.formValues.NodeName">
</node-selector>
<!-- !node-selection -->
</div>
<!-- access-control -->
<por-access-control-form form-data="formValues.AccessControlData" resource-control="fromContainer.ResourceControl" ng-if="applicationState.application.authentication && fromContainer"></por-access-control-form>
<por-access-control-form form-data="ctrl.formValues.AccessControlData" resource-control="ctrl.fromContainer.ResourceControl" ng-if="applicationState.application.authentication && fromContainer"></por-access-control-form>
<!-- !access-control -->
<!-- actions -->
<div class="col-sm-12 form-section-title">
@ -132,18 +132,18 @@
<portainer-tooltip position="bottom" message="When enabled, Portainer will automatically remove the container when it exits. This is useful when you want to use the container only once."></portainer-tooltip>
</label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" ng-model="config.HostConfig.AutoRemove"><i></i>
<input type="checkbox" ng-model="ctrl.config.HostConfig.AutoRemove"><i></i>
</label>
</div>
</div>
<!-- !autoremove -->
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !config.Image || (!formValues.Registry && fromContainer)" ng-click="create()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Deploy the container</span>
<span ng-show="state.actionInProgress">Deployment in progress...</span>
<button type="button" class="btn btn-primary btn-sm" ng-disabled="ctrl.state.actionInProgress || !ctrl.config.Image || (!ctrl.formValues.Registry && fromContainer)" ng-click="ctrl.createContainer()" button-spinner="ctrl.state.actionInProgress">
<span ng-hide="ctrl.state.actionInProgress">Deploy the container</span>
<span ng-show="ctrl.state.actionInProgress">Deployment in progress...</span>
</button>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
<span class="text-danger" ng-if="ctrl.state.formValidationError" style="margin-left: 5px;">{{ ctrl.state.formValidationError }}</span>
</div>
</div>
<!-- !actions -->
@ -165,7 +165,7 @@
<li class="interactive"><a data-target="#env" data-toggle="tab">Env</a></li>
<li class="interactive"><a data-target="#labels" data-toggle="tab">Labels</a></li>
<li class="interactive"><a data-target="#restart-policy" data-toggle="tab">Restart policy</a></li>
<li class="interactive"><a data-target="#runtime-resources" ng-click="refreshSlider()" data-toggle="tab">Runtime & Resources</a></li>
<li class="interactive"><a data-target="#runtime-resources" ng-click="ctrl.refreshSlider()" data-toggle="tab">Runtime & Resources</a></li>
<li class="interactive"><a data-target="#container-capabilities" data-toggle="tab">Capabilities</a></li>
</ul>
<!-- tab-content -->
@ -177,7 +177,7 @@
<div class="form-group">
<label for="container_command" class="col-sm-2 col-lg-1 control-label text-left">Command</label>
<div class="col-sm-9">
<input type="text" class="form-control" ng-model="config.Cmd" id="container_command" placeholder="e.g. /usr/bin/nginx -t -c /mynginx.conf">
<input type="text" class="form-control" ng-model="ctrl.config.Cmd" id="container_command" placeholder="e.g. /usr/bin/nginx -t -c /mynginx.conf">
</div>
</div>
<!-- !command-input -->
@ -185,7 +185,7 @@
<div class="form-group">
<label for="container_entrypoint" class="col-sm-2 col-lg-1 control-label text-left">Entry Point</label>
<div class="col-sm-9">
<input type="text" class="form-control" ng-model="config.Entrypoint" id="container_entrypoint" placeholder="e.g. /bin/sh -c">
<input type="text" class="form-control" ng-model="ctrl.config.Entrypoint" id="container_entrypoint" placeholder="e.g. /bin/sh -c">
</div>
</div>
<!-- !entrypoint-input -->
@ -193,11 +193,11 @@
<div class="form-group">
<label for="container_workingdir" class="col-sm-2 col-lg-1 control-label text-left">Working Dir</label>
<div class="col-sm-4">
<input type="text" class="form-control" ng-model="config.WorkingDir" id="container_workingdir" placeholder="e.g. /myapp">
<input type="text" class="form-control" ng-model="ctrl.config.WorkingDir" id="container_workingdir" placeholder="e.g. /myapp">
</div>
<label for="container_user" class="col-sm-1 control-label text-left">User</label>
<div class="col-sm-4">
<input type="text" class="form-control" ng-model="config.User" id="container_user" placeholder="e.g. nginx">
<input type="text" class="form-control" ng-model="ctrl.config.User" id="container_user" placeholder="e.g. nginx">
</div>
</div>
<!-- !workdir-user-input -->
@ -207,13 +207,13 @@
<div class="col-sm-10 col-lg-11">
<div class="col-sm-4">
<label class="radio-inline">
<input type="radio" name="container_console" ng-model="formValues.Console" value="both">
<input type="radio" name="container_console" ng-model="ctrl.formValues.Console" value="both">
Interactive & TTY <span class="small text-muted">(-i -t)</span>
</label>
</div>
<div class="col-sm-4">
<label class="radio-inline">
<input type="radio" name="container_console" ng-model="formValues.Console" value="interactive">
<input type="radio" name="container_console" ng-model="ctrl.formValues.Console" value="interactive">
Interactive <span class="small text-muted">(-i)</span>
</label>
</div>
@ -221,13 +221,13 @@
<div class="col-sm-offset-2 col-sm-10 col-lg-offset-1 col-lg-11">
<div class="col-sm-4">
<label class="radio-inline">
<input type="radio" name="container_console" ng-model="formValues.Console" value="tty">
<input type="radio" name="container_console" ng-model="ctrl.formValues.Console" value="tty">
TTY <span class="small text-muted">(-t)</span>
</label>
</div>
<div class="col-sm-4">
<label class="radio-inline">
<input type="radio" name="container_console" ng-model="formValues.Console" value="none">
<input type="radio" name="container_console" ng-model="ctrl.formValues.Console" value="none">
None
</label>
</div>
@ -242,7 +242,7 @@
<div class="form-group">
<label for="log-driver" class="col-sm-2 col-lg-1 control-label text-left">Driver</label>
<div class="col-sm-4">
<select class="form-control" ng-model="formValues.LogDriverName" id="log-driver">
<select class="form-control" ng-model="ctrl.formValues.LogDriverName" id="log-driver">
<option selected value="">Default logging driver</option>
<option ng-repeat="driver in availableLoggingDrivers" ng-value="driver">{{ driver }}</option>
<option value="none">none</option>
@ -262,13 +262,13 @@
Options
<portainer-tooltip position="top" message="Add button is disabled unless a driver other than none or default is selected. Options are specific to the selected driver, refer to the driver documentation."></portainer-tooltip>
</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="!formValues.LogDriverName || formValues.LogDriverName === 'none' || addLogDriverOpt(formValues.LogDriverName)">
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="!ctrl.formValues.LogDriverName || ctrl.formValues.LogDriverName === 'none' || ctrl.addLogDriverOpt(ctrl.formValues.LogDriverName)">
<i class="fa fa-plus-circle" aria-hidden="true"></i> add logging driver option
</span>
</div>
<!-- logging-opts-input-list -->
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
<div ng-repeat="opt in formValues.LogDriverOpts" style="margin-top: 2px;">
<div ng-repeat="opt in ctrl.formValues.LogDriverOpts" style="margin-top: 2px;">
<div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">option</span>
<input type="text" class="form-control" ng-model="opt.name" placeholder="e.g. FOO">
@ -277,7 +277,7 @@
<span class="input-group-addon">value</span>
<input type="text" class="form-control" ng-model="opt.value" placeholder="e.g. bar">
</div>
<button class="btn btn-sm btn-danger" type="button" ng-click="removeLogDriverOpt($index)">
<button class="btn btn-sm btn-danger" type="button" ng-click="ctrl.removeLogDriverOpt($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
@ -297,13 +297,13 @@
<div class="form-group">
<div class="col-sm-12" style="margin-top: 5px;">
<label class="control-label text-left">Volume mapping</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="addVolume()">
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="ctrl.addVolume()">
<i class="fa fa-plus-circle" aria-hidden="true"></i> map additional volume
</span>
</div>
<!-- volumes-input-list -->
<div class="form-inline" style="margin-top: 10px;">
<div ng-repeat="volume in formValues.Volumes">
<div ng-repeat="volume in ctrl.formValues.Volumes">
<!-- volume-line1 -->
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
<!-- container-path -->
@ -313,12 +313,12 @@
</div>
<!-- !container-path -->
<!-- volume-type -->
<div class="input-group col-sm-5" style="margin-left: 5px;" ng-if="isAdmin || allowBindMounts">
<div class="input-group col-sm-5" style="margin-left: 5px;" ng-if="ctrl.isAdmin || ctrl.allowBindMounts">
<div class="btn-group btn-group-sm">
<label class="btn btn-primary" ng-model="volume.type" uib-btn-radio="'volume'" ng-click="volume.name = ''">Volume</label>
<label class="btn btn-primary" ng-model="volume.type" uib-btn-radio="'bind'" ng-click="volume.name = ''">Bind</label>
</div>
<button class="btn btn-sm btn-danger" type="button" ng-click="removeVolume($index)">
<button class="btn btn-sm btn-danger" type="button" ng-click="ctrl.removeVolume($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
@ -333,7 +333,7 @@
<span class="input-group-addon">volume</span>
<select class="form-control" ng-model="volume.name">
<option selected disabled hidden value="">Select a volume</option>
<option ng-repeat="vol in availableVolumes" ng-value="vol.Name">{{ vol.Name|truncate:30}} - {{ vol.Driver|truncate:30}}</option>
<option ng-repeat="vol in ctrl.availableVolumes" ng-value="vol.Id">{{ vol.Id|truncate:30}} - {{ vol.Driver|truncate:30}}</option>
</select>
</div>
<!-- !volume -->
@ -373,7 +373,7 @@
<div class="form-group">
<label for="container_network" class="col-sm-2 col-lg-1 control-label text-left">Network</label>
<div class="col-sm-9">
<select class="form-control" ng-model="config.HostConfig.NetworkMode" id="container_network" ng-change="resetNetworkConfig()">
<select class="form-control" ng-model="ctrl.config.HostConfig.NetworkMode" id="container_network" ng-change="ctrl.resetNetworkConfig()">
<option selected disabled hidden value="">Select a network</option>
<option ng-repeat="net in availableNetworks | orderBy: 'Name'" ng-value="net.Name">{{ net.Name }}</option>
</select>
@ -381,10 +381,10 @@
</div>
<!-- !network-input -->
<!-- container-name-input -->
<div class="form-group" ng-if="config.HostConfig.NetworkMode == 'container'">
<div class="form-group" ng-if="ctrl.config.HostConfig.NetworkMode == 'container'">
<label for="container_network_container" class="col-sm-2 col-lg-1 control-label text-left">Container</label>
<div class="col-sm-9">
<select ng-options="container|containername for container in runningContainers" class="form-control" ng-model="formValues.NetworkContainer">
<select ng-options="container|containername for container in runningContainers" class="form-control" ng-model="ctrl.formValues.NetworkContainer">
<option selected disabled hidden value="">Select a container</option>
</select>
</div>
@ -394,7 +394,7 @@
<div class="form-group">
<label for="container_hostname" class="col-sm-2 col-lg-1 control-label text-left">Hostname</label>
<div class="col-sm-9">
<input type="text" class="form-control" ng-model="config.Hostname" id="container_hostname" placeholder="e.g. web01">
<input type="text" class="form-control" ng-model="ctrl.config.Hostname" id="container_hostname" placeholder="e.g. web01">
</div>
</div>
<!-- !hostname-input -->
@ -402,7 +402,7 @@
<div class="form-group">
<label for="container_domainname" class="col-sm-2 col-lg-1 control-label text-left">Domain Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" ng-model="config.Domainname" id="container_domainname" placeholder="e.g. example.com">
<input type="text" class="form-control" ng-model="ctrl.config.Domainname" id="container_domainname" placeholder="e.g. example.com">
</div>
</div>
<!-- !domainname -->
@ -410,7 +410,7 @@
<div class="form-group">
<label for="container_macaddress" class="col-sm-2 col-lg-1 control-label text-left">Mac Address</label>
<div class="col-sm-9">
<input type="text" class="form-control" ng-model="formValues.MacAddress" id="container_macaddress" placeholder="e.g. 12-34-56-78-9a-bc">
<input type="text" class="form-control" ng-model="ctrl.formValues.MacAddress" id="container_macaddress" placeholder="e.g. 12-34-56-78-9a-bc">
</div>
</div>
<!-- !mac-address-input -->
@ -418,7 +418,7 @@
<div class="form-group">
<label for="container_ipv4" class="col-sm-2 col-lg-1 control-label text-left">IPv4 Address</label>
<div class="col-sm-9">
<input type="text" class="form-control" ng-model="formValues.IPv4" id="container_ipv4" placeholder="e.g. 172.20.0.7">
<input type="text" class="form-control" ng-model="ctrl.formValues.IPv4" id="container_ipv4" placeholder="e.g. 172.20.0.7">
</div>
</div>
<!-- !ipv4-input -->
@ -426,7 +426,7 @@
<div class="form-group">
<label for="container_ipv6" class="col-sm-2 col-lg-1 control-label text-left">IPv6 Address</label>
<div class="col-sm-9">
<input type="text" class="form-control" ng-model="formValues.IPv6" id="container_ipv6" placeholder="e.g. a:b:c:d::1234">
<input type="text" class="form-control" ng-model="ctrl.formValues.IPv6" id="container_ipv6" placeholder="e.g. a:b:c:d::1234">
</div>
</div>
<!-- !ipv6-input -->
@ -434,18 +434,18 @@
<div class="form-group">
<div class="col-sm-12" style="margin-top: 5px;">
<label class="control-label text-left">Hosts file entries</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="addExtraHost()">
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="ctrl.addExtraHost()">
<i class="fa fa-plus-circle" aria-hidden="true"></i> add additional entry
</span>
</div>
<!-- extra-hosts-input-list -->
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
<div ng-repeat="variable in formValues.ExtraHosts" style="margin-top: 2px;">
<div ng-repeat="variable in ctrl.formValues.ExtraHosts" style="margin-top: 2px;">
<div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">value</span>
<input type="text" class="form-control" ng-model="variable.value" placeholder="e.g. host:IP">
</div>
<button class="btn btn-sm btn-danger" type="button" ng-click="removeExtraHost($index)">
<button class="btn btn-sm btn-danger" type="button" ng-click="ctrl.removeExtraHost($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
@ -463,13 +463,13 @@
<div class="form-group">
<div class="col-sm-12" style="margin-top: 5px;">
<label class="control-label text-left">Labels</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="addLabel()">
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="ctrl.addLabel()">
<i class="fa fa-plus-circle" aria-hidden="true"></i> add label
</span>
</div>
<!-- labels-input-list -->
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
<div ng-repeat="label in formValues.Labels" style="margin-top: 2px;">
<div ng-repeat="label in ctrl.formValues.Labels" style="margin-top: 2px;">
<div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">name</span>
<input type="text" class="form-control" ng-model="label.name" placeholder="e.g. com.example.foo">
@ -478,7 +478,7 @@
<span class="input-group-addon">value</span>
<input type="text" class="form-control" ng-model="label.value" placeholder="e.g. bar">
</div>
<button class="btn btn-sm btn-danger" type="button" ng-click="removeLabel($index)">
<button class="btn btn-sm btn-danger" type="button" ng-click="ctrl.removeLabel($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
@ -496,13 +496,13 @@
<div class="form-group">
<div class="col-sm-12" style="margin-top: 5px;">
<label class="control-label text-left">Environment variables</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="addEnvironmentVariable()">
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="ctrl.addEnvironmentVariable()">
<i class="fa fa-plus-circle" aria-hidden="true"></i> add environment variable
</span>
</div>
<!-- environment-variable-input-list -->
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
<div ng-repeat="variable in config.Env" style="margin-top: 2px;">
<div ng-repeat="variable in ctrl.config.Env" style="margin-top: 2px;">
<div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">name</span>
<input type="text" class="form-control" ng-model="variable.name" placeholder="e.g. FOO">
@ -511,7 +511,7 @@
<span class="input-group-addon">value</span>
<input type="text" class="form-control" ng-model="variable.value" placeholder="e.g. bar">
</div>
<button class="btn btn-sm btn-danger" type="button" ng-click="removeEnvironmentVariable($index)">
<button class="btn btn-sm btn-danger" type="button" ng-click="ctrl.removeEnvironmentVariable($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
@ -531,16 +531,16 @@
Restart policy
</label>
<div class="btn-group btn-group-sm" style="margin-left: 20px;">
<label class="btn btn-primary" ng-model="config.HostConfig.RestartPolicy.Name" uib-btn-radio="'no'">
<label class="btn btn-primary" ng-model="ctrl.config.HostConfig.RestartPolicy.Name" uib-btn-radio="'no'">
Never
</label>
<label class="btn btn-primary" ng-model="config.HostConfig.RestartPolicy.Name" uib-btn-radio="'always'">
<label class="btn btn-primary" ng-model="ctrl.config.HostConfig.RestartPolicy.Name" uib-btn-radio="'always'">
Always
</label>
<label class="btn btn-primary" ng-model="config.HostConfig.RestartPolicy.Name" uib-btn-radio="'on-failure'">
<label class="btn btn-primary" ng-model="ctrl.config.HostConfig.RestartPolicy.Name" uib-btn-radio="'on-failure'">
On failure
</label>
<label class="btn btn-primary" ng-model="config.HostConfig.RestartPolicy.Name" uib-btn-radio="'unless-stopped'">
<label class="btn btn-primary" ng-model="ctrl.config.HostConfig.RestartPolicy.Name" uib-btn-radio="'unless-stopped'">
Unless stopped
</label>
</div>
@ -562,7 +562,7 @@
Privileged mode
</label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" name="privileged_mode" ng-model="config.HostConfig.Privileged"><i></i>
<input type="checkbox" name="privileged_mode" ng-model="ctrl.config.HostConfig.Privileged"><i></i>
</label>
</div>
</div>
@ -571,7 +571,7 @@
<div class="form-group">
<label for="container_runtime" class="col-sm-1 control-label text-left">Runtime</label>
<div class="col-sm-11">
<select class="form-control" ng-model="config.HostConfig.Runtime"
<select class="form-control" ng-model="ctrl.config.HostConfig.Runtime"
id="container_runtime" ng-options="runtime for runtime in availableRuntimes">
<option selected value="">Default</option>
</select>
@ -584,13 +584,13 @@
<div class="form-group">
<div class="col-sm-12" style="margin-top: 5px;">
<label class="control-label text-left">Devices</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="addDevice()">
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="ctrl.addDevice()">
<i class="fa fa-plus-circle" aria-hidden="true"></i> add device
</span>
</div>
<!-- devices-input-list -->
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
<div ng-repeat="device in config.HostConfig.Devices" style="margin-top: 2px;">
<div ng-repeat="device in ctrl.config.HostConfig.Devices" style="margin-top: 2px;">
<div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">host</span>
<input type="text" class="form-control" ng-model="device.pathOnHost" placeholder="e.g. /dev/tty0">
@ -599,7 +599,7 @@
<span class="input-group-addon">container</span>
<input type="text" class="form-control" ng-model="device.pathInContainer" placeholder="e.g. /dev/tty0">
</div>
<button class="btn btn-sm btn-danger" type="button" ng-click="removeDevice($index)">
<button class="btn btn-sm btn-danger" type="button" ng-click="ctrl.removeDevice($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
@ -616,10 +616,10 @@
Memory reservation
</label>
<div class="col-sm-3">
<slider model="formValues.MemoryReservation" floor="0" ceil="state.sliderMaxMemory" step="256" ng-if="state.sliderMaxMemory"></slider>
<slider model="ctrl.formValues.MemoryReservation" floor="0" ceil="ctrl.state.sliderMaxMemory" step="256" ng-if="ctrl.state.sliderMaxMemory"></slider>
</div>
<div class="col-sm-2">
<input type="number" min="0" class="form-control" ng-model="formValues.MemoryReservation" id="memory-reservation">
<input type="number" min="0" class="form-control" ng-model="ctrl.formValues.MemoryReservation" id="memory-reservation">
</div>
<div class="col-sm-4">
<p class="small text-muted" style="margin-top: 7px;">
@ -634,10 +634,10 @@
Memory limit
</label>
<div class="col-sm-3">
<slider model="formValues.MemoryLimit" floor="0" ceil="state.sliderMaxMemory" step="256" ng-if="state.sliderMaxMemory"></slider>
<slider model="ctrl.formValues.MemoryLimit" floor="0" ceil="ctrl.state.sliderMaxMemory" step="256" ng-if="ctrl.state.sliderMaxMemory"></slider>
</div>
<div class="col-sm-2">
<input type="number" min="0" class="form-control" ng-model="formValues.MemoryLimit" id="memory-limit">
<input type="number" min="0" class="form-control" ng-model="ctrl.formValues.MemoryLimit" id="memory-limit">
</div>
<div class="col-sm-4">
<p class="small text-muted" style="margin-top: 7px;">
@ -652,7 +652,7 @@
CPU limit
</label>
<div class="col-sm-5">
<slider model="formValues.CpuLimit" floor="0" ceil="state.sliderMaxCpu" step="0.25" precision="2" ng-if="state.sliderMaxCpu"></slider>
<slider model="ctrl.formValues.CpuLimit" floor="0" ceil="ctrl.state.sliderMaxCpu" step="0.25" precision="2" ng-if="ctrl.state.sliderMaxCpu"></slider>
</div>
<div class="col-sm-4" style="margin-top: 20px;">
<p class="small text-muted">
@ -666,7 +666,7 @@
<!-- !tab-runtime-resources -->
<!-- tab-container-capabilities -->
<div class="tab-pane" id="container-capabilities">
<container-capabilities capabilities="formValues.capabilities" ></container-capabilities>
<container-capabilities capabilities="ctrl.formValues.capabilities"></container-capabilities>
</div>
<!-- !tab-container-capabilities -->
</div>

View File

@ -1,861 +0,0 @@
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,
MemoryReservation: 0,
NodeName: null,
capabilities: [],
LogDriverName: '',
LogDriverOpts: []
};
$scope.extraNetworks = {};
$scope.state = {
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 = {};
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
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() {
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;
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;
});
var userDetails = Authentication.getUserDetails();
$scope.isAdmin = userDetails.role === 1;
}
function validateForm(accessControlData, isAdmin) {
$scope.state.formValidationError = '';
var error = '';
error = FormValidator.validateAccessControl(accessControlData, isAdmin);
if (error) {
$scope.state.formValidationError = error;
return false;
}
return true;
}
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;
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1;
return validateForm(accessControlData, isAdmin);
}
function onSuccess() {
Notifications.success('Container successfully created');
$state.go('docker.containers', {}, { reload: true });
}
}
initView();
}]);

View File

@ -0,0 +1,188 @@
export default function prepareConfiguration() {
let prepareNetworkConfig = (config) => {
var mode = config.HostConfig.NetworkMode;
var container = this.formValues.NetworkContainer;
var containerName = container;
if (container && typeof container === 'object') {
containerName = this.$filter('trimcontainername')(container.Names[0]);
}
var networkMode = mode;
if (containerName) {
networkMode += ':' + containerName;
config.Hostname = '';
}
config.HostConfig.NetworkMode = networkMode;
config.MacAddress = this.formValues.MacAddress;
config.NetworkingConfig.EndpointsConfig[networkMode] = {
IPAMConfig: {
IPv4Address: this.formValues.IPv4,
IPv6Address: this.formValues.IPv6
}
};
for (const v of this.formValues.ExtraHosts) {
if (v.value) {
config.HostConfig.ExtraHosts.push(v.value);
}
}
};
let prepareImageConfig = (config) => {
var image = config.Image;
var registry = this.formValues.Registry;
var imageConfig = this.ImageHelper.createImageConfigForContainer(image, registry.URL);
config.Image = imageConfig.fromImage + ':' + imageConfig.tag;
this.imageConfig = imageConfig;
};
let preparePortBindings = (config) => {
var bindings = {};
for (const portBinding of config.HostConfig.PortBindings) {
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;
};
let prepareConsole = (config) => {
var value = this.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;
};
let prepareEnvironmentVariables = (config) => {
var env = [];
for (const v of config.Env) {
if (v.name && v.value) {
env.push(v.name + '=' + v.value);
}
}
config.Env = env;
};
let prepareVolumes = (config) => {
var binds = [];
var volumes = {};
for (const volume of this.formValues.Volumes) {
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;
};
let prepareLabels = (config) => {
var labels = {};
for (const label of this.formValues.Labels) {
if (label.name && label.value) {
labels[label.name] = label.value;
}
}
config.Labels = labels;
};
let prepareDevices = (config) => {
var path = [];
for (const p of config.HostConfig.Devices) {
if (p.pathOnHost) {
if(p.pathInContainer === '') {
p.pathInContainer = p.pathOnHost;
}
path.push({PathOnHost:p.pathOnHost,PathInContainer:p.pathInContainer,CgroupPermissions:'rwm'});
}
}
config.HostConfig.Devices = path;
};
let prepareResources = (config) => {
// Memory Limit - Round to 0.125
var memoryLimit = (Math.round(this.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(this.formValues.MemoryReservation * 8) / 8).toFixed(3);
memoryReservation *= 1024 * 1024;
if (memoryReservation > 0) {
config.HostConfig.MemoryReservation = memoryReservation;
}
// CPU Limit
if (this.formValues.CpuLimit > 0) {
config.HostConfig.NanoCpus = this.formValues.CpuLimit * 1000000000;
}
};
let prepareLogDriver = (config) => {
var logOpts = {};
if (this.formValues.LogDriverName) {
config.HostConfig.LogConfig = { Type: this.formValues.LogDriverName };
if (this.formValues.LogDriverName !== 'none') {
for (const opt of this.formValues.LogDriverOpts) {
if (opt.name) {
logOpts[opt.name] = opt.value;
}
}
if (Object.keys(logOpts).length !== 0 && logOpts.constructor === Object) {
config.HostConfig.LogConfig.Config = logOpts;
}
}
}
};
let prepareCapabilities = (config) => {
var allowed = this.formValues.capabilities.filter(function(item) {return item.allowed === true;});
var notAllowed = this.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);
};
var config = angular.copy(this.config);
config.Cmd = this.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;
}