mirror of https://github.com/portainer/portainer
Implemented remaining container creation options.
parent
c8213bbf33
commit
4682ae4ca7
|
@ -12,7 +12,7 @@ angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services'
|
|||
}])
|
||||
// This is your docker url that the api will use to make requests
|
||||
// You need to set this to the api endpoint without the port i.e. http://192.168.1.9
|
||||
.constant('DOCKER_ENDPOINT', '/dockerapi')
|
||||
.constant('DOCKER_ENDPOINT', 'dockerapi')
|
||||
.constant('DOCKER_PORT', '') // Docker port, leave as an empty string if no port is requred. If you have a port, prefix it with a ':' i.e. :4243
|
||||
.constant('UI_VERSION', 'v0.6.0')
|
||||
.constant('DOCKER_API_VERSION', 'v1.16');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
angular.module('startContainer', [])
|
||||
angular.module('startContainer', ['ui.bootstrap'])
|
||||
.controller('StartContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages', 'containernameFilter',
|
||||
function($scope, $routeParams, $location, Container, Messages, containernameFilter) {
|
||||
$scope.template = 'app/components/startContainer/startcontainer.html';
|
||||
|
@ -10,81 +10,94 @@ function($scope, $routeParams, $location, Container, Messages, containernameFilt
|
|||
});
|
||||
|
||||
$scope.config = {
|
||||
name: '',
|
||||
memory: 0,
|
||||
memorySwap: 0,
|
||||
cpuShares: 1024,
|
||||
env: [],
|
||||
commands: '',
|
||||
volumesFrom: [],
|
||||
portBindings: []
|
||||
Env: [],
|
||||
Volumes: [],
|
||||
SecurityOpts: [],
|
||||
PortBindings: [],
|
||||
HostConfig: {
|
||||
Binds: [],
|
||||
Links: [],
|
||||
Dns: [],
|
||||
DnsSearch: [],
|
||||
VolumesFrom: [],
|
||||
CapAdd: [],
|
||||
CapDrop: []
|
||||
}
|
||||
};
|
||||
$scope.commandPlaceholder = '["/bin/echo", "Hello world"]';
|
||||
|
||||
function failedRequestHandler(e, Messages) {
|
||||
Messages.send({class: 'text-error', data: e.data});
|
||||
}
|
||||
|
||||
$scope.create = function() {
|
||||
var cmds = null;
|
||||
if ($scope.config.commands !== '') {
|
||||
cmds = angular.fromJson($scope.config.commands);
|
||||
function rmEmptyKeys(col) {
|
||||
for (var key in col) {
|
||||
if (col[key] === null || col[key] === undefined || col[key] === '' || $.isEmptyObject(col[key]) || col[key].length === 0) {
|
||||
delete col[key];
|
||||
}
|
||||
}
|
||||
var id = $routeParams.id;
|
||||
var ctor = Container;
|
||||
var loc = $location;
|
||||
var s = $scope;
|
||||
}
|
||||
|
||||
var volumesFrom = $scope.config.volumesFrom.map(function(volume) {
|
||||
return volume.name;
|
||||
});
|
||||
function getNames(arr) {
|
||||
return arr.map(function(item) {return item.name;});
|
||||
}
|
||||
|
||||
var env = $scope.config.env.map(function(envar) {
|
||||
return envar.name + '=' + envar.value;
|
||||
});
|
||||
$scope.create = function() {
|
||||
// Copy the config before transforming fields to the remote API format
|
||||
var config = angular.copy($scope.config);
|
||||
|
||||
var exposedPorts = {};
|
||||
var portBindings = {};
|
||||
config.Image = $routeParams.id;
|
||||
|
||||
if (config.Cmd && config.Cmd[0] === "[") {
|
||||
config.Cmd = angular.fromJson(config.Cmd);
|
||||
}
|
||||
|
||||
config.Env = config.Env.map(function(envar) {return envar.name + '=' + envar.value;});
|
||||
|
||||
config.Volumes = getNames(config.Volumes);
|
||||
config.SecurityOpts = getNames(config.SecurityOpts);
|
||||
|
||||
config.HostConfig.VolumesFrom = getNames(config.HostConfig.VolumesFrom);
|
||||
config.HostConfig.Binds = getNames(config.HostConfig.Binds);
|
||||
config.HostConfig.Links = getNames(config.HostConfig.Links);
|
||||
config.HostConfig.Dns = getNames(config.HostConfig.Dns);
|
||||
config.HostConfig.DnsSearch = getNames(config.HostConfig.DnsSearch);
|
||||
config.HostConfig.CapAdd = getNames(config.HostConfig.CapAdd);
|
||||
config.HostConfig.CapDrop = getNames(config.HostConfig.CapDrop);
|
||||
|
||||
var ExposedPorts = {};
|
||||
var PortBindings = {};
|
||||
// TODO: consider using compatibility library
|
||||
$scope.config.portBindings.forEach(function(portBinding) {
|
||||
config.PortBindings.forEach(function(portBinding) {
|
||||
var intPort = portBinding.intPort + "/tcp";
|
||||
var binding = {
|
||||
HostIp: portBinding.ip,
|
||||
HostPort: portBinding.extPort
|
||||
};
|
||||
if (portBinding.intPort) {
|
||||
exposedPorts[intPort] = {};
|
||||
if (intPort in portBindings) {
|
||||
portBindings[intPort].push(binding);
|
||||
ExposedPorts[intPort] = {};
|
||||
if (intPort in PortBindings) {
|
||||
PortBindings[intPort].push(binding);
|
||||
} else {
|
||||
portBindings[intPort] = [binding];
|
||||
PortBindings[intPort] = [binding];
|
||||
}
|
||||
} else {
|
||||
// TODO: Send warning message? Internal port need to be specified.
|
||||
}
|
||||
});
|
||||
config.ExposedPorts = ExposedPorts;
|
||||
delete config.PortBindings;
|
||||
config.HostConfig.PortBindings = PortBindings;
|
||||
|
||||
Container.create({
|
||||
Image: id,
|
||||
name: $scope.config.name,
|
||||
Memory: $scope.config.memory,
|
||||
MemorySwap: $scope.config.memorySwap,
|
||||
CpuShares: $scope.config.cpuShares,
|
||||
Cmd: cmds,
|
||||
VolumesFrom: volumesFrom,
|
||||
Env: env,
|
||||
ExposedPorts: exposedPorts,
|
||||
HostConfig: {
|
||||
PortBindings: portBindings
|
||||
}
|
||||
}, function(d) {
|
||||
// Remove empty fields from the request to avoid overriding defaults
|
||||
rmEmptyKeys(config.HostConfig);
|
||||
rmEmptyKeys(config);
|
||||
|
||||
var ctor = Container;
|
||||
var loc = $location;
|
||||
var s = $scope;
|
||||
Container.create(config, function(d) {
|
||||
if (d.Id) {
|
||||
ctor.start({
|
||||
id: d.Id,
|
||||
HostConfig: {
|
||||
PortBindings: portBindings
|
||||
}
|
||||
}, function(cd) {
|
||||
ctor.start({id: d.Id}, function(cd) {
|
||||
$('#create-modal').modal('hide');
|
||||
loc.path('/containers/' + d.Id + '/');
|
||||
}, function(e) {
|
||||
|
@ -99,29 +112,39 @@ function($scope, $routeParams, $location, Container, Messages, containernameFilt
|
|||
};
|
||||
|
||||
$scope.addPortBinding = function() {
|
||||
$scope.config.portBindings.push({ip: '', extPort: '', intPort: ''});
|
||||
$scope.config.PortBindings.push({ip: '', extPort: '', intPort: ''});
|
||||
};
|
||||
|
||||
$scope.removePortBinding = function(portBinding) {
|
||||
var idx = $scope.config.portBindings.indexOf(portBinding);
|
||||
$scope.config.portBindings.splice(idx, 1);
|
||||
var idx = $scope.config.PortBindings.indexOf(portBinding);
|
||||
$scope.config.PortBindings.splice(idx, 1);
|
||||
};
|
||||
|
||||
// TODO: refactor out
|
||||
$scope.addEnv = function() {
|
||||
$scope.config.env.push({name: '', value: ''});
|
||||
$scope.config.Env.push({name: '', value: ''});
|
||||
};
|
||||
|
||||
$scope.removeEnv = function(envar) {
|
||||
var idx = $scope.config.env.indexOf(envar);
|
||||
$scope.config.env.splice(idx, 1);
|
||||
$scope.config.Env.splice(idx, 1);
|
||||
};
|
||||
|
||||
$scope.addVolume = function() {
|
||||
$scope.config.volumesFrom.push({name: ''});
|
||||
// Todo: refactor out
|
||||
$scope.addVolumeFrom = function() {
|
||||
$scope.config.HostConfig.volumesFrom.push({name: ''});
|
||||
};
|
||||
|
||||
$scope.removeVolume = function(volume) {
|
||||
var idx = $scope.config.volumesFrom.indexOf(volume);
|
||||
$scope.config.volumesFrom.splice(idx, 1);
|
||||
$scope.removeVolumeFrom = function(volume) {
|
||||
var idx = $scope.config.HostConfig.volumesFrom.indexOf(volume);
|
||||
$scope.config.HostConfig.volumesFrom.splice(idx, 1);
|
||||
};
|
||||
|
||||
$scope.addEntry = function(array, entry) {
|
||||
array.push(entry);
|
||||
};
|
||||
$scope.rmEntry = function(array, entry) {
|
||||
var idx = array.indexOf(entry);
|
||||
array.splice(idx, 1);
|
||||
};
|
||||
}]);
|
||||
|
|
|
@ -18,19 +18,28 @@ describe('startContainerController', function() {
|
|||
$httpBackend = _$httpBackend_;
|
||||
});
|
||||
}));
|
||||
|
||||
function expectGetContainers() {
|
||||
$httpBackend.expectGET('dockerapi/containers/json?all=1').respond([{
|
||||
"Command": "./dockerui -e /docker.sock",
|
||||
"Created": 1421817232,
|
||||
"Id": "b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f",
|
||||
"Image": "dockerui:latest",
|
||||
"Names": ["/dockerui"],
|
||||
"Ports": [{
|
||||
"IP": "0.0.0.0",
|
||||
"PrivatePort": 9000,
|
||||
"PublicPort": 9000,
|
||||
"Type": "tcp"
|
||||
}],
|
||||
"Status": "Up 2 minutes"
|
||||
}]);
|
||||
}
|
||||
describe('Create and start a container with port bindings', function() {
|
||||
it('should issue a correct create request to the Docker remote API', function() {
|
||||
var controller = createController();
|
||||
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
|
||||
var expectedBody = {
|
||||
"name": "container-name",
|
||||
"Memory": 0,
|
||||
"MemorySwap": 0,
|
||||
"CpuShares": 1024,
|
||||
"Cmd": null,
|
||||
"VolumesFrom": [],
|
||||
"Env": [],
|
||||
"ExposedPorts": {
|
||||
"9000/tcp": {},
|
||||
},
|
||||
|
@ -44,32 +53,19 @@ describe('startContainerController', function() {
|
|||
}
|
||||
};
|
||||
|
||||
$httpBackend.expectGET('/dockerapi/containers/json?all=1').respond([{
|
||||
"Command": "./dockerui -e /docker.sock",
|
||||
"Created": 1421817232,
|
||||
"Id": "b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f",
|
||||
"Image": "dockerui:latest",
|
||||
"Names": ["/dockerui"],
|
||||
"Ports": [{
|
||||
"IP": "0.0.0.0",
|
||||
"PrivatePort": 9000,
|
||||
"PublicPort": 9000,
|
||||
"Type": "tcp"
|
||||
}],
|
||||
"Status": "Up 2 minutes"
|
||||
}]);
|
||||
expectGetContainers();
|
||||
|
||||
$httpBackend.expectPOST('/dockerapi/containers/create?name=container-name', expectedBody).respond({
|
||||
$httpBackend.expectPOST('dockerapi/containers/create?name=container-name', expectedBody).respond({
|
||||
"Id": id,
|
||||
"Warnings": null
|
||||
});
|
||||
$httpBackend.expectPOST('/dockerapi/containers/' + id + '/start?').respond({
|
||||
$httpBackend.expectPOST('dockerapi/containers/' + id + '/start?').respond({
|
||||
"Id": id,
|
||||
"Warnings": null
|
||||
});
|
||||
|
||||
scope.config.name = 'container-name';
|
||||
scope.config.portBindings = [{
|
||||
scope.config.PortBindings = [{
|
||||
ip: '10.20.10.15',
|
||||
extPort: '9999',
|
||||
intPort: '9000'
|
||||
|
@ -86,44 +82,22 @@ describe('startContainerController', function() {
|
|||
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
|
||||
var expectedBody = {
|
||||
"name": "container-name",
|
||||
"Memory": 0,
|
||||
"MemorySwap": 0,
|
||||
"CpuShares": 1024,
|
||||
"Cmd": null,
|
||||
"VolumesFrom": [],
|
||||
"Env": ["SHELL=/bin/bash", "TERM=xterm-256color"],
|
||||
"ExposedPorts": {},
|
||||
"HostConfig": {
|
||||
"PortBindings": {}
|
||||
}
|
||||
"Env": ["SHELL=/bin/bash", "TERM=xterm-256color"]
|
||||
};
|
||||
|
||||
$httpBackend.expectGET('/dockerapi/containers/json?all=1').respond([{
|
||||
"Command": "./dockerui -e /docker.sock",
|
||||
"Created": 1421817232,
|
||||
"Id": "b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f",
|
||||
"Image": "dockerui:latest",
|
||||
"Names": ["/dockerui"],
|
||||
"Ports": [{
|
||||
"IP": "0.0.0.0",
|
||||
"PrivatePort": 9000,
|
||||
"PublicPort": 9000,
|
||||
"Type": "tcp"
|
||||
}],
|
||||
"Status": "Up 2 minutes"
|
||||
}]);
|
||||
expectGetContainers();
|
||||
|
||||
$httpBackend.expectPOST('/dockerapi/containers/create?name=container-name', expectedBody).respond({
|
||||
$httpBackend.expectPOST('dockerapi/containers/create?name=container-name', expectedBody).respond({
|
||||
"Id": id,
|
||||
"Warnings": null
|
||||
});
|
||||
$httpBackend.expectPOST('/dockerapi/containers/' + id + '/start?').respond({
|
||||
$httpBackend.expectPOST('dockerapi/containers/' + id + '/start?').respond({
|
||||
"Id": id,
|
||||
"Warnings": null
|
||||
});
|
||||
|
||||
scope.config.name = 'container-name';
|
||||
scope.config.env = [{
|
||||
scope.config.Env = [{
|
||||
name: 'SHELL',
|
||||
value: '/bin/bash'
|
||||
}, {
|
||||
|
@ -141,45 +115,26 @@ describe('startContainerController', function() {
|
|||
var controller = createController();
|
||||
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
|
||||
var expectedBody = {
|
||||
"name": "container-name",
|
||||
"Memory": 0,
|
||||
"MemorySwap": 0,
|
||||
"CpuShares": 1024,
|
||||
"Cmd": null,
|
||||
"VolumesFrom": ["parent", "other:ro"],
|
||||
"Env": [],
|
||||
"ExposedPorts": {},
|
||||
"HostConfig": {
|
||||
"PortBindings": {}
|
||||
}
|
||||
HostConfig: {
|
||||
"VolumesFrom": ["parent", "other:ro"]
|
||||
},
|
||||
"name": "container-name"
|
||||
};
|
||||
|
||||
$httpBackend.expectGET('/dockerapi/containers/json?all=1').respond([{
|
||||
"Command": "./dockerui -e /docker.sock",
|
||||
"Created": 1421817232,
|
||||
"Id": "b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f",
|
||||
"Image": "dockerui:latest",
|
||||
"Names": ["/dockerui"],
|
||||
"Ports": [{
|
||||
"IP": "0.0.0.0",
|
||||
"PrivatePort": 9000,
|
||||
"PublicPort": 9000,
|
||||
"Type": "tcp"
|
||||
}],
|
||||
"Status": "Up 2 minutes"
|
||||
}]);
|
||||
expectGetContainers();
|
||||
|
||||
$httpBackend.expectPOST('/dockerapi/containers/create?name=container-name', expectedBody).respond({
|
||||
|
||||
$httpBackend.expectPOST('dockerapi/containers/create?name=container-name', expectedBody).respond({
|
||||
"Id": id,
|
||||
"Warnings": null
|
||||
});
|
||||
$httpBackend.expectPOST('/dockerapi/containers/' + id + '/start?').respond({
|
||||
$httpBackend.expectPOST('dockerapi/containers/' + id + '/start?').respond({
|
||||
"Id": id,
|
||||
"Warnings": null
|
||||
});
|
||||
|
||||
scope.config.name = 'container-name';
|
||||
scope.config.volumesFrom = [{name: "parent"}, {name:"other:ro"}];
|
||||
scope.config.HostConfig.VolumesFrom = [{name: "parent"}, {name:"other:ro"}];
|
||||
|
||||
scope.create();
|
||||
$httpBackend.flush();
|
||||
|
|
|
@ -6,40 +6,106 @@
|
|||
<h3>Create And Start Container From Image</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form role="form">
|
||||
<form role="form">
|
||||
<accordion close-others="true">
|
||||
<accordion-group heading="Container options">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label>Cmd:</label>
|
||||
<input type="text" placeholder="{{ commandPlaceholder }}" ng-model="config.commands" class="form-control"/>
|
||||
<small>Input commands as an array</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" ng-model="config.name" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Memory:</label>
|
||||
<input type="number" ng-model="config.memory" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Memory Swap:</label>
|
||||
<input type="number" ng-model="config.memorySwap" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>CPU Shares:</label>
|
||||
<input type="number" ng-model="config.cpuShares" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Mount Volumes From other containers:</label>
|
||||
<div ng-repeat="volume in config.volumesFrom" class="form-inline">
|
||||
<select ng-model="volume.name" ng-options="name for name in containerNames track by name"/>
|
||||
<button class="btn btn-danger btn-xs form-control" ng-click="removeVolume($index)">Remove</button>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label>Cmd:</label>
|
||||
<input type="text" placeholder='["/bin/echo", "Hello world"]' ng-model="config.Cmd" class="form-control"/>
|
||||
<small>Input commands as a raw string or JSON array</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Entrypoint:</label>
|
||||
<input type="text" ng-model="config.Entrypoint" class="form-control" placeholder="./entrypoint.sh"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" ng-model="config.name" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Hostname:</label>
|
||||
<input type="text" ng-model="config.Hostname" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Domainname:</label>
|
||||
<input type="text" ng-model="config.Domainname" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>User:</label>
|
||||
<input type="text" ng-model="config.User" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Memory:</label>
|
||||
<input type="number" ng-model="config.Memory" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Volumes:</label>
|
||||
<div ng-repeat="volume in config.Volumes">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="volume.name" class="form-control" placeholder="/var/data"/>
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.Volumes, volume)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.Volumes, {name: ''})">Add Volume</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success" ng-click="addVolume()">Add volume</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Environment Variables:</label>
|
||||
<div ng-repeat="envar in config.env" class="form-inline">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label>Memory Swap:</label>
|
||||
<input type="number" ng-model="config.MemorySwap" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>CPU Shares:</label>
|
||||
<input type="number" ng-model="config.CpuShares" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Cpuset:</label>
|
||||
<input type="text" ng-model="config.Cpuset" class="form-control" placeholder="1,2"/>
|
||||
<small>Input as comma-separated list of numbers</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>WorkingDir:</label>
|
||||
<input type="text" ng-model="config.WorkingDir" class="form-control" placeholder="/app"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>MacAddress:</label>
|
||||
<input type="text" ng-model="config.MacAddress" class="form-control" placeholder="12:34:56:78:9a:bc"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="networkDisabled">NetworkDisabled:</label>
|
||||
<input id="networkDisabled" type="checkbox" ng-model="config.NetworkDisabled"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="tty">Tty:</label>
|
||||
<input id="tty" type="checkbox" ng-model="config.Tty"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="openStdin">OpenStdin:</label>
|
||||
<input id="openStdin" type="checkbox" ng-model="config.OpenStdin"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="stdinOnce">StdinOnce:</label>
|
||||
<input id="stdinOnce" type="checkbox" ng-model="config.StdinOnce"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Security Options:</label>
|
||||
<div ng-repeat="opt in config.SecurityOpts">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="opt.name" class="form-control" placeholder="???"/>
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.SecurityOpts, opt)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.SecurityOpts, {name: ''})">Add Option</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Environment Variables:</label>
|
||||
<div ng-repeat="envar in config.Env">
|
||||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<label class="sr-only">Variable Name:</label>
|
||||
<input type="text" ng-model="envar.name" class="form-control" placeholder="NAME"/>
|
||||
|
@ -52,34 +118,134 @@
|
|||
<button class="btn btn-danger btn-xs form-control" ng-click="removeEnv(portBinding)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success" ng-click="addEnv()">Add ENV variable</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Port bindings:</label>
|
||||
<div ng-repeat="portBinding in config.portBindings" class="form-inline">
|
||||
<div class="form-group">
|
||||
<label class="sr-only">Host IP:</label>
|
||||
<input type="text" ng-model="portBinding.ip" class="form-control" placeholder="Host IP Address"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="sr-only">Host Port:</label>
|
||||
<input type="text" ng-model="portBinding.extPort" class="form-control" placeholder="Host Port"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="sr-only">Container port:</label>
|
||||
<input type="text" ng-model="portBinding.intPort" class="form-control" placeholder="Container Port"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-danger btn-xs form-control" ng-click="removePortBinding(portBinding)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success" ng-click="addPortBinding()">Add Port Binding</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" ng-click="addEnv()">Add ENV variable</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</accordion-group>
|
||||
<accordion-group heading="HostConfig options">
|
||||
<fieldset>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label>Binds:</label>
|
||||
<div ng-repeat="bind in config.HostConfig.Binds">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="bind.name" class="form-control" placeholder="???"/>
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.Binds, bind)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.Binds, {name: ''})">Add Bind</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Links:</label>
|
||||
<div ng-repeat="link in config.HostConfig.Links">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="link.name" class="form-control" placeholder="web:db">
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.Links, link)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.Links, {name: ''})">Add Link</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>DNS:</label>
|
||||
<div ng-repeat="entry in config.HostConfig.Dns">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="entry.name" class="form-control" placeholder="8.8.8.8"/>
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.Dns, entry)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.Dns, {name: ''})">Add</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>DnsSearch:</label>
|
||||
<div ng-repeat="entry in config.HostConfig.DnsSearch">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="entry.name" class="form-control" placeholder="???"/>
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.DnsSearch, entry)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.DnsSearch, {name: ''})">Add</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>CapAdd:</label>
|
||||
<div ng-repeat="entry in config.HostConfig.CapAdd">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="entry.name" class="form-control" placeholder="???"/>
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.CapAdd, entry)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.CapAdd, {name: ''})">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label>CapDrop:</label>
|
||||
<div ng-repeat="entry in config.HostConfig.CapDrop">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="entry.name" class="form-control" placeholder="???"/>
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.CapDrop, entry)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.CapDrop, {name: ''})">Add</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>NetworkMode:</label>
|
||||
<input type="text" ng-model="config.HostConfig.NetworkMode" class="form-control" placeholder="bridge"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="publishAllPorts">PublishAllPorts:</label>
|
||||
<input id="publishAllPorts" type="checkbox" ng-model="config.HostConfig.PublishAllPorts"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="privileged">Privileged:</label>
|
||||
<input id="privileged" type="checkbox" ng-model="config.HostConfig.Privileged"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Mount Volumes From other containers:</label>
|
||||
<div ng-repeat="volume in config.HostConfig.VolumesFrom">
|
||||
<div class="form-inline">
|
||||
<select ng-model="volume.name" ng-options="name for name in containerNames track by name" class="form-control"/>
|
||||
<button class="btn btn-danger btn-xs form-control" ng-click="removeVolumeFrom($index)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" ng-click="addVolumeFrom()">Add volume</button>
|
||||
</div>
|
||||
<!--
|
||||
<div class="form-group">
|
||||
RestartPolicy unimplemented...
|
||||
</div>
|
||||
<div class="form-group">
|
||||
Devices unimplemented...
|
||||
</div>
|
||||
<div class="form-group">
|
||||
LxcConf unimplemented...
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Port bindings:</label>
|
||||
<div ng-repeat="portBinding in config.PortBindings">
|
||||
<div class="form-group form-inline">
|
||||
<label class="sr-only">Host IP:</label>
|
||||
<input type="text" ng-model="portBinding.ip" class="form-control" placeholder="Host IP Address"/>
|
||||
<label class="sr-only">Host Port:</label>
|
||||
<input type="text" ng-model="portBinding.extPort" class="form-control" placeholder="Host Port"/>
|
||||
<label class="sr-only">Container port:</label>
|
||||
<input type="text" ng-model="portBinding.intPort" class="form-control" placeholder="Container Port"/>
|
||||
<button class="btn btn-danger btn-xs form-control" ng-click="removePortBinding(portBinding)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm" ng-click="addPortBinding()">Add Port Binding</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</accordion-group>
|
||||
</accordion>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="" class="btn btn-primary" ng-click="create()">Create</a>
|
||||
<a href="" class="btn btn-primary btn-lg" ng-click="create()">Create</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* angular-ui-bootstrap
|
||||
* http://angular-ui.github.io/bootstrap/
|
||||
|
||||
* Version: 0.12.0 - 2014-11-16
|
||||
* License: MIT
|
||||
*/
|
||||
angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.accordion","ui.bootstrap.collapse","ui.bootstrap.transition"]),angular.module("ui.bootstrap.tpls",["template/accordion/accordion-group.html","template/accordion/accordion.html"]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("accordionConfig",{closeOthers:!0}).controller("AccordionController",["$scope","$attrs","accordionConfig",function(n,o,i){this.groups=[],this.closeOthers=function(t){var e=angular.isDefined(o.closeOthers)?n.$eval(o.closeOthers):i.closeOthers;e&&angular.forEach(this.groups,function(n){n!==t&&(n.isOpen=!1)})},this.addGroup=function(n){var o=this;this.groups.push(n),n.$on("$destroy",function(){o.removeGroup(n)})},this.removeGroup=function(n){var o=this.groups.indexOf(n);-1!==o&&this.groups.splice(o,1)}}]).directive("accordion",function(){return{restrict:"EA",controller:"AccordionController",transclude:!0,replace:!1,templateUrl:"template/accordion/accordion.html"}}).directive("accordionGroup",function(){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/accordion/accordion-group.html",scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(n){this.heading=n}},link:function(n,o,i,t){t.addGroup(n),n.$watch("isOpen",function(o){o&&t.closeOthers(n)}),n.toggleOpen=function(){n.isDisabled||(n.isOpen=!n.isOpen)}}}}).directive("accordionHeading",function(){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",link:function(n,o,i,t,e){t.setHeading(e(n,function(){}))}}}).directive("accordionTransclude",function(){return{require:"^accordionGroup",link:function(n,o,i,t){n.$watch(function(){return t[i.accordionTransclude]},function(n){n&&(o.html(""),o.append(n))})}}}),angular.module("ui.bootstrap.collapse",["ui.bootstrap.transition"]).directive("collapse",["$transition",function(n){return{link:function(o,i,t){function e(o){function t(){l===e&&(l=void 0)}var e=n(i,o);return l&&l.cancel(),l=e,e.then(t,t),e}function a(){u?(u=!1,r()):(i.removeClass("collapse").addClass("collapsing"),e({height:i[0].scrollHeight+"px"}).then(r))}function r(){i.removeClass("collapsing"),i.addClass("collapse in"),i.css({height:"auto"})}function c(){if(u)u=!1,s(),i.css({height:0});else{i.css({height:i[0].scrollHeight+"px"});{i[0].offsetWidth}i.removeClass("collapse in").addClass("collapsing"),e({height:0}).then(s)}}function s(){i.removeClass("collapsing"),i.addClass("collapse")}var l,u=!0;o.$watch(t.collapse,function(n){n?c():a()})}}}]),angular.module("ui.bootstrap.transition",[]).factory("$transition",["$q","$timeout","$rootScope",function(n,o,i){function t(n){for(var o in n)if(void 0!==a.style[o])return n[o]}var e=function(t,a,r){r=r||{};var c=n.defer(),s=e[r.animation?"animationEndEventName":"transitionEndEventName"],l=function(){i.$apply(function(){t.unbind(s,l),c.resolve(t)})};return s&&t.bind(s,l),o(function(){angular.isString(a)?t.addClass(a):angular.isFunction(a)?a(t):angular.isObject(a)&&t.css(a),s||c.resolve(t)}),c.promise.cancel=function(){s&&t.unbind(s,l),c.reject("Transition cancelled")},c.promise},a=document.createElement("trans"),r={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"},c={WebkitTransition:"webkitAnimationEnd",MozTransition:"animationend",OTransition:"oAnimationEnd",transition:"animationend"};return e.transitionEndEventName=t(r),e.animationEndEventName=t(c),e}]),angular.module("template/accordion/accordion-group.html",[]).run(["$templateCache",function(n){n.put("template/accordion/accordion-group.html",'<div class="panel panel-default">\n <div class="panel-heading">\n <h4 class="panel-title">\n <a href class="accordion-toggle" ng-click="toggleOpen()" accordion-transclude="heading"><span ng-class="{\'text-muted\': isDisabled}">{{heading}}</span></a>\n </h4>\n </div>\n <div class="panel-collapse" collapse="!isOpen">\n <div class="panel-body" ng-transclude></div>\n </div>\n</div>\n')}]),angular.module("template/accordion/accordion.html",[]).run(["$templateCache",function(n){n.put("template/accordion/accordion.html",'<div class="panel-group" ng-transclude></div>')}]);
|
|
@ -86,7 +86,8 @@ module.exports = function (grunt) {
|
|||
angular: {
|
||||
src:['assets/js/angularjs/1.2.6/angular.min.js',
|
||||
'assets/js/angularjs/1.2.6/angular-route.min.js',
|
||||
'assets/js/angularjs/1.2.6/angular-resource.min.js'],
|
||||
'assets/js/angularjs/1.2.6/angular-resource.min.js',
|
||||
'assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js'],
|
||||
dest: '<%= distdir %>/angular.js'
|
||||
}
|
||||
},
|
||||
|
|
|
@ -23,11 +23,7 @@
|
|||
|
||||
|
||||
<script src="assets/js/spin.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="assets/js/angularjs/1.2.6/angular.min.js"></script>
|
||||
<script src="assets/js/angularjs/1.2.6/angular-resource.min.js"></script>
|
||||
<script src="assets/js/angularjs/1.2.6/angular-route.min.js"></script>
|
||||
|
||||
<script src="angular.js"></script>
|
||||
<script src="assets/js/jquery.gritter.min.js"></script>
|
||||
<script src="assets/js/Chart.min.js"></script>
|
||||
<script src="assets/js/legend.js"></script>
|
||||
|
|
|
@ -10,6 +10,7 @@ files = [
|
|||
'assets/js/angularjs/1.2.6/angular.min.js',
|
||||
'assets/js/angularjs/1.2.6/angular-route.min.js',
|
||||
'assets/js/angularjs/1.2.6/angular-resource.min.js',
|
||||
'assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js',
|
||||
'test/assets/angular/angular-mocks.js',
|
||||
'app/**/*.js',
|
||||
'test/unit/**/*.spec.js',
|
||||
|
|
Loading…
Reference in New Issue