Merge branch 'feat-1807-service-network-management' of github.com:portainer/portainer into develop

feat-1807-service-network-management
itsconquest 2020-02-06 19:08:56 +13:00
commit e4254cb634
3 changed files with 77 additions and 14 deletions

View File

@ -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,

View File

@ -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>

View File

@ -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) {