mirror of https://github.com/portainer/portainer
feat(service-creation): add placement constraints (#837)
parent
7c6c9284f2
commit
43e1f25f89
|
@ -1,8 +1,8 @@
|
||||||
// @@OLD_SERVICE_CONTROLLER: this service should be rewritten to use services.
|
// @@OLD_SERVICE_CONTROLLER: this service should be rewritten to use services.
|
||||||
// See app/components/templates/templatesController.js as a reference.
|
// See app/components/templates/templatesController.js as a reference.
|
||||||
angular.module('createService', [])
|
angular.module('createService', [])
|
||||||
.controller('CreateServiceController', ['$scope', '$state', 'Service', 'Volume', 'Network', 'ImageHelper', 'Authentication', 'ResourceControlService', 'Notifications',
|
.controller('CreateServiceController', ['$scope', '$state', 'Service', 'ServiceHelper', 'Volume', 'Network', 'ImageHelper', 'Authentication', 'ResourceControlService', 'Notifications',
|
||||||
function ($scope, $state, Service, Volume, Network, ImageHelper, Authentication, ResourceControlService, Notifications) {
|
function ($scope, $state, Service, ServiceHelper, Volume, Network, ImageHelper, Authentication, ResourceControlService, Notifications) {
|
||||||
|
|
||||||
$scope.formValues = {
|
$scope.formValues = {
|
||||||
Ownership: $scope.applicationState.application.authentication ? 'private' : '',
|
Ownership: $scope.applicationState.application.authentication ? 'private' : '',
|
||||||
|
@ -23,6 +23,7 @@ function ($scope, $state, Service, Volume, Network, ImageHelper, Authentication,
|
||||||
ExtraNetworks: [],
|
ExtraNetworks: [],
|
||||||
Ports: [],
|
Ports: [],
|
||||||
Parallelism: 1,
|
Parallelism: 1,
|
||||||
|
PlacementConstraints: [],
|
||||||
UpdateDelay: 0,
|
UpdateDelay: 0,
|
||||||
FailureAction: 'pause'
|
FailureAction: 'pause'
|
||||||
};
|
};
|
||||||
|
@ -58,7 +59,18 @@ function ($scope, $state, Service, Volume, Network, ImageHelper, Authentication,
|
||||||
$scope.removeEnvironmentVariable = function(index) {
|
$scope.removeEnvironmentVariable = function(index) {
|
||||||
$scope.formValues.Env.splice(index, 1);
|
$scope.formValues.Env.splice(index, 1);
|
||||||
};
|
};
|
||||||
|
$scope.addPlacementConstraint = function() {
|
||||||
|
$scope.formValues.PlacementConstraints.push({ key: '', operator: '==', value: '' });
|
||||||
|
};
|
||||||
|
$scope.removePlacementConstraint = function(index) {
|
||||||
|
$scope.formValues.PlacementConstraints.splice(index, 1);
|
||||||
|
};
|
||||||
|
$scope.addPlacementPreference = function() {
|
||||||
|
$scope.formValues.PlacementPreferences.push({ key: '', operator: '==', value: '' });
|
||||||
|
};
|
||||||
|
$scope.removePlacementPreference = function(index) {
|
||||||
|
$scope.formValues.PlacementPreferences.splice(index, 1);
|
||||||
|
};
|
||||||
$scope.addLabel = function() {
|
$scope.addLabel = function() {
|
||||||
$scope.formValues.Labels.push({ name: '', value: ''});
|
$scope.formValues.Labels.push({ name: '', value: ''});
|
||||||
};
|
};
|
||||||
|
@ -188,6 +200,9 @@ function ($scope, $state, Service, Volume, Network, ImageHelper, Authentication,
|
||||||
FailureAction: input.FailureAction
|
FailureAction: input.FailureAction
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
function preparePlacementConfig(config, input) {
|
||||||
|
config.TaskTemplate.Placement.Constraints = ServiceHelper.translateKeyValueToPlacementConstraints(input.PlacementConstraints);
|
||||||
|
}
|
||||||
|
|
||||||
function prepareConfiguration() {
|
function prepareConfiguration() {
|
||||||
var input = $scope.formValues;
|
var input = $scope.formValues;
|
||||||
|
@ -196,7 +211,8 @@ function ($scope, $state, Service, Volume, Network, ImageHelper, Authentication,
|
||||||
TaskTemplate: {
|
TaskTemplate: {
|
||||||
ContainerSpec: {
|
ContainerSpec: {
|
||||||
Mounts: []
|
Mounts: []
|
||||||
}
|
},
|
||||||
|
Placement: {}
|
||||||
},
|
},
|
||||||
Mode: {},
|
Mode: {},
|
||||||
EndpointSpec: {}
|
EndpointSpec: {}
|
||||||
|
@ -210,6 +226,7 @@ function ($scope, $state, Service, Volume, Network, ImageHelper, Authentication,
|
||||||
prepareVolumes(config, input);
|
prepareVolumes(config, input);
|
||||||
prepareNetworks(config, input);
|
prepareNetworks(config, input);
|
||||||
prepareUpdateConfig(config, input);
|
prepareUpdateConfig(config, input);
|
||||||
|
preparePlacementConfig(config, input);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,7 @@
|
||||||
<li class="interactive"><a data-target="#network" data-toggle="tab">Network</a></li>
|
<li class="interactive"><a data-target="#network" data-toggle="tab">Network</a></li>
|
||||||
<li class="interactive"><a data-target="#labels" data-toggle="tab">Labels</a></li>
|
<li class="interactive"><a data-target="#labels" data-toggle="tab">Labels</a></li>
|
||||||
<li class="interactive"><a data-target="#update-config" data-toggle="tab">Update config</a></li>
|
<li class="interactive"><a data-target="#update-config" data-toggle="tab">Update config</a></li>
|
||||||
|
<li class="interactive"><a data-target="#placement" data-toggle="tab">Placement</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<!-- tab-content -->
|
<!-- tab-content -->
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
@ -436,6 +437,10 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<!-- !tab-update-config -->
|
<!-- !tab-update-config -->
|
||||||
|
|
||||||
|
<!-- tab-placement -->
|
||||||
|
<div class="tab-pane" id="placement" ng-include="'app/components/createService/includes/placement.html'"></div>
|
||||||
|
<!-- !tab-placement -->
|
||||||
</div>
|
</div>
|
||||||
</rd-widget-body>
|
</rd-widget-body>
|
||||||
</rd-widget>
|
</rd-widget>
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<form class="form-horizontal" style="margin-top: 15px;">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-12" style="margin-top: 5px;">
|
||||||
|
<label class="control-label text-left">Placement constraints</label>
|
||||||
|
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="addPlacementConstraint(service)">
|
||||||
|
<i class="fa fa-plus-circle" aria-hidden="true"></i> placement constraint
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
|
||||||
|
<div ng-repeat="constraint in formValues.PlacementConstraints" style="margin-top: 2px;">
|
||||||
|
<div class="input-group col-sm-4 input-group-sm">
|
||||||
|
<span class="input-group-addon">name</span>
|
||||||
|
<input type="text" class="form-control" ng-model="constraint.key" placeholder="e.g. node.role">
|
||||||
|
</div>
|
||||||
|
<div class="input-group col-sm-1 input-group-sm">
|
||||||
|
<select name="constraintOperator" class="form-control" ng-model="constraint.operator">
|
||||||
|
<option value="==">==</option>
|
||||||
|
<option value="!=">!=</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<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="constraint.value" placeholder="e.g. manager">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-sm btn-danger" type="button" ng-click="removePlacementConstraint($index)">
|
||||||
|
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -173,7 +173,7 @@ function ($scope, $stateParams, $state, $location, $anchorScroll, Service, Servi
|
||||||
if (typeof config.TaskTemplate.Placement === 'undefined') {
|
if (typeof config.TaskTemplate.Placement === 'undefined') {
|
||||||
config.TaskTemplate.Placement = {};
|
config.TaskTemplate.Placement = {};
|
||||||
}
|
}
|
||||||
config.TaskTemplate.Placement.Constraints = translateKeyValueToConstraints(service.ServiceConstraints);
|
config.TaskTemplate.Placement.Constraints = ServiceHelper.translateKeyValueToPlacementConstraints(service.ServiceConstraints);
|
||||||
|
|
||||||
config.TaskTemplate.Resources = {
|
config.TaskTemplate.Resources = {
|
||||||
Limits: {
|
Limits: {
|
||||||
|
@ -382,18 +382,5 @@ function ($scope, $stateParams, $state, $location, $anchorScroll, Service, Servi
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateKeyValueToConstraints(keyValueConstraints) {
|
|
||||||
if (keyValueConstraints) {
|
|
||||||
var constraints = [];
|
|
||||||
keyValueConstraints.forEach(function(keyValueConstraint) {
|
|
||||||
if (keyValueConstraint.key && keyValueConstraint.key !== '' && keyValueConstraint.value && keyValueConstraint.value !== '') {
|
|
||||||
constraints.push(keyValueConstraint.key + keyValueConstraint.operator + keyValueConstraint.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return constraints;
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchServiceDetails();
|
fetchServiceDetails();
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -12,6 +12,18 @@ angular.module('portainer.helpers')
|
||||||
Networks: service.Spec.Networks,
|
Networks: service.Spec.Networks,
|
||||||
EndpointSpec: service.Spec.EndpointSpec
|
EndpointSpec: service.Spec.EndpointSpec
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
translateKeyValueToPlacementConstraints: function(keyValueConstraints) {
|
||||||
|
if (keyValueConstraints) {
|
||||||
|
var constraints = [];
|
||||||
|
keyValueConstraints.forEach(function(keyValueConstraint) {
|
||||||
|
if (keyValueConstraint.key && keyValueConstraint.key !== '' && keyValueConstraint.value && keyValueConstraint.value !== '') {
|
||||||
|
constraints.push(keyValueConstraint.key + keyValueConstraint.operator + keyValueConstraint.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return constraints;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
Loading…
Reference in New Issue