mirror of
https://github.com/portainer/portainer.git
synced 2025-08-02 20:35:25 +02:00
feat(secrets): add secret management (#894)
This commit is contained in:
parent
128601bb58
commit
42d28db47a
21 changed files with 730 additions and 27 deletions
60
app/components/service/includes/secrets.html
Normal file
60
app/components/service/includes/secrets.html
Normal file
|
@ -0,0 +1,60 @@
|
|||
<div ng-if="applicationState.endpoint.apiVersion >= 1.25">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-tasks" title="Secrets">
|
||||
</rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="form-inline" style="padding: 10px;">
|
||||
Add a secret:
|
||||
<select class="form-control" ng-options="secret.Name for secret in secrets" ng-model="newSecret">
|
||||
<option selected disabled hidden value="">Select a secret</option>
|
||||
</select>
|
||||
<a class="btn btn-default btn-sm" ng-click="addSecret(service, newSecret)">
|
||||
<i class="fa fa-plus-circle" aria-hidden="true"></i> add secret
|
||||
</a>
|
||||
</div>
|
||||
<table class="table" style="margin-top: 5px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>File name</th>
|
||||
<th>UID</th>
|
||||
<th>GID</th>
|
||||
<th>Mode</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="secret in service.ServiceSecrets">
|
||||
<td><a ui-sref="secret({id: secret.Id})">{{ secret.Name }}</a></td>
|
||||
<td>{{ secret.FileName }}</td>
|
||||
<td>{{ secret.Uid }}</td>
|
||||
<td>{{ secret.Gid }}</td>
|
||||
<td>{{ secret.Mode }}</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-danger pull-right" type="button" ng-click="removeSecret(service, $index)" ng-disabled="isUpdating">
|
||||
<i class="fa fa-trash" aria-hidden="true"></i> Remove secret
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="service.ServiceSecrets.length === 0">
|
||||
<td colspan="6" class="text-center text-muted">No secrets associated to this service.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
<rd-widget-footer>
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!hasChanges(service, ['ServiceSecrets'])" 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, ['ServiceSecrets'])">Reset changes</a></li>
|
||||
<li><a ng-click="cancelChanges(service)">Reset all changes</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-footer>
|
||||
</rd-widget>
|
||||
</div>
|
|
@ -109,6 +109,7 @@
|
|||
<li><a href ng-click="goToItem('service-restart-policy')">Restart policy</a></li>
|
||||
<li><a href ng-click="goToItem('service-update-config')">Update configuration</a></li>
|
||||
<li><a href ng-click="goToItem('service-labels')">Service labels</a></li>
|
||||
<li><a href ng-click="goToItem('service-secrets')">Secrets</a></li>
|
||||
<li><a href ng-click="goToItem('service-tasks')">Tasks</a></li>
|
||||
<ul>
|
||||
</rd-widget-body>
|
||||
|
@ -147,6 +148,7 @@
|
|||
<div id="service-restart-policy" class="padding-top" ng-include="'app/components/service/includes/restart.html'"></div>
|
||||
<div id="service-update-config" class="padding-top" ng-include="'app/components/service/includes/updateconfig.html'"></div>
|
||||
<div id="service-labels" class="padding-top" ng-include="'app/components/service/includes/servicelabels.html'"></div>
|
||||
<div id="service-secrets" class="padding-top" ng-include="'app/components/service/includes/secrets.html'"></div>
|
||||
<div id="service-tasks" class="padding-top" ng-include="'app/components/service/includes/tasks.html'"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('service', [])
|
||||
.controller('ServiceController', ['$q', '$scope', '$stateParams', '$state', '$location', '$anchorScroll', 'ServiceService', 'Service', 'ServiceHelper', 'TaskService', 'NodeService', 'Notifications', 'Pagination', 'ModalService', 'ControllerDataPipeline',
|
||||
function ($q, $scope, $stateParams, $state, $location, $anchorScroll, ServiceService, Service, ServiceHelper, TaskService, NodeService, Notifications, Pagination, ModalService, ControllerDataPipeline) {
|
||||
.controller('ServiceController', ['$q', '$scope', '$stateParams', '$state', '$location', '$anchorScroll', 'ServiceService', 'Secret', 'SecretHelper', 'Service', 'ServiceHelper', 'TaskService', 'NodeService', 'Notifications', 'Pagination', 'ModalService', 'ControllerDataPipeline',
|
||||
function ($q, $scope, $stateParams, $state, $location, $anchorScroll, ServiceService, Secret, SecretHelper, Service, ServiceHelper, TaskService, NodeService, Notifications, Pagination, ModalService, ControllerDataPipeline) {
|
||||
|
||||
$scope.state = {};
|
||||
$scope.state.pagination_count = Pagination.getPaginationCount('service_tasks');
|
||||
|
@ -55,6 +55,18 @@ function ($q, $scope, $stateParams, $state, $location, $anchorScroll, ServiceSer
|
|||
updateServiceArray(service, 'EnvironmentVariables', service.EnvironmentVariables);
|
||||
}
|
||||
};
|
||||
$scope.addSecret = function addSecret(service, secret) {
|
||||
if (secret && service.ServiceSecrets.filter(function(serviceSecret) { return serviceSecret.Id === secret.Id;}).length === 0) {
|
||||
service.ServiceSecrets.push({ Id: secret.Id, Name: secret.Name, FileName: secret.Name, Uid: '0', Gid: '0', Mode: 444 });
|
||||
updateServiceArray(service, 'ServiceSecrets', service.ServiceSecrets);
|
||||
}
|
||||
};
|
||||
$scope.removeSecret = function removeSecret(service, index) {
|
||||
var removedElement = service.ServiceSecrets.splice(index, 1);
|
||||
if (removedElement !== null) {
|
||||
updateServiceArray(service, 'ServiceSecrets', service.ServiceSecrets);
|
||||
}
|
||||
};
|
||||
$scope.addLabel = function addLabel(service) {
|
||||
service.ServiceLabels.push({ key: '', value: '', originalValue: '' });
|
||||
updateServiceArray(service, 'ServiceLabels', service.ServiceLabels);
|
||||
|
@ -162,7 +174,7 @@ function ($q, $scope, $stateParams, $state, $location, $anchorScroll, ServiceSer
|
|||
config.TaskTemplate.ContainerSpec.Env = translateEnvironmentVariablesToEnv(service.EnvironmentVariables);
|
||||
config.TaskTemplate.ContainerSpec.Labels = translateServiceLabelsToLabels(service.ServiceContainerLabels);
|
||||
config.TaskTemplate.ContainerSpec.Image = service.Image;
|
||||
config.TaskTemplate.ContainerSpec.Secrets = service.ServiceSecrets;
|
||||
config.TaskTemplate.ContainerSpec.Secrets = service.ServiceSecrets ? service.ServiceSecrets.map(SecretHelper.secretConfig) : [];
|
||||
|
||||
if (service.Mode === 'replicated') {
|
||||
config.Mode.Replicated.Replicas = service.Replicas;
|
||||
|
@ -246,7 +258,7 @@ function ($q, $scope, $stateParams, $state, $location, $anchorScroll, ServiceSer
|
|||
}
|
||||
|
||||
function translateServiceArrays(service) {
|
||||
service.ServiceSecrets = service.Secrets;
|
||||
service.ServiceSecrets = service.Secrets ? service.Secrets.map(SecretHelper.flattenSecret) : [];
|
||||
service.EnvironmentVariables = translateEnvironmentVariables(service.Env);
|
||||
service.ServiceLabels = translateLabelsToServiceLabels(service.Labels);
|
||||
service.ServiceContainerLabels = translateLabelsToServiceLabels(service.ContainerLabels);
|
||||
|
@ -272,14 +284,19 @@ function ($q, $scope, $stateParams, $state, $location, $anchorScroll, ServiceSer
|
|||
|
||||
return $q.all({
|
||||
tasks: TaskService.serviceTasks(service.Name),
|
||||
nodes: NodeService.nodes()
|
||||
nodes: NodeService.nodes(),
|
||||
secrets: Secret.query({}).$promise
|
||||
});
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.tasks = data.tasks;
|
||||
$scope.nodes = data.nodes;
|
||||
$scope.secrets = data.secrets.map(function (secret) {
|
||||
return new SecretViewModel(secret);
|
||||
});
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$scope.secrets = [];
|
||||
Notifications.error('Failure', err, 'Unable to retrieve service details');
|
||||
})
|
||||
.finally(function final() {
|
||||
|
@ -287,6 +304,20 @@ function ($q, $scope, $stateParams, $state, $location, $anchorScroll, ServiceSer
|
|||
});
|
||||
}
|
||||
|
||||
function fetchSecrets() {
|
||||
$('#loadSecretsSpinner').show();
|
||||
Secret.query({}, function (d) {
|
||||
$scope.secrets = d.map(function (secret) {
|
||||
return new SecretViewModel(secret);
|
||||
});
|
||||
$('#loadSecretsSpinner').hide();
|
||||
}, function(e) {
|
||||
$('#loadSecretsSpinner').hide();
|
||||
Notifications.error('Failure', e, 'Unable to retrieve secrets');
|
||||
$scope.secrets = [];
|
||||
});
|
||||
}
|
||||
|
||||
$scope.updateServiceAttribute = function updateServiceAttribute(service, name) {
|
||||
if (service[name] !== originalService[name] || !(name in originalService)) {
|
||||
service.hasChanges = true;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue