mirror of
https://github.com/openfaas/faas.git
synced 2025-06-08 16:26:47 +00:00
Add additional fields to the UI
Adding additional inputs for Environment variables, Secrets, Labels, and annotations so that more advanced functions can be deployed through the UI. This also allows more advanced functions to be provided via the store. Allowing modification to secret values, and environment variables which will allow functions like a Slack Bot function to be deployed through the store adn configured with an environment variable Signed-off-by: Burton Rheutan <rheutan7@gmail.com>
This commit is contained in:
parent
490d74f885
commit
a987d2147b
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg>
|
After Width: | Height: | Size: 296 B |
1
gateway/assets/img/icons/baseline-expand_less-24px.svg
Normal file
1
gateway/assets/img/icons/baseline-expand_less-24px.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
|
After Width: | Height: | Size: 184 B |
1
gateway/assets/img/icons/baseline-expand_more-24px.svg
Normal file
1
gateway/assets/img/icons/baseline-expand_more-24px.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
|
After Width: | Height: | Size: 182 B |
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg>
|
After Width: | Height: | Size: 277 B |
221
gateway/assets/script/bootstrap.js
vendored
221
gateway/assets/script/bootstrap.js
vendored
@ -7,7 +7,7 @@ var app = angular.module('faasGateway', ['ngMaterial', 'ngMessages', 'faasGatewa
|
||||
app.controller("home", ['$scope', '$log', '$http', '$location', '$interval', '$filter', '$mdDialog', '$mdToast', '$mdSidenav',
|
||||
function($scope, $log, $http, $location, $interval, $filter, $mdDialog, $mdToast, $mdSidenav) {
|
||||
var FUNCSTORE_DEPLOY_TAB_INDEX = 0;
|
||||
var MANUAL_DEPLOY_TAB_INDEX = 1;
|
||||
var CUSTOM_DEPLOY_TAB_INDEX = 1;
|
||||
|
||||
var newFuncTabIdx = FUNCSTORE_DEPLOY_TAB_INDEX;
|
||||
$scope.functions = [];
|
||||
@ -50,7 +50,9 @@ app.controller("home", ['$scope', '$log', '$http', '$location', '$interval', '$f
|
||||
network: "",
|
||||
service: "",
|
||||
envVars: {},
|
||||
labels: {}
|
||||
labels: {},
|
||||
annotations: {},
|
||||
secrets: []
|
||||
};
|
||||
|
||||
$scope.invocation.request = "";
|
||||
@ -250,8 +252,20 @@ app.controller("home", ['$scope', '$log', '$http', '$location', '$interval', '$f
|
||||
|
||||
var DialogController = function($scope, $mdDialog, item) {
|
||||
$scope.selectedTabIdx = newFuncTabIdx;
|
||||
$scope.item = item;
|
||||
$scope.item = {};
|
||||
$scope.selectedFunc = null;
|
||||
$scope.envFieldsVisible = false;
|
||||
$scope.envVarInputs = [{key: "", value: ""}];
|
||||
|
||||
$scope.secretFieldsVisible = false;
|
||||
$scope.secretInputs = [{name: ""}];
|
||||
|
||||
$scope.labelFieldsVisible = false;
|
||||
$scope.labelInputs = [{key: "", value: ""}];
|
||||
|
||||
$scope.annotationFieldsVisible = false;
|
||||
$scope.annotationInputs = [{key: "", value: ""}];
|
||||
|
||||
$scope.closeDialog = function() {
|
||||
$mdDialog.hide();
|
||||
};
|
||||
@ -261,11 +275,17 @@ app.controller("home", ['$scope', '$log', '$http', '$location', '$interval', '$f
|
||||
$scope.item.service = func.name;
|
||||
$scope.item.envProcess = func.fprocess;
|
||||
$scope.item.network = func.network;
|
||||
$scope.item.envVars = func.environment;
|
||||
$scope.item.labels = func.labels;
|
||||
$scope.envVarsToEnvVarInputs(func.environment);
|
||||
$scope.labelsToLabelInputs(func.labels);
|
||||
$scope.annotationsToAnnotationInputs(func.annotations);
|
||||
$scope.secretsToSecretInputs(func.secrets);
|
||||
|
||||
$scope.selectedFunc = func;
|
||||
}
|
||||
|
||||
$scope.customizeStoreFunc = function() {
|
||||
$scope.selectedTabIdx = CUSTOM_DEPLOY_TAB_INDEX;
|
||||
}
|
||||
|
||||
$scope.onTabSelect = function(idx) {
|
||||
newFuncTabIdx = idx;
|
||||
@ -275,11 +295,16 @@ app.controller("home", ['$scope', '$log', '$http', '$location', '$interval', '$f
|
||||
$scope.selectedFunc = null;
|
||||
}
|
||||
|
||||
$scope.onManualTabDeselect = function() {
|
||||
$scope.onCustomTabDeselect = function() {
|
||||
$scope.item = {};
|
||||
}
|
||||
|
||||
$scope.createFunc = function() {
|
||||
$scope.item.envVars = $scope.envVarInputsToEnvVars();
|
||||
$scope.item.secrets = $scope.secretInputsToSecrets();
|
||||
$scope.item.labels = $scope.labelInputsToLabels();
|
||||
$scope.item.annotations = $scope.annotationInputsToAnnotations();
|
||||
|
||||
var options = {
|
||||
url: "../system/functions",
|
||||
data: $scope.item,
|
||||
@ -296,16 +321,198 @@ app.controller("home", ['$scope', '$log', '$http', '$location', '$interval', '$f
|
||||
item.network = "";
|
||||
item.envVars = {};
|
||||
item.labels = {};
|
||||
item.annotations = {};
|
||||
item.secrets = [];
|
||||
|
||||
$scope.validationError = "";
|
||||
$scope.closeDialog();
|
||||
showPostInvokedToast("Function created");
|
||||
}).catch(function(error1) {
|
||||
showPostInvokedToast("Error");
|
||||
$scope.selectedTabIdx = MANUAL_DEPLOY_TAB_INDEX;
|
||||
$scope.selectedTabIdx = CUSTOM_DEPLOY_TAB_INDEX;
|
||||
$scope.validationError = error1.data;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.onEnvInputExpand = function() {
|
||||
$scope.envFieldsVisible = !$scope.envFieldsVisible;
|
||||
}
|
||||
|
||||
$scope.addEnvVar = function(index) {
|
||||
var newInput = {key: "", value: ""};
|
||||
$scope.envVarInputs.splice(index + 1, 0, newInput);
|
||||
}
|
||||
|
||||
$scope.removeEnvVar = function($event, envVar) {
|
||||
var index = $scope.envVarInputs.indexOf(envVar);
|
||||
if ($event.which == 1) {
|
||||
$scope.envVarInputs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.onSecretInputExpand = function() {
|
||||
$scope.secretFieldsVisible = !$scope.secretFieldsVisible;
|
||||
}
|
||||
|
||||
$scope.addSecret = function(index) {
|
||||
var newInput = {name: ""};
|
||||
$scope.secretInputs.splice(index + 1, 0, newInput);
|
||||
}
|
||||
|
||||
$scope.removeSecret = function($event, secret) {
|
||||
var index = $scope.secretInputs.indexOf(secret);
|
||||
if ($event.which == 1) {
|
||||
$scope.secretInputs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.onLabelInputExpand = function() {
|
||||
$scope.labelFieldsVisible = !$scope.labelFieldsVisible;
|
||||
}
|
||||
|
||||
$scope.addLabel = function(index) {
|
||||
var newInput = {key: "", value: ""};
|
||||
$scope.labelInputs.splice(index + 1, 0, newInput);
|
||||
}
|
||||
|
||||
$scope.removeLabel = function($event, label) {
|
||||
var index = $scope.labelInputs.indexOf(label);
|
||||
if ($event.which == 1) {
|
||||
$scope.labelInputs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.onAnnotationInputExpand = function() {
|
||||
$scope.annotationFieldsVisible = !$scope.annotationFieldsVisible;
|
||||
}
|
||||
|
||||
$scope.addAnnotation = function(index) {
|
||||
var newInput = {key: "", value: ""};
|
||||
$scope.annotationInputs.splice(index + 1, 0, newInput);
|
||||
}
|
||||
|
||||
$scope.removeAnnotation = function($event, annotation) {
|
||||
var index = $scope.annotationInputs.indexOf(annotation);
|
||||
if ($event.which == 1) {
|
||||
$scope.annotationInputs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.envVarInputsToEnvVars = function() {
|
||||
var self = this;
|
||||
var result = {};
|
||||
for(var i = 0; i < self.envVarInputs.length; i++) {
|
||||
if (self.envVarInputs[i].key && self.envVarInputs[i].value) {
|
||||
result[self.envVarInputs[i].key] = self.envVarInputs[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
$scope.envVarsToEnvVarInputs = function(envVars) {
|
||||
var result = [];
|
||||
for (var e in envVars) {
|
||||
result.push({key: e, value: envVars[e]});
|
||||
}
|
||||
|
||||
if (result.length > 0) {
|
||||
// make the fields visible if deploying from store with values
|
||||
$scope.envFieldsVisible = true;
|
||||
} else {
|
||||
result.push({key: "", value: ""});
|
||||
$scope.envFieldsVisible = false;
|
||||
}
|
||||
|
||||
$scope.envVarInputs = result;
|
||||
}
|
||||
|
||||
$scope.secretInputsToSecrets = function() {
|
||||
var self = this;
|
||||
var result = [];
|
||||
for(var i = 0; i < self.secretInputs.length; i++) {
|
||||
if (self.secretInputs[i].name) {
|
||||
result.push(self.secretInputs[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
$scope.secretsToSecretInputs = function(secrets) {
|
||||
var result = [];
|
||||
for (var secret in secrets) {
|
||||
result.push({name: secret});
|
||||
}
|
||||
|
||||
if (result.length > 0) {
|
||||
// make the fields visible if deploying from store with values
|
||||
$scope.secretFieldsVisible = true;
|
||||
} else {
|
||||
result.push({name: ""});
|
||||
$scope.secretFieldsVisible = false;
|
||||
}
|
||||
|
||||
$scope.secretInputs = result;
|
||||
}
|
||||
|
||||
$scope.labelInputsToLabels = function() {
|
||||
var self = this;
|
||||
var result = {};
|
||||
for(var i = 0; i < self.labelInputs.length; i++) {
|
||||
if (self.labelInputs[i].key && self.labelInputs[i].value) {
|
||||
result[self.labelInputs[i].key] = self.labelInputs[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
$scope.labelsToLabelInputs = function(labels) {
|
||||
var result = [];
|
||||
for (var l in labels) {
|
||||
result.push({key: l, value: labels[l]});
|
||||
}
|
||||
|
||||
if (result.length > 0) {
|
||||
// make the fields visible if deploying from store with values
|
||||
$scope.labelFieldsVisible = true;
|
||||
} else {
|
||||
result.push({key: "", value: ""});
|
||||
$scope.labelFieldsVisible = false;
|
||||
}
|
||||
|
||||
$scope.labelInputs = result;
|
||||
}
|
||||
|
||||
$scope.annotationInputsToAnnotations = function() {
|
||||
var self = this;
|
||||
var result = {};
|
||||
for(var i = 0; i < self.annotationInputs.length; i++) {
|
||||
if (self.annotationInputs[i].key && self.annotationInputs[i].value) {
|
||||
result[self.annotationInputs[i].key] = self.annotationInputs[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
$scope.annotationsToAnnotationInputs = function(annotations) {
|
||||
var result = [];
|
||||
for (var a in annotations) {
|
||||
result.push({key: a, value: annotations[a]});
|
||||
}
|
||||
|
||||
if (result.length > 0) {
|
||||
// make the fields visible if deploying from store with values
|
||||
$scope.annotationFieldsVisible = true;
|
||||
} else {
|
||||
result.push({key: "", value: ""});
|
||||
$scope.annotationFieldsVisible = false;
|
||||
}
|
||||
|
||||
$scope.annotationInputs = result;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.newFunction = function() {
|
||||
|
19
gateway/assets/style/bootstrap.css
vendored
19
gateway/assets/style/bootstrap.css
vendored
@ -109,4 +109,23 @@ md-icon.link {
|
||||
md-input-container.function-url md-icon {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.repeating-inputs {
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
margin: 0;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.add-input-icon {
|
||||
padding-top: 25px;
|
||||
color: green;
|
||||
}
|
||||
|
||||
.remove-input-icon {
|
||||
padding-top: 25px;
|
||||
color: red;
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
<func-store selected-func="selectedFunc" on-selected="onFuncSelected"></func-store>
|
||||
</md-content>
|
||||
</md-tab>
|
||||
<md-tab label="Manually" md-on-deselect="onManualTabDeselect()" md-on-select="onTabSelect(1)">
|
||||
<md-tab label="Custom" md-on-deselect="onCustomTabDeselect()" md-on-select="onTabSelect(1)">
|
||||
<md-content class="md-padding">
|
||||
<div>
|
||||
<label><i>Use this form to test a function or the <a ng-href="https://github.com/openfaas/faas-cli">faas-cli</a> for more options.</i></label>
|
||||
@ -58,6 +58,97 @@
|
||||
<input name="network" ng-model="item.network" md-maxlength="200" minlength="0">
|
||||
</md-input-container>
|
||||
</div>
|
||||
<br />
|
||||
<div layout-gt-xs="column">
|
||||
<div layout-gt-xs="row" ng-click="onEnvInputExpand()" stop-propagation>
|
||||
Environment Variables
|
||||
<md-icon class="expand-icon" ng-if="!envFieldsVisible" aria-label="env-expand" md-svg-src="img/icons/baseline-expand_more-24px.svg"></md-icon>
|
||||
<md-icon class="expand-icon" ng-if="envFieldsVisible" aria-label="env-expand" md-svg-src="img/icons/baseline-expand_less-24px.svg"></md-icon>
|
||||
</div>
|
||||
<div ng-if="envFieldsVisible" layout-gt-xs="column" class="repeating-inputs">
|
||||
<div ng-repeat="envVar in envVarInputs track by $index" layout-gt-xs="row">
|
||||
<md-input-container class="md-block" flex-gt-sm>
|
||||
<md-tooltip md-direction="bottom">Environment variable key to be used in the function</md-tooltip>
|
||||
<label>Key:</label>
|
||||
<input name="envVarKey" ng-model="envVarInputs[$index].key" autofocus>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block" flex-gt-sm>
|
||||
<md-tooltip md-direction="bottom">Environment variable value to be used in the function</md-tooltip>
|
||||
<label>Value:</label>
|
||||
<input name="envVarValue" ng-model="envVarInputs[$index].value">
|
||||
</md-input-container>
|
||||
<md-icon class="add-input-icon" ng-click="addEnvVar($index)" ng-show="$last" aria-label="add-env" md-svg-src="img/icons/baseline-add_circle_outline-24px.svg"></md-icon>
|
||||
<md-icon class="remove-input-icon" ng-click="removeEnvVar($event,envVar)" ng-show="!$last" aria-label="remove-env" md-svg-src="img/icons/baseline-remove_circle_outline-24px.svg"></md-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div layout-gt-xs="column">
|
||||
<div layout-gt-xs="row" ng-click="onSecretInputExpand()" stop-propagation>
|
||||
Secrets
|
||||
<md-icon class="expand-icon" ng-if="!secretFieldsVisible" aria-label="secret-expand" md-svg-src="img/icons/baseline-expand_more-24px.svg"></md-icon>
|
||||
<md-icon class="expand-icon" ng-if="secretFieldsVisible" aria-label="secret-expand" md-svg-src="img/icons/baseline-expand_less-24px.svg"></md-icon>
|
||||
</div>
|
||||
<div ng-if="secretFieldsVisible" layout-gt-xs="column" class="repeating-inputs">
|
||||
<div ng-repeat="secret in secretInputs track by $index" layout-gt-xs="row">
|
||||
<md-input-container class="md-block" flex-gt-sm>
|
||||
<md-tooltip md-direction="bottom">Secret name to be used in the function</md-tooltip>
|
||||
<label>Secret Name:</label>
|
||||
<input name="secretField" ng-model="secretInputs[$index].name" autofocus>
|
||||
</md-input-container>
|
||||
<md-icon class="add-input-icon" ng-click="addSecret($index)" ng-show="$last" aria-label="add-secret" md-svg-src="img/icons/baseline-add_circle_outline-24px.svg"></md-icon>
|
||||
<md-icon class="remove-input-icon" ng-click="removeSecret($event,secret)" ng-show="!$last" aria-label="remove-secret" md-svg-src="img/icons/baseline-remove_circle_outline-24px.svg"></md-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div layout-gt-xs="column">
|
||||
<div layout-gt-xs="row" ng-click="onLabelInputExpand()" stop-propagation>
|
||||
Labels
|
||||
<md-icon class="expand-icon" ng-if="!labelFieldsVisible" aria-label="label-expand" md-svg-src="img/icons/baseline-expand_more-24px.svg"></md-icon>
|
||||
<md-icon class="expand-icon" ng-if="labelFieldsVisible" aria-label="label-expand" md-svg-src="img/icons/baseline-expand_less-24px.svg"></md-icon>
|
||||
</div>
|
||||
<div ng-if="labelFieldsVisible" layout-gt-xs="column" class="repeating-inputs">
|
||||
<div ng-repeat="label in labelInputs track by $index" layout-gt-xs="row">
|
||||
<md-input-container class="md-block" flex-gt-sm>
|
||||
<md-tooltip md-direction="bottom">Label key to be used on the deployed function</md-tooltip>
|
||||
<label>Key:</label>
|
||||
<input name="labelKey" ng-model="labelInputs[$index].key" autofocus>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block" flex-gt-sm>
|
||||
<md-tooltip md-direction="bottom">Label value to be used on the deployed function</md-tooltip>
|
||||
<label>Value:</label>
|
||||
<input name="labelValue" ng-model="labelInputs[$index].value">
|
||||
</md-input-container>
|
||||
<md-icon class="add-input-icon" ng-click="addLabel($index)" ng-show="$last" aria-label="add-label" md-svg-src="img/icons/baseline-add_circle_outline-24px.svg"></md-icon>
|
||||
<md-icon class="remove-input-icon" ng-click="removeLabel($event,label)" ng-show="!$last" aria-label="remove-label" md-svg-src="img/icons/baseline-remove_circle_outline-24px.svg"></md-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div layout-gt-xs="column">
|
||||
<div layout-gt-xs="row" ng-click="onAnnotationInputExpand()" stop-propagation>
|
||||
Annotations
|
||||
<md-icon class="expand-icon" ng-if="!annotationFieldsVisible" aria-label="annotation-expand" md-svg-src="img/icons/baseline-expand_more-24px.svg"></md-icon>
|
||||
<md-icon class="expand-icon" ng-if="annotationFieldsVisible" aria-label="annotation-expand" md-svg-src="img/icons/baseline-expand_less-24px.svg"></md-icon>
|
||||
</div>
|
||||
<div ng-if="annotationFieldsVisible" layout-gt-xs="column" class="repeating-inputs">
|
||||
<div ng-repeat="annotation in annotationInputs track by $index" layout-gt-xs="row">
|
||||
<md-input-container class="md-block" flex-gt-sm>
|
||||
<md-tooltip md-direction="bottom">Annotation key to be used on the deployed function</md-tooltip>
|
||||
<label>Key:</label>
|
||||
<input name="annotationkey" ng-model="annotationInputs[$index].key" autofocus>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block" flex-gt-sm>
|
||||
<md-tooltip md-direction="bottom">Annotation value to be used on the deployed function</md-tooltip>
|
||||
<label>Value:</label>
|
||||
<input name="annotationValue" ng-model="annotationInputs[$index].value">
|
||||
</md-input-container>
|
||||
<md-icon class="add-input-icon" ng-click="addAnnotation($index)" ng-show="$last" aria-label="add-annotation" md-svg-src="img/icons/baseline-add_circle_outline-24px.svg"></md-icon>
|
||||
<md-icon class="remove-input-icon" ng-click="removeAnnotation($event,annotation)" ng-show="!$last" aria-label="remove-annotation" md-svg-src="img/icons/baseline-remove_circle_outline-24px.svg"></md-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="validation-error" layout-gt-xs="row" layout-align="start end">
|
||||
<span ng-show="validationError">{{ validationError }}</span>
|
||||
</div>
|
||||
@ -71,6 +162,9 @@
|
||||
<md-button ng-click="closeDialog()" class="md-secondary">
|
||||
Close Dialog
|
||||
</md-button>
|
||||
<md-button ng-if="selectedFunc" ng-click="customizeStoreFunc()" class="md-warn">
|
||||
Customize
|
||||
</md-button>
|
||||
<md-button ng-disabled="userForm.$invalid" ng-click="createFunc()" class="md-primary">
|
||||
Deploy
|
||||
</md-button>
|
||||
|
Loading…
x
Reference in New Issue
Block a user