Skip to content

Commit

Permalink
Add the ability to add a secret to an application
Browse files Browse the repository at this point in the history
  • Loading branch information
jeff-phillips-18 committed Sep 6, 2017
1 parent 73c7420 commit fd6cb24
Show file tree
Hide file tree
Showing 9 changed files with 630 additions and 92 deletions.
1 change: 1 addition & 0 deletions app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ <h1>JavaScript Required</h1>
<script src="scripts/directives/labels.js"></script>
<script src="scripts/directives/lifecycleHook.js"></script>
<script src="scripts/directives/actionChip.js"></script>
<script src="scripts/directives/addSecretToApplication.js"></script>
<script src="scripts/directives/templateopt.js"></script>
<script src="scripts/directives/tasks.js"></script>
<script src="scripts/directives/catalog.js"></script>
Expand Down
10 changes: 10 additions & 0 deletions app/scripts/controllers/secret.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ angular.module('openshiftConsole')
}
];

$scope.addToApplicationVisible = false;

$scope.addToApplication = function() {
$scope.addToApplicationVisible = true;
};

$scope.closeAddToApplication = function() {
$scope.addToApplicationVisible = false;
};

ProjectsService
.get($routeParams.project)
.then(_.spread(function(project, context) {
Expand Down
171 changes: 171 additions & 0 deletions app/scripts/directives/addSecretToApplication.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
"use strict";
(function() {
angular.module("openshiftConsole").component('addSecretToApplication', {
controller: [
'$filter',
'$scope',
'APIService',
'DataService',
'NotificationsService',
'StorageService',
AddSecretToApplication
],
controllerAs: 'ctrl',
bindings: {
project: '<',
secret: '<',
onComplete: '<',
onCancel: '<'
},
templateUrl: 'views/directives/add-secret-to-application.html'
});

function AddSecretToApplication($filter, $scope, APIService, DataService, NotificationsService, StorageService) {
var ctrl = this;
var deploymentConfigs;
var deployments;
var replicationControllers;
var replicaSets;
var statefulSets;

var sortApplications = function() {
// Don't waste time sorting on each data load, just sort when we have them all
if (deploymentConfigs && deployments && replicationControllers && replicaSets && statefulSets) {
var apiObjects = deploymentConfigs.concat(deployments)
.concat(replicationControllers)
.concat(replicaSets)
.concat(statefulSets);
ctrl.applications = _.sortBy(apiObjects, ['metadata.name', 'kind']);
ctrl.updating = false;
}
};

var getApplications = function() {
var hasDeploymentFilter = $filter('hasDeployment');
var hasDeploymentConfigFilter = $filter('hasDeploymentConfig');

ctrl.updating = true;
var context = {
namespace: ctrl.project.metadata.name
};
// Load all the "application" types
DataService.list('deploymentconfigs', context).then(function(deploymentConfigData) {
deploymentConfigs = _.toArray(deploymentConfigData.by('metadata.name'));
sortApplications();
});
DataService.list('replicationcontrollers', context).then(function(replicationControllerData) {
replicationControllers = _.reject(replicationControllerData.by('metadata.name'), hasDeploymentConfigFilter);
sortApplications();
});
DataService.list({
group: 'apps',
resource: 'deployments'
}, context).then(function(deploymentData) {
deployments = _.toArray(deploymentData.by('metadata.name'));
sortApplications();
});
DataService.list({
group: 'extensions',
resource: 'replicasets'
}, context).then(function(replicaSetData) {
replicaSets = _.reject(replicaSetData.by('metadata.name'), hasDeploymentFilter);
sortApplications();
});
DataService.list({
group: 'apps',
resource: 'statefulsets'
}, context).then(function(statefulSetData) {
statefulSets = _.toArray(statefulSetData.by('metadata.name'));
sortApplications();
});
};

ctrl.$onInit = function() {
ctrl.addType = 'env';
ctrl.disableInputs = false;
getApplications();
};

ctrl.$postLink = function() {
$scope.$watch(function() {
return ctrl.application;
}, function() {
// Look at the existing mount paths so that we can warn if the new value is not unique.
var podTemplate = _.get(ctrl.application, 'spec.template');
ctrl.existingMountPaths = StorageService.getMountPaths(podTemplate);
});
};

ctrl.addToApplication = function() {
var podTemplate = _.get(ctrl.application, 'spec.template');

ctrl.disableInputs = true;

if (ctrl.addType === 'env') {
var newEnvFrom = {
secretRef: ctrl.secret.metadata
};

// For each container, add the new volume mount.
_.each(podTemplate.spec.containers, function(container) {
container.envFrom = container.envFrom || [];
container.envFrom.push(newEnvFrom);
});
} else {
var generateName = $filter('generateName');
var name = generateName('volume-');
var newVolumeMount = {
name: name,
mountPath: ctrl.mountVolume,
readOnly: true
};

// For each selected container, add the new volume mount.
_.each(podTemplate.spec.containers, function(container) {
container.volumeMounts = container.volumeMounts || [];
container.volumeMounts.push(newVolumeMount);
});

var newVolume = {
name: name,
secret: {
secretName: ctrl.secret.metadata.name,
}
};

podTemplate.spec.volumes = podTemplate.spec.volumes || [];
podTemplate.spec.volumes.push(newVolume);
}

var humanizeKind = $filter('humanizeKind');
var sourceKind = humanizeKind(ctrl.secret.kind);
var targetKind = humanizeKind(ctrl.application.kind);
var context = {
namespace: ctrl.project.metadata.name
};

DataService.update(APIService.kindToResource(ctrl.application.kind), ctrl.application.metadata.name, ctrl.application, context).then(
function() {
NotificationsService.addNotification({
type: "success",
message: "Successfully added " + sourceKind + " " + ctrl.secret.metadata.name + " to " + targetKind + " " + ctrl.application.metadata.name + "."
});
if (angular.isFunction(ctrl.onComplete)) {
ctrl.onComplete();
}
},
function(result) {
var getErrorDetails = $filter('getErrorDetails');

NotificationsService.addNotification({
type: "error",
message: "An error occurred adding " + sourceKind + " " + ctrl.secret.metadata.name + " to " + targetKind + " " + ctrl.application.metadata.name + ". " +
getErrorDetails(result)
});
}).finally(function() {
ctrl.disableInputs = false;
}
);
};
}
})();
57 changes: 56 additions & 1 deletion app/styles/_secrets.less
Original file line number Diff line number Diff line change
@@ -1,3 +1,58 @@
.add-secret-to-application {
.catalogs-overlay-panel {
max-width: 600px;
}

.dialog-title {
border-bottom: 1px solid @color-pf-black-300;

h3 {
margin: 18px 0;
padding-left: 15px;
}
}

.dialog-body {
padding: 20px;

.add-choice {
margin-bottom: 10px;
}
.button-group {
.btn {
margin-left: 10px;
&:first-of-type {
margin-left: 0;
}
}
}
legend {
border-bottom: 0;
font-size: @font-size-base + 1;
font-weight: 600;
margin-bottom: 10px;
}
.radio {
margin-top: 0;
}
}

.updating {
background-color: @color-pf-white;
bottom: 55px;
left: 0;
padding-top: 60px;
position: absolute;
right: 0;
top: 55px;
z-index: 1000;
}

.volume-options {
margin-left: 20px;
}
}

.osc-secrets-form {
.advanced-secrets,
.basic-secrets {
Expand Down Expand Up @@ -80,7 +135,7 @@ dl.secret-data {
}

.create-secret-modal {
background-color: #F5F5F5;
background-color: @color-pf-black-150;
.modal-footer{
margin-top: 0px
}
Expand Down
15 changes: 13 additions & 2 deletions app/views/browse/secret.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@ <h2>The secret details could not be loaded.</h2>
</div>
<div ng-if="loaded && !error">
<h1 class="contains-actions">
<div class="pull-right dropdown" ng-hide="!('secrets' | canIDoAny)">
<button type="button" class="dropdown-toggle btn btn-default actions-dropdown-btn hidden-xs" data-toggle="dropdown">
<div class="pull-right dropdown">
<button type="button" class="btn btn-default hidden-xs" ng-click="addToApplication()">
Add to Application
</button>
<button type="button" class="dropdown-toggle btn btn-default actions-dropdown-btn hidden-xs" data-toggle="dropdown" ng-hide="!('secrets' | canIDoAny)">
Actions
<span class="caret"></span>
</button>
<a href=""
class="dropdown-toggle actions-dropdown-kebab visible-xs-inline"
data-toggle="dropdown"><i class="fa fa-ellipsis-v"></i><span class="sr-only">Actions</span></a>
<ul class="dropdown-menu dropdown-menu-right actions action-button">
<li class="visible-xs">
<a href="" role="button" ng-click="addToApplication()">Add to Application</a>
</li>
<li ng-if="'secrets' | canI : 'update'">
<a ng-href="{{secret | editYamlURL}}" role="button">Edit YAML</a>
</li>
Expand All @@ -32,6 +38,8 @@ <h1 class="contains-actions">
</li>
</ul>
</div>
<div class="pull-right" ng-hide="!('secrets' | canIDoAny)">
</div>
{{secret.metadata.name}}
<small class="meta">created <span am-time-ago="secret.metadata.creationTimestamp"></span></small>
</h1>
Expand Down Expand Up @@ -79,5 +87,8 @@ <h2 class="mar-top-none">
</div><!-- /col-* -->
</div>
</div>
<overlay-panel class="add-secret-to-application" show-panel="addToApplicationVisible" show-close="true" handle-close="closeAddToApplication">
<add-secret-to-application project="project" secret="secret" on-cancel="closeAddToApplication" on-complete="closeAddToApplication"></add-secret-to-application>
</overlay-panel>
</div><!-- /middle-content -->
</div>
92 changes: 92 additions & 0 deletions app/views/directives/add-secret-to-application.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<div>
<div class="dialog-title">
<h3>Add to Application</h3>
</div>
<div class="dialog-body">
<form name="addToApplicationForm" novalidate>
<fieldset ng-disabled="disableInputs">
<legend>Add this secret to application:</legend>
<div class="form-group">
<div class="application-select">
<ui-select id="application" ng-model="ctrl.application" required="true" ng-disabled="ctrl.disableInputs">
<ui-select-match placeholder="{{ctrl.applications.length ? 'Select an application' : 'There are no applications in this project'}}">
<span>
{{$select.selected.metadata.name}}
<small class="text-muted">&ndash; {{$select.selected.kind | humanizeKind : true}}</small>
</span>
</ui-select-match>
<ui-select-choices
repeat="application in (ctrl.applications) | filter : { metadata: { name: $select.search } } track by (application | uid)"
group-by="ctrl.groupByKind">
<span ng-bind-html="application.metadata.name | highlight : $select.search"></span>
</ui-select-choices>
</ui-select>
</div>
</div>
<legend>Add secret as:</legend>
<div class="form-group">
<div class="radio">
<label class="add-choice" for="envFrom">
<input id="envFrom" type="radio" ng-model="ctrl.addType" value="env" ng-disabled="ctrl.disableInputs">
Environment variables
</label>
<div>
<label class="add-choice" for="mountVolume">
<input type="radio" ng-model="ctrl.addType" value="volume" ng-disabled="ctrl.disableInputs">
Volume
</label>
</div>
<div class="volume-options">
<div ng-class="{'has-error': (addToApplicationForm.mountVolume.$error.pattern && addToApplicationForm.mountVolume.$touched)}">
<input class="form-control"
name="mountVolume"
id="mountVolume"
placeholder="Enter a mount path"
type="text"
required
ng-pattern="/^\/.*$/"
osc-unique="ctrl.existingMountPaths"
aria-describedby="mount-path-help"
ng-disabled="ctrl.addType !== 'volume' || ctrl.disableInputs"
ng-model="ctrl.mountVolume"
autocorrect="off"
autocapitalize="off"
spellcheck="false">
</div>
<div class="help-block bind-description">
Mount Path for the volume. A file will be created in this director for each key from the secret. The file contents will be the value of the key.
</div>
<div class="has-error" ng-show="addToApplicationForm.mountVolume.$error.oscUnique">
<span class="help-block">
The mount path is already used. Please choose another mount path.
</span>
</div>
</div>
</div>
</div>
<div class="button-group pull-right">
<button
class="btn btn-default"
ng-class="{'dialog-btn': isDialog}"
ng-click="ctrl.onCancel()">
Cancel
</button>
<button type="submit"
class="btn btn-primary"
ng-class="{'dialog-btn': isDialog}"
ng-click="ctrl.addToApplication()"
ng-disabled="ctrl.addType === 'volume' && addToApplicationForm.$invalid || !ctrl.application"
value="">
Save
</button>
</div>
</fieldset>
</form>
<div class="updating" ng-if="ctrl.updating">
<div class="spinner spinner-lg" aria-hidden="true"></div>
<h3>
<span class="sr-only">Updating</span>
</h3>
</div>
</div>
</div>
Loading

0 comments on commit fd6cb24

Please sign in to comment.