mirror of https://github.com/portainer/portainer
Merge branch 'feat-1807-service-network-management' of github.com:portainer/portainer into develop
commit
e4254cb634
|
@ -1,4 +1,5 @@
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import _ from 'lodash-es';
|
||||||
|
|
||||||
angular.module('portainer.docker')
|
angular.module('portainer.docker')
|
||||||
.factory('ServiceHelper', [function ServiceHelperFactory() {
|
.factory('ServiceHelper', [function ServiceHelperFactory() {
|
||||||
|
@ -21,6 +22,22 @@ angular.module('portainer.docker')
|
||||||
tasks = otherServicesTasks;
|
tasks = otherServicesTasks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function findAssociatedNetwork(serviceNetwork, networks) {
|
||||||
|
return _.find(networks, function(network) {
|
||||||
|
return network.Id === serviceNetwork.NetworkID;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.mapNetworkNameToServiceNetwork = function(service, networks) {
|
||||||
|
for (var i = 0; i < service.ServiceNetworks.length; i++) {
|
||||||
|
var serviceNetwork = service.ServiceNetworks[i];
|
||||||
|
var network = findAssociatedNetwork(serviceNetwork, networks);
|
||||||
|
if (network) {
|
||||||
|
serviceNetwork.Name = network.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
helper.serviceToConfig = function(service) {
|
helper.serviceToConfig = function(service) {
|
||||||
return {
|
return {
|
||||||
Name: service.Spec.Name,
|
Name: service.Spec.Name,
|
||||||
|
|
|
@ -1,26 +1,52 @@
|
||||||
<div id="service-network-specs">
|
<div id="service-network-specs">
|
||||||
<rd-widget>
|
<rd-widget>
|
||||||
<rd-widget-header icon="fa-tasks" title-text="Networks"></rd-widget-header>
|
<rd-widget-header icon="fa-tasks" title-text="Networks"></rd-widget-header>
|
||||||
<rd-widget-body ng-if="!service.VirtualIPs || service.VirtualIPs.length === 0">
|
<div class="form-inline" style="padding: 10px;" authorization="DockerServiceUpdate">
|
||||||
<p>This service is not connected to any networks.</p>
|
Add a network:
|
||||||
</rd-widget-body>
|
<select class="form-control" ng-options="network.Name for network in availableNetworks | orderBy: 'Id'" ng-model="newNetwork">
|
||||||
<rd-widget-body ng-if="service.VirtualIPs && service.VirtualIPs.length > 0" classes="no-padding">
|
<option selected disabled hidden value="">Select a network</option>
|
||||||
<table class="table" >
|
</select>
|
||||||
|
<a class="btn btn-default btn-sm" ng-click="addNetwork(service, newNetwork)">
|
||||||
|
<i class="fa fa-plus-circle" aria-hidden="true"></i> add network
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<p style="padding: 10px;" ng-if="!service.ServiceNetworks || service.ServiceNetworks.length === 0">This service is not connected to any networks.</p>
|
||||||
|
<table class="table" style="margin-top: 5px;" ng-if="service.ServiceNetworks && service.ServiceNetworks.length > 0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>Network</th>
|
||||||
<th>IP address</th>
|
<th>IP address</th>
|
||||||
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="network in service.VirtualIPs">
|
<tr ng-repeat="network in service.ServiceNetworks">
|
||||||
<td>
|
<td>
|
||||||
<a ui-sref="docker.networks.network({id: network.NetworkID})">{{ network.NetworkID }}</a>
|
<a ui-sref="docker.networks.network({id: network.NetworkID})">{{ network.Name }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ network.Addr }}</td>
|
<td>{{ network.Addr }}</td>
|
||||||
|
<td authorization="DockerServiceUpdate">
|
||||||
|
<button type="button" class="btn btn-xs btn-danger" ng-click="removeNetwork(service, $index)" ng-disabled="isUpdating">
|
||||||
|
<i class="fa fa-trash" aria-hidden="true"></i> Remove network
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</rd-widget-body>
|
</rd-widget-body>
|
||||||
|
<rd-widget-footer>
|
||||||
|
<div class="btn-toolbar" role="toolbar">
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="!hasChanges(service, ['ServiceNetworks'])" ng-click="updateService(service)">Apply changes</button>
|
||||||
|
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a ng-click="cancelChanges(service, ['ServiceNetworks'])"">Reset changes</a></li>
|
||||||
|
<li><a ng-click="cancelChanges(service)">Reset all changes</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</rd-widget-footer>
|
||||||
</rd-widget>
|
</rd-widget>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,10 +18,11 @@ require('./includes/tasks.html')
|
||||||
require('./includes/updateconfig.html')
|
require('./includes/updateconfig.html')
|
||||||
|
|
||||||
import { PorImageRegistryModel } from 'Docker/models/porImageRegistry';
|
import { PorImageRegistryModel } from 'Docker/models/porImageRegistry';
|
||||||
|
import _ from 'lodash-es';
|
||||||
|
|
||||||
angular.module('portainer.docker')
|
angular.module('portainer.docker')
|
||||||
.controller('ServiceController', ['$q', '$scope', '$transition$', '$state', '$location', '$timeout', '$anchorScroll', 'ServiceService', 'ConfigService', 'ConfigHelper', 'SecretService', 'ImageService', 'SecretHelper', 'Service', 'ServiceHelper', 'LabelHelper', 'TaskService', 'NodeService', 'ContainerService', 'TaskHelper', 'Notifications', 'ModalService', 'PluginService', 'Authentication', 'SettingsService', 'VolumeService', 'ImageHelper', 'WebhookService', 'EndpointProvider', 'clipboard','WebhookHelper',
|
.controller('ServiceController', ['$q', '$scope', '$transition$', '$state', '$location', '$timeout', '$anchorScroll', 'ServiceService', 'ConfigService', 'ConfigHelper', 'SecretService', 'ImageService', 'SecretHelper', 'Service', 'ServiceHelper', 'LabelHelper', 'TaskService', 'NodeService', 'NetworkService', 'ContainerService', 'TaskHelper', 'Notifications', 'ModalService', 'PluginService', 'Authentication', 'SettingsService', 'VolumeService', 'ImageHelper', 'WebhookService', 'EndpointProvider', 'clipboard','WebhookHelper',
|
||||||
function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll, ServiceService, ConfigService, ConfigHelper, SecretService, ImageService, SecretHelper, Service, ServiceHelper, LabelHelper, TaskService, NodeService, ContainerService, TaskHelper, Notifications, ModalService, PluginService, Authentication, SettingsService, VolumeService, ImageHelper, WebhookService, EndpointProvider, clipboard, WebhookHelper) {
|
function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll, ServiceService, ConfigService, ConfigHelper, SecretService, ImageService, SecretHelper, Service, ServiceHelper, LabelHelper, TaskService, NodeService, NetworkService, ContainerService, TaskHelper, Notifications, ModalService, PluginService, Authentication, SettingsService, VolumeService, ImageHelper, WebhookService, EndpointProvider, clipboard, WebhookHelper) {
|
||||||
|
|
||||||
$scope.state = {
|
$scope.state = {
|
||||||
updateInProgress: false,
|
updateInProgress: false,
|
||||||
|
@ -167,7 +168,19 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
|
||||||
$scope.updatePlacementPreference = function(service) {
|
$scope.updatePlacementPreference = function(service) {
|
||||||
updateServiceArray(service, 'ServicePreferences', service.ServicePreferences);
|
updateServiceArray(service, 'ServicePreferences', service.ServicePreferences);
|
||||||
};
|
};
|
||||||
|
$scope.addNetwork = function addNetwork(service, network) {
|
||||||
|
if (network && !_.find(service.ServiceNetworks, {'NetworkID': network.Id})) {
|
||||||
|
service.ServiceNetworks.push({NetworkID: network.Id})
|
||||||
|
updateServiceArray(service, 'ServiceNetworks', service.ServiceNetworks);
|
||||||
|
ServiceHelper.mapNetworkNameToServiceNetwork(service, $scope.networks);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$scope.removeNetwork = function removeNetwork(service, index) {
|
||||||
|
var removedElement = service.ServiceNetworks.splice(index, 1);
|
||||||
|
if (removedElement !== null) {
|
||||||
|
updateServiceArray(service, 'ServiceNetworks', service.ServiceNetworks);
|
||||||
|
}
|
||||||
|
};
|
||||||
$scope.addPublishedPort = function addPublishedPort(service) {
|
$scope.addPublishedPort = function addPublishedPort(service) {
|
||||||
if (!service.Ports) {
|
if (!service.Ports) {
|
||||||
service.Ports = [];
|
service.Ports = [];
|
||||||
|
@ -297,7 +310,9 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
|
||||||
} else {
|
} else {
|
||||||
config.TaskTemplate.ContainerSpec.Image = service.Image;
|
config.TaskTemplate.ContainerSpec.Image = service.Image;
|
||||||
}
|
}
|
||||||
|
if ($scope.hasChanges(service, ["ServiceNetworks"])) {
|
||||||
|
config.TaskTemplate.Networks = _.map(service.ServiceNetworks, ({NetworkID}) => ({ Target: NetworkID}));
|
||||||
|
}
|
||||||
config.TaskTemplate.ContainerSpec.Secrets = service.ServiceSecrets ? service.ServiceSecrets.map(SecretHelper.secretConfig) : [];
|
config.TaskTemplate.ContainerSpec.Secrets = service.ServiceSecrets ? service.ServiceSecrets.map(SecretHelper.secretConfig) : [];
|
||||||
config.TaskTemplate.ContainerSpec.Configs = service.ServiceConfigs ? service.ServiceConfigs.map(ConfigHelper.configConfig) : [];
|
config.TaskTemplate.ContainerSpec.Configs = service.ServiceConfigs ? service.ServiceConfigs.map(ConfigHelper.configConfig) : [];
|
||||||
config.TaskTemplate.ContainerSpec.Hosts = service.Hosts ? ServiceHelper.translateHostnameIPToHostsEntries(service.Hosts) : [];
|
config.TaskTemplate.ContainerSpec.Hosts = service.Hosts ? ServiceHelper.translateHostnameIPToHostsEntries(service.Hosts) : [];
|
||||||
|
@ -508,6 +523,7 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
|
||||||
service.ServiceMounts = angular.copy(service.Mounts);
|
service.ServiceMounts = angular.copy(service.Mounts);
|
||||||
service.ServiceConstraints = ServiceHelper.translateConstraintsToKeyValue(service.Constraints);
|
service.ServiceConstraints = ServiceHelper.translateConstraintsToKeyValue(service.Constraints);
|
||||||
service.ServicePreferences = ServiceHelper.translatePreferencesToKeyValue(service.Preferences);
|
service.ServicePreferences = ServiceHelper.translatePreferencesToKeyValue(service.Preferences);
|
||||||
|
service.ServiceNetworks = service.VirtualIPs ? angular.copy(service.VirtualIPs) : [];
|
||||||
service.Hosts = ServiceHelper.translateHostsEntriesToHostnameIP(service.Hosts);
|
service.Hosts = ServiceHelper.translateHostsEntriesToHostnameIP(service.Hosts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,6 +565,8 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
|
||||||
tasks: TaskService.tasks({ service: [service.Name] }),
|
tasks: TaskService.tasks({ service: [service.Name] }),
|
||||||
containers: agentProxy ? ContainerService.containers() : [],
|
containers: agentProxy ? ContainerService.containers() : [],
|
||||||
nodes: NodeService.nodes(),
|
nodes: NodeService.nodes(),
|
||||||
|
networks: NetworkService.networks(true, true, true),
|
||||||
|
availableNetworks: NetworkService.networks(true, true, true),
|
||||||
secrets: apiVersion >= 1.25 ? SecretService.secrets() : [],
|
secrets: apiVersion >= 1.25 ? SecretService.secrets() : [],
|
||||||
configs: apiVersion >= 1.30 ? ConfigService.configs() : [],
|
configs: apiVersion >= 1.30 ? ConfigService.configs() : [],
|
||||||
availableImages: ImageService.images(),
|
availableImages: ImageService.images(),
|
||||||
|
@ -566,7 +584,8 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
|
||||||
$scope.availableVolumes = data.volumes;
|
$scope.availableVolumes = data.volumes;
|
||||||
$scope.allowBindMounts = data.settings.AllowBindMountsForRegularUsers;
|
$scope.allowBindMounts = data.settings.AllowBindMountsForRegularUsers;
|
||||||
$scope.isAdmin = Authentication.isAdmin();
|
$scope.isAdmin = Authentication.isAdmin();
|
||||||
|
$scope.networks = data.networks;
|
||||||
|
$scope.availableNetworks = data.availableNetworks;
|
||||||
if (data.webhooks.length > 0) {
|
if (data.webhooks.length > 0) {
|
||||||
var webhook = data.webhooks[0];
|
var webhook = data.webhooks[0];
|
||||||
$scope.WebhookExists = true;
|
$scope.WebhookExists = true;
|
||||||
|
@ -588,7 +607,8 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
|
||||||
|
|
||||||
$scope.tasks = data.tasks;
|
$scope.tasks = data.tasks;
|
||||||
|
|
||||||
|
ServiceHelper.mapNetworkNameToServiceNetwork(service, data.networks);
|
||||||
|
|
||||||
// Set max cpu value
|
// Set max cpu value
|
||||||
var maxCpus = 0;
|
var maxCpus = 0;
|
||||||
for (var n in data.nodes) {
|
for (var n in data.nodes) {
|
||||||
|
|
Loading…
Reference in New Issue