mirror of https://github.com/portainer/portainer
feat(templates): allow to edit template port mapping (#293)
* feat(templates): allow to edit template port mapping * refactor(templates): remove advanced template configuration featurepull/294/head
parent
fa53339fea
commit
d4f0145161
|
@ -81,9 +81,6 @@ angular.module('portainer', [
|
||||||
})
|
})
|
||||||
.state('actions.create.container', {
|
.state('actions.create.container', {
|
||||||
url: "/container",
|
url: "/container",
|
||||||
params: {
|
|
||||||
template: null
|
|
||||||
},
|
|
||||||
templateUrl: 'app/components/createContainer/createcontainer.html',
|
templateUrl: 'app/components/createContainer/createcontainer.html',
|
||||||
controller: 'CreateContainerController'
|
controller: 'CreateContainerController'
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,29 +1,25 @@
|
||||||
angular.module('createContainer', [])
|
angular.module('createContainer', [])
|
||||||
.controller('CreateContainerController', ['$scope', '$state', '$stateParams', 'Config', 'Info', 'Container', 'Image', 'Volume', 'Network', 'TemplateHelper', 'Messages',
|
.controller('CreateContainerController', ['$scope', '$state', '$stateParams', 'Config', 'Info', 'Container', 'Image', 'Volume', 'Network', 'Messages',
|
||||||
function ($scope, $state, $stateParams, Config, Info, Container, Image, Volume, Network, TemplateHelper, Messages) {
|
function ($scope, $state, $stateParams, Config, Info, Container, Image, Volume, Network, Messages) {
|
||||||
|
|
||||||
if ($stateParams.template) {
|
|
||||||
$scope.template = $stateParams.template;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.formValues = {
|
$scope.formValues = {
|
||||||
alwaysPull: true,
|
alwaysPull: true,
|
||||||
Console: 'none',
|
Console: 'none',
|
||||||
Volumes: $scope.template && $scope.template.volumes ? TemplateHelper.getVolumeBindings($scope.template.volumes) : [],
|
Volumes: [],
|
||||||
Registry: ''
|
Registry: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.imageConfig = {};
|
$scope.imageConfig = {};
|
||||||
|
|
||||||
$scope.config = {
|
$scope.config = {
|
||||||
Image: $scope.template ? $scope.template.image : '',
|
Image: '',
|
||||||
Env: $scope.template && $scope.template.env ? TemplateHelper.getEnvBindings($scope.template.env) : [],
|
Env: [],
|
||||||
ExposedPorts: {},
|
ExposedPorts: {},
|
||||||
HostConfig: {
|
HostConfig: {
|
||||||
RestartPolicy: {
|
RestartPolicy: {
|
||||||
Name: 'no'
|
Name: 'no'
|
||||||
},
|
},
|
||||||
PortBindings: $scope.template ? TemplateHelper.getPortBindings($scope.template.ports) : [],
|
PortBindings: [],
|
||||||
Binds: [],
|
Binds: [],
|
||||||
NetworkMode: 'bridge',
|
NetworkMode: 'bridge',
|
||||||
Privileged: false
|
Privileged: false
|
||||||
|
@ -237,5 +233,4 @@ function ($scope, $state, $stateParams, Config, Info, Container, Image, Volume,
|
||||||
createContainer(config);
|
createContainer(config);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
<rd-header-content>Templates</rd-header-content>
|
<rd-header-content>Templates</rd-header-content>
|
||||||
</rd-header>
|
</rd-header>
|
||||||
|
|
||||||
<div class="row" ng-if="selectedTemplate">
|
<div class="row" ng-if="state.selectedTemplate">
|
||||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||||
<rd-widget>
|
<rd-widget>
|
||||||
<rd-widget-custom-header icon="selectedTemplate.logo" title="selectedTemplate.image">
|
<rd-widget-custom-header icon="state.selectedTemplate.logo" title="state.selectedTemplate.image">
|
||||||
</rd-widget-custom-header>
|
</rd-widget-custom-header>
|
||||||
<rd-widget-body classes="padding">
|
<rd-widget-body classes="padding">
|
||||||
<form class="form-horizontal">
|
<form class="form-horizontal">
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !name-and-network-inputs -->
|
<!-- !name-and-network-inputs -->
|
||||||
<div ng-repeat="var in selectedTemplate.env" ng-if="!var.set" class="form-group">
|
<div ng-repeat="var in state.selectedTemplate.env" ng-if="!var.set" class="form-group">
|
||||||
<label for="field_{{ $index }}" class="col-sm-2 control-label text-left">{{ var.label }}</label>
|
<label for="field_{{ $index }}" class="col-sm-2 control-label text-left">{{ var.label }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select ng-if="(!swarm || swarm && swarm_mode) && var.type === 'container'" ng-options="container|containername for container in runningContainers" class="selectpicker form-control" ng-model="var.value">
|
<select ng-if="(!swarm || swarm && swarm_mode) && var.type === 'container'" ng-options="container|containername for container in runningContainers" class="selectpicker form-control" ng-model="var.value">
|
||||||
|
@ -51,10 +51,53 @@
|
||||||
<input ng-if="!var.type || !var.type === 'container'" type="text" class="form-control" ng-model="var.value" id="field_{{ $index }}">
|
<input ng-if="!var.type || !var.type === 'container'" type="text" class="form-control" ng-model="var.value" id="field_{{ $index }}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<a class="small interactive" ng-if="!state.showAdvancedOptions" ng-click="state.showAdvancedOptions = true;">
|
||||||
|
<i class="fa fa-plus text-icon" aria-hidden="true"></i> Show advanced options
|
||||||
|
</a>
|
||||||
|
<a class="small interactive" ng-if="state.showAdvancedOptions" ng-click="state.showAdvancedOptions = false;">
|
||||||
|
<i class="fa fa-minus text-icon" aria-hidden="true"></i> Hide advanced options
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" ng-if="state.showAdvancedOptions">
|
||||||
|
<label for="container_ports" class="col-sm-1 control-label text-left">Port mapping</label>
|
||||||
|
<div class="col-sm-11" style="margin-top: 5px;">
|
||||||
|
<span class="label label-default clickable" ng-click="addPortBinding()">
|
||||||
|
<i class="fa fa-plus-circle" aria-hidden="true"></i> map additional port
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- port-mapping-input-list -->
|
||||||
|
<div class="col-sm-offset-1 col-sm-11 form-inline" style="margin-top: 10px;">
|
||||||
|
<div ng-repeat="portBinding in formValues.ports" 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="portBinding.hostPort" placeholder="e.g. 80 or 1.2.3.4:80 (optional)">
|
||||||
|
</div>
|
||||||
|
<div class="input-group col-sm-5 input-group-sm">
|
||||||
|
<span class="input-group-addon">container</span>
|
||||||
|
<input type="text" class="form-control" ng-model="portBinding.containerPort" placeholder="e.g. 80">
|
||||||
|
</div>
|
||||||
|
<div class="input-group col-sm-1 input-group-sm">
|
||||||
|
<select class="selectpicker form-control" ng-model="portBinding.protocol">
|
||||||
|
<option value="tcp">tcp</option>
|
||||||
|
<option value="udp">udp</option>
|
||||||
|
</select>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="btn btn-default" type="button" ng-click="removePortBinding($index)">
|
||||||
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- !port-mapping-input-list -->
|
||||||
|
</div>
|
||||||
|
<!-- !port-mapping -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<button type="button" class="btn btn-default btn-sm" ng-disabled="!formValues.network" ng-click="createTemplate()">Create</button>
|
<button type="button" class="btn btn-default btn-sm" ng-disabled="!formValues.network" ng-click="createTemplate()">Create</button>
|
||||||
<button type="button" class="btn btn-default btn-sm" ui-sref="actions.create.container({template: selectedTemplate})">Advanced configuration...</button>
|
|
||||||
<i id="createContainerSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
|
<i id="createContainerSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -64,7 +107,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" ng-if="selectedTemplate">
|
<div class="row" ng-if="state.selectedTemplate">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,26 @@
|
||||||
angular.module('templates', [])
|
angular.module('templates', [])
|
||||||
.controller('TemplatesController', ['$scope', '$q', '$state', '$filter', 'Config', 'Info', 'Container', 'ContainerHelper', 'Image', 'Volume', 'Network', 'Templates', 'Messages',
|
.controller('TemplatesController', ['$scope', '$q', '$state', '$filter', 'Config', 'Info', 'Container', 'ContainerHelper', 'Image', 'Volume', 'Network', 'Templates', 'TemplateHelper', 'Messages',
|
||||||
function ($scope, $q, $state, $filter, Config, Info, Container, ContainerHelper, Image, Volume, Network, Templates, Messages) {
|
function ($scope, $q, $state, $filter, Config, Info, Container, ContainerHelper, Image, Volume, Network, Templates, TemplateHelper, Messages) {
|
||||||
$scope.selectedTemplate = null;
|
$scope.state = {
|
||||||
|
selectedTemplate: null,
|
||||||
|
showAdvancedOptions: false
|
||||||
|
};
|
||||||
$scope.formValues = {
|
$scope.formValues = {
|
||||||
network: "",
|
network: "",
|
||||||
name: ""
|
name: "",
|
||||||
|
ports: []
|
||||||
};
|
};
|
||||||
|
|
||||||
var selectedItem = -1;
|
var selectedItem = -1;
|
||||||
|
|
||||||
|
$scope.addPortBinding = function() {
|
||||||
|
$scope.formValues.ports.push({ hostPort: '', containerPort: '', protocol: 'tcp' });
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.removePortBinding = function(index) {
|
||||||
|
$scope.formValues.ports.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: centralize, already present in createContainerController
|
// TODO: centralize, already present in createContainerController
|
||||||
function createContainer(config) {
|
function createContainer(config) {
|
||||||
Container.create(config, function (d) {
|
Container.create(config, function (d) {
|
||||||
|
@ -74,6 +86,26 @@ function ($scope, $q, $state, $filter, Config, Info, Container, ContainerHelper,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function preparePortBindings(config, ports) {
|
||||||
|
var bindings = {};
|
||||||
|
ports.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 createConfigFromTemplate(template) {
|
function createConfigFromTemplate(template) {
|
||||||
var containerConfig = getInitialConfiguration();
|
var containerConfig = getInitialConfiguration();
|
||||||
containerConfig.Image = template.image;
|
containerConfig.Image = template.image;
|
||||||
|
@ -95,12 +127,7 @@ function ($scope, $q, $state, $filter, Config, Info, Container, ContainerHelper,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (template.ports) {
|
preparePortBindings(containerConfig, $scope.formValues.ports);
|
||||||
template.ports.forEach(function (p) {
|
|
||||||
containerConfig.ExposedPorts[p] = {};
|
|
||||||
containerConfig.HostConfig.PortBindings[p] = [{ HostPort: ""}];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return containerConfig;
|
return containerConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +155,7 @@ function ($scope, $q, $state, $filter, Config, Info, Container, ContainerHelper,
|
||||||
|
|
||||||
$scope.createTemplate = function() {
|
$scope.createTemplate = function() {
|
||||||
$('#createContainerSpinner').show();
|
$('#createContainerSpinner').show();
|
||||||
var template = $scope.selectedTemplate;
|
var template = $scope.state.selectedTemplate;
|
||||||
var containerConfig = createConfigFromTemplate(template);
|
var containerConfig = createConfigFromTemplate(template);
|
||||||
var imageConfig = {
|
var imageConfig = {
|
||||||
fromImage: template.image.split(':')[0],
|
fromImage: template.image.split(':')[0],
|
||||||
|
@ -144,11 +171,13 @@ function ($scope, $q, $state, $filter, Config, Info, Container, ContainerHelper,
|
||||||
$('#template_' + id).toggleClass("container-template--selected");
|
$('#template_' + id).toggleClass("container-template--selected");
|
||||||
if (selectedItem === id) {
|
if (selectedItem === id) {
|
||||||
selectedItem = -1;
|
selectedItem = -1;
|
||||||
$scope.selectedTemplate = null;
|
$scope.state.selectedTemplate = null;
|
||||||
} else {
|
} else {
|
||||||
$('#template_' + selectedItem).toggleClass("container-template--selected");
|
$('#template_' + selectedItem).toggleClass("container-template--selected");
|
||||||
selectedItem = id;
|
selectedItem = id;
|
||||||
$scope.selectedTemplate = $scope.templates[id];
|
var selectedTemplate = $scope.templates[id];
|
||||||
|
$scope.state.selectedTemplate = selectedTemplate;
|
||||||
|
$scope.formValues.ports = selectedTemplate.ports ? TemplateHelper.getPortBindings(selectedTemplate.ports) : [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@ angular.module('portainer.helpers', [])
|
||||||
});
|
});
|
||||||
return bindings;
|
return bindings;
|
||||||
},
|
},
|
||||||
|
//Not used atm, may prove useful later
|
||||||
getVolumeBindings: function(volumes) {
|
getVolumeBindings: function(volumes) {
|
||||||
var bindings = [];
|
var bindings = [];
|
||||||
volumes.forEach(function (volume) {
|
volumes.forEach(function (volume) {
|
||||||
|
@ -71,6 +72,7 @@ angular.module('portainer.helpers', [])
|
||||||
});
|
});
|
||||||
return bindings;
|
return bindings;
|
||||||
},
|
},
|
||||||
|
//Not used atm, may prove useful later
|
||||||
getEnvBindings: function(env) {
|
getEnvBindings: function(env) {
|
||||||
var bindings = [];
|
var bindings = [];
|
||||||
env.forEach(function (envvar) {
|
env.forEach(function (envvar) {
|
||||||
|
@ -85,5 +87,4 @@ angular.module('portainer.helpers', [])
|
||||||
return bindings;
|
return bindings;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}])
|
}]);
|
||||||
;
|
|
||||||
|
|
Loading…
Reference in New Issue