mirror of https://github.com/portainer/portainer
feat(secrets): allow creating secrets beyond opaque [EE-2625] (#7709)
parent
3b2f0ff9eb
commit
4e20d70a99
|
@ -1,9 +1,9 @@
|
||||||
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
$onInit() {
|
$onInit() {
|
||||||
const secrets = (this.configurations || [])
|
const secrets = (this.configurations || [])
|
||||||
.filter((config) => config.Data && config.Type === KubernetesConfigurationTypes.SECRET)
|
.filter((config) => config.Data && config.Type === KubernetesConfigurationKinds.SECRET)
|
||||||
.flatMap((config) => Object.entries(config.Data))
|
.flatMap((config) => Object.entries(config.Data))
|
||||||
.map(([key, value]) => ({ key, value }));
|
.map(([key, value]) => ({ key, value }));
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import _ from 'lodash-es';
|
||||||
import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
|
import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
|
||||||
import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
|
import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
|
||||||
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
||||||
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
|
||||||
|
|
||||||
angular.module('portainer.docker').controller('KubernetesApplicationsDatatableController', [
|
angular.module('portainer.docker').controller('KubernetesApplicationsDatatableController', [
|
||||||
'$scope',
|
'$scope',
|
||||||
|
@ -112,7 +112,7 @@ angular.module('portainer.docker').controller('KubernetesApplicationsDatatableCo
|
||||||
};
|
};
|
||||||
|
|
||||||
this.hasConfigurationSecrets = function (item) {
|
this.hasConfigurationSecrets = function (item) {
|
||||||
return item.Configurations && item.Configurations.some((config) => config.Data && config.Type === KubernetesConfigurationTypes.SECRET);
|
return item.Configurations && item.Configurations.some((config) => config.Data && config.Type === KubernetesConfigurationKinds.SECRET);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<rd-widget-body classes="no-padding">
|
<rd-widget-body classes="no-padding">
|
||||||
<!-- table title and action menu -->
|
<!-- table title and action menu -->
|
||||||
<div class="toolBar !flex-col gap-1">
|
<div class="toolBar !flex-col gap-1">
|
||||||
<div class="toolBar vertical-center !gap-x-5 !gap-y-1 flex-wrap !p-0 w-full">
|
<div class="toolBar vertical-center !gap-x-5 !gap-y-1 flex-wrap !px-0 !py-1 w-full">
|
||||||
<div class="toolBarTitle">
|
<div class="toolBarTitle">
|
||||||
<div class="widget-icon space-right">
|
<div class="widget-icon space-right">
|
||||||
<pr-icon icon="'lock'" feather="true"></pr-icon>
|
<pr-icon icon="'lock'" feather="true"></pr-icon>
|
||||||
|
@ -125,11 +125,11 @@
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<table-column-header
|
<table-column-header
|
||||||
col-title="'Type'"
|
col-title="'Kind'"
|
||||||
can-sort="true"
|
can-sort="true"
|
||||||
is-sorted="$ctrl.state.orderBy === 'Type'"
|
is-sorted="$ctrl.state.orderBy === 'Kind'"
|
||||||
is-sorted-desc="$ctrl.state.orderBy === 'Type' && $ctrl.state.reverseOrder"
|
is-sorted-desc="$ctrl.state.orderBy === 'Kind' && $ctrl.state.reverseOrder"
|
||||||
ng-click="$ctrl.changeOrderBy('Type')"
|
ng-click="$ctrl.changeOrderBy('Kind')"
|
||||||
></table-column-header>
|
></table-column-header>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
<td>
|
<td>
|
||||||
<a ui-sref="kubernetes.resourcePools.resourcePool({ id: item.Namespace })">{{ item.Namespace }}</a>
|
<a ui-sref="kubernetes.resourcePools.resourcePool({ id: item.Namespace })">{{ item.Namespace }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ item.Type | kubernetesConfigurationTypeText }}</td>
|
<td>{{ item.Kind | kubernetesConfigurationKindText }}</td>
|
||||||
<td>{{ item.CreationDate | getisodate }} {{ item.ConfigurationOwner ? 'by ' + item.ConfigurationOwner : '' }}</td>
|
<td>{{ item.CreationDate | getisodate }} {{ item.ConfigurationOwner ? 'by ' + item.ConfigurationOwner : '' }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="!$ctrl.dataset">
|
<tr ng-if="!$ctrl.dataset">
|
||||||
|
|
|
@ -21,33 +21,70 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" ng-if="$ctrl.formValues.IsSimple">
|
<div class="form-group" ng-if="$ctrl.formValues.IsSimple">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12 vertical-center">
|
||||||
<button type="button" class="btn btn-sm btn-default" style="margin-left: 0" ng-click="$ctrl.addEntry()" data-cy="k8sConfigCreate-createEntryButton">
|
<button type="button" class="btn btn-sm btn-default" style="margin-left: 0" ng-click="$ctrl.addEntry()" data-cy="k8sConfigCreate-createEntryButton">
|
||||||
<pr-icon class="vertical-center" icon="'plus'" feather="true"></pr-icon> Create entry
|
<pr-icon class="vertical-center" icon="'plus'" feather="true"></pr-icon> Create entry
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-sm btn-default" ngf-select="$ctrl.addEntryFromFile($file)" style="margin-left: 0" data-cy="k8sConfigCreate-createConfigsFromFileButton">
|
<button
|
||||||
<pr-icon class="vertical-center" icon="'upload'" feather="true"></pr-icon> Create key/value from file
|
ng-if="
|
||||||
|
!(
|
||||||
|
($ctrl.isDockerConfig || $ctrl.formValues.Type.name === $ctrl.KubernetesSecretTypes.TLS.name || $ctrl.formValues.Type === $ctrl.KubernetesSecretTypes.TLS.value) &&
|
||||||
|
$ctrl.formValues.Kind === $ctrl.KubernetesConfigurationKinds.SECRET
|
||||||
|
)
|
||||||
|
"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-default ml-0"
|
||||||
|
ngf-select="$ctrl.addEntryFromFile($file)"
|
||||||
|
data-cy="k8sConfigCreate-createConfigsFromFileButton"
|
||||||
|
>
|
||||||
|
<pr-icon icon="'upload'" feather="true" class="vertical-center"></pr-icon> Create key/value from file
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
ng-if="$ctrl.isDockerConfig && $ctrl.formValues.Kind === $ctrl.KubernetesConfigurationKinds.SECRET"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-default ml-0"
|
||||||
|
ngf-select="$ctrl.addEntryFromFile($file)"
|
||||||
|
ngf-accept="'.json'"
|
||||||
|
data-cy="k8sConfigCreate-createConfigsFromFileButton"
|
||||||
|
>
|
||||||
|
<pr-icon icon="'upload'" feather="true" class="vertical-center"></pr-icon> Upload docker config file
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
ng-if="
|
||||||
|
($ctrl.formValues.Type.name === $ctrl.KubernetesSecretTypes.TLS.name || $ctrl.formValues.Type === $ctrl.KubernetesSecretTypes.TLS.value) &&
|
||||||
|
$ctrl.formValues.Kind === $ctrl.KubernetesConfigurationKinds.SECRET
|
||||||
|
"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-default ml-0"
|
||||||
|
ngf-select="$ctrl.addEntryFromFile($file)"
|
||||||
|
data-cy="k8sConfigCreate-createConfigsFromFileButton"
|
||||||
|
>
|
||||||
|
<pr-icon icon="'upload'" feather="true" class="vertical-center"></pr-icon> Upload TLS key/cert file
|
||||||
|
</button>
|
||||||
|
<portainer-tooltip message="'Maximum upload file size is 1MB'"></portainer-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-repeat="(index, entry) in $ctrl.formValues.Data" ng-if="$ctrl.formValues.IsSimple">
|
<div ng-repeat="(index, entry) in $ctrl.formValues.Data" ng-if="$ctrl.formValues.IsSimple">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="configuration_data_key_{{ index }}" class="col-sm-3 col-lg-2 control-label text-left required">Key</label>
|
<label for="configuration_data_key_{{ index }}" class="col-sm-3 col-lg-2 control-label text-left required"
|
||||||
|
>Key
|
||||||
|
<portainer-tooltip message="'The key must consist of alphanumeric characters, \'-\', \'_\' or \'.\' and be up to 253 characters in length.'"></portainer-tooltip>
|
||||||
|
</label>
|
||||||
<div class="col-sm-8 col-lg-9">
|
<div class="col-sm-8 col-lg-9">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
maxlength="253"
|
||||||
id="configuration_data_key_{{ index }}"
|
id="configuration_data_key_{{ index }}"
|
||||||
name="configuration_data_key_{{ index }}"
|
name="configuration_data_key_{{ index }}"
|
||||||
ng-model="$ctrl.formValues.Data[index].Key"
|
ng-model="$ctrl.formValues.Data[index].Key"
|
||||||
ng-disabled="entry.Used"
|
ng-disabled="entry.Used || $ctrl.isRequiredKey(entry.Key)"
|
||||||
required
|
required
|
||||||
ng-change="$ctrl.onChangeKey(entry)"
|
ng-change="$ctrl.onChangeKey(entry)"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="small text-warning"
|
class="small text-warning mt-1"
|
||||||
style="margin-top: 5px"
|
|
||||||
ng-show="
|
ng-show="
|
||||||
kubernetesConfigurationDataCreationForm['configuration_data_key_' + index].$invalid ||
|
kubernetesConfigurationDataCreationForm['configuration_data_key_' + index].$invalid ||
|
||||||
(!entry.Used && $ctrl.state.duplicateKeys[index] !== undefined) ||
|
(!entry.Used && $ctrl.state.duplicateKeys[index] !== undefined) ||
|
||||||
|
@ -55,18 +92,16 @@
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<ng-messages for="kubernetesConfigurationDataCreationForm['configuration_data_key_' + index].$error">
|
<ng-messages for="kubernetesConfigurationDataCreationForm['configuration_data_key_' + index].$error">
|
||||||
<p ng-message="required" class="vertical-center">
|
<p ng-message="required" class="vertical-center"> <pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> This field is required. </p>
|
||||||
<pr-icon class="vertical-center" icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> This field is required.
|
|
||||||
</p>
|
|
||||||
</ng-messages>
|
</ng-messages>
|
||||||
<div>
|
<div>
|
||||||
<p ng-if="$ctrl.state.duplicateKeys[index] !== undefined" class="vertical-center">
|
<p ng-if="$ctrl.state.duplicateKeys[index] !== undefined" class="vertical-center">
|
||||||
<pr-icon class="vertical-center" icon="'alert-triangle'" feather="true" mode="'warning'" class="vertical-center"></pr-icon>This key is already defined.
|
<pr-icon icon="'alert-triangle'" feather="true" mode="'warning'" class="vertical-center"></pr-icon>This key is already defined.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p ng-if="$ctrl.state.invalidKeys[index]" class="vertical-center">
|
<p ng-if="$ctrl.state.invalidKeys[index]" class="vertical-center">
|
||||||
<pr-icon class="vertical-center" icon="'alert-triangle'" feather="true" mode="'warning'" class="vertical-center"></pr-icon> This key is invalid. A valid key must
|
<pr-icon icon="'alert-triangle'" feather="true" mode="'warning'" class="vertical-center"></pr-icon> This key is invalid. A valid key must consist of alphanumeric
|
||||||
consist of alphanumeric characters, '-', '_' or '.'
|
characters, '-', '_' or '.'
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -106,10 +141,11 @@
|
||||||
<div class="col-sm-3 col-lg-2"></div>
|
<div class="col-sm-3 col-lg-2"></div>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<button
|
<button
|
||||||
|
ng-if="!$ctrl.isRequiredKey(entry.Key) || $ctrl.state.duplicateKeys[index] !== undefined"
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-dangerlight !ml-0"
|
class="btn btn-sm btn-dangerlight !ml-0"
|
||||||
style="margin-left: 0"
|
style="margin-left: 0"
|
||||||
ng-disabled="entry.Used"
|
ng-disabled="entry.Used || $ctrl.isEntryRequired()"
|
||||||
ng-click="$ctrl.removeEntry(index, entry)"
|
ng-click="$ctrl.removeEntry(index, entry)"
|
||||||
data-cy="k8sConfigDetail-removeEntryButton{{ index }}"
|
data-cy="k8sConfigDetail-removeEntryButton{{ index }}"
|
||||||
>
|
>
|
||||||
|
|
|
@ -3,6 +3,8 @@ angular.module('portainer.kubernetes').component('kubernetesConfigurationData',
|
||||||
controller: 'KubernetesConfigurationDataController',
|
controller: 'KubernetesConfigurationDataController',
|
||||||
bindings: {
|
bindings: {
|
||||||
formValues: '=',
|
formValues: '=',
|
||||||
|
isDockerConfig: '=',
|
||||||
|
onChangeValidation: '&',
|
||||||
isValid: '=',
|
isValid: '=',
|
||||||
isCreation: '=',
|
isCreation: '=',
|
||||||
isEditorDirty: '=',
|
isEditorDirty: '=',
|
||||||
|
|
|
@ -6,11 +6,12 @@ import { Base64 } from 'js-base64';
|
||||||
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper';
|
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper';
|
||||||
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
|
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
|
||||||
import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
|
import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
|
||||||
|
import { KubernetesConfigurationKinds, KubernetesSecretTypes } from 'Kubernetes/models/configuration/models';
|
||||||
|
|
||||||
class KubernetesConfigurationDataController {
|
class KubernetesConfigurationDataController {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor($async) {
|
constructor($async, Notifications) {
|
||||||
this.$async = $async;
|
Object.assign(this, { $async, Notifications });
|
||||||
|
|
||||||
this.editorUpdate = this.editorUpdate.bind(this);
|
this.editorUpdate = this.editorUpdate.bind(this);
|
||||||
this.editorUpdateAsync = this.editorUpdateAsync.bind(this);
|
this.editorUpdateAsync = this.editorUpdateAsync.bind(this);
|
||||||
|
@ -18,6 +19,8 @@ class KubernetesConfigurationDataController {
|
||||||
this.onFileLoadAsync = this.onFileLoadAsync.bind(this);
|
this.onFileLoadAsync = this.onFileLoadAsync.bind(this);
|
||||||
this.showSimpleMode = this.showSimpleMode.bind(this);
|
this.showSimpleMode = this.showSimpleMode.bind(this);
|
||||||
this.showAdvancedMode = this.showAdvancedMode.bind(this);
|
this.showAdvancedMode = this.showAdvancedMode.bind(this);
|
||||||
|
this.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
|
||||||
|
this.KubernetesSecretTypes = KubernetesSecretTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeKey(entry) {
|
onChangeKey(entry) {
|
||||||
|
@ -25,6 +28,8 @@ class KubernetesConfigurationDataController {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.onChangeValidation();
|
||||||
|
|
||||||
this.state.duplicateKeys = KubernetesFormValidationHelper.getDuplicates(_.map(this.formValues.Data, (data) => data.Key));
|
this.state.duplicateKeys = KubernetesFormValidationHelper.getDuplicates(_.map(this.formValues.Data, (data) => data.Key));
|
||||||
this.state.invalidKeys = KubernetesFormValidationHelper.getInvalidKeys(_.map(this.formValues.Data, (data) => data.Key));
|
this.state.invalidKeys = KubernetesFormValidationHelper.getInvalidKeys(_.map(this.formValues.Data, (data) => data.Key));
|
||||||
this.isValid = Object.keys(this.state.duplicateKeys).length === 0 && Object.keys(this.state.invalidKeys).length === 0;
|
this.isValid = Object.keys(this.state.duplicateKeys).length === 0 && Object.keys(this.state.invalidKeys).length === 0;
|
||||||
|
@ -32,6 +37,85 @@ class KubernetesConfigurationDataController {
|
||||||
|
|
||||||
addEntry() {
|
addEntry() {
|
||||||
this.formValues.Data.push(new KubernetesConfigurationFormValuesEntry());
|
this.formValues.Data.push(new KubernetesConfigurationFormValuesEntry());
|
||||||
|
|
||||||
|
// logic for setting required keys for new entries, based on the secret type
|
||||||
|
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
|
||||||
|
const newDataIndex = this.formValues.Data.length - 1;
|
||||||
|
const typeValue = typeof this.formValues.Type === 'string' ? this.formValues.Type : this.formValues.Type.value;
|
||||||
|
switch (typeValue) {
|
||||||
|
case this.KubernetesSecretTypes.DOCKERCFG.value:
|
||||||
|
this.addMissingKeys(['dockercfg'], newDataIndex);
|
||||||
|
break;
|
||||||
|
case this.KubernetesSecretTypes.DOCKERCONFIGJSON.value:
|
||||||
|
this.addMissingKeys(['.dockerconfigjson'], newDataIndex);
|
||||||
|
break;
|
||||||
|
case this.KubernetesSecretTypes.BASICAUTH.value:
|
||||||
|
// only add a required key if there is no required key out of username and password
|
||||||
|
if (!this.formValues.Data.some((entry) => entry.Key === 'username' || entry.Key === 'password')) {
|
||||||
|
this.addMissingKeys(['username', 'password'], newDataIndex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case this.KubernetesSecretTypes.SSHAUTH.value:
|
||||||
|
this.addMissingKeys(['ssh-privatekey'], newDataIndex);
|
||||||
|
break;
|
||||||
|
case this.KubernetesSecretTypes.TLS.value:
|
||||||
|
this.addMissingKeys(['tls.crt', 'tls.key'], newDataIndex);
|
||||||
|
break;
|
||||||
|
case this.KubernetesSecretTypes.BOOTSTRAPTOKEN.value:
|
||||||
|
this.addMissingKeys(['token-id', 'token-secret'], newDataIndex);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onChangeValidation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// addMissingKeys adds the keys in the keys array to the entry at the index provided and stops when the first one is added
|
||||||
|
addMissingKeys(keys, newDataIndex) {
|
||||||
|
for (let key of keys) {
|
||||||
|
if (this.formValues.Data.every((entry) => entry.Key !== key)) {
|
||||||
|
this.formValues.Data[newDataIndex].Key = key;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isRequiredKey(key) {
|
||||||
|
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
|
||||||
|
const secretTypeValue = typeof this.formValues.Type === 'string' ? this.formValues.Type : this.formValues.Type.value;
|
||||||
|
switch (secretTypeValue) {
|
||||||
|
case this.KubernetesSecretTypes.DOCKERCONFIGJSON.value:
|
||||||
|
if (key === '.dockerconfigjson') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case this.KubernetesSecretTypes.DOCKERCFG.value:
|
||||||
|
if (key === '.dockercfg') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case this.KubernetesSecretTypes.SSHAUTH.value:
|
||||||
|
if (key === 'ssh-privatekey') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case this.KubernetesSecretTypes.TLS.value:
|
||||||
|
if (key === 'tls.crt' || key === 'tls.key') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case this.KubernetesSecretTypes.BOOTSTRAPTOKEN.value:
|
||||||
|
if (key === 'token-id' || key === 'token-secret') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeEntry(index, entry) {
|
removeEntry(index, entry) {
|
||||||
|
@ -55,24 +139,77 @@ class KubernetesConfigurationDataController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async onFileLoadAsync(event) {
|
async onFileLoadAsync(event) {
|
||||||
const entry = new KubernetesConfigurationFormValuesEntry();
|
// exit if the file is too big
|
||||||
const encoding = chardet.detect(Buffer.from(event.target.result));
|
const maximumFileSizeBytes = 1024 * 1024; // 1MB
|
||||||
const decoder = new TextDecoder(encoding);
|
if (event.target.result.byteLength > maximumFileSizeBytes) {
|
||||||
|
this.Notifications.error('File size is too big', 'File size is too big', 'Select a file that is 1MB or smaller.');
|
||||||
entry.Key = event.target.fileName;
|
return;
|
||||||
entry.IsBinary = KubernetesConfigurationHelper.isBinary(encoding);
|
}
|
||||||
|
|
||||||
if (!entry.IsBinary) {
|
const entry = new KubernetesConfigurationFormValuesEntry();
|
||||||
entry.Value = decoder.decode(event.target.result);
|
try {
|
||||||
} else {
|
const encoding = chardet.detect(Buffer.from(event.target.result));
|
||||||
const stringValue = decoder.decode(event.target.result);
|
const decoder = new TextDecoder(encoding);
|
||||||
entry.Value = Base64.encode(stringValue);
|
|
||||||
|
entry.IsBinary = KubernetesConfigurationHelper.isBinary(encoding);
|
||||||
|
|
||||||
|
if (!entry.IsBinary) {
|
||||||
|
entry.Value = decoder.decode(event.target.result);
|
||||||
|
} else {
|
||||||
|
const stringValue = decoder.decode(event.target.result);
|
||||||
|
entry.Value = Base64.encode(stringValue);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.Notifications.error('Failed to upload file', error, 'Failed to upload file');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Key = event.target.fileName;
|
||||||
|
|
||||||
|
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
|
||||||
|
if (this.isDockerConfig) {
|
||||||
|
if (this.formValues.Type.name === this.KubernetesSecretTypes.DOCKERCFG.name) {
|
||||||
|
entry.Key = '.dockercfg';
|
||||||
|
} else {
|
||||||
|
entry.Key = '.dockerconfigjson';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.formValues.Type.name === this.KubernetesSecretTypes.TLS.name) {
|
||||||
|
const isCrt = entry.Value.indexOf('BEGIN CERTIFICATE') !== -1;
|
||||||
|
if (isCrt) {
|
||||||
|
entry.Key = 'tls.crt';
|
||||||
|
}
|
||||||
|
const isKey = entry.Value.indexOf('PRIVATE KEY') !== -1;
|
||||||
|
if (isKey) {
|
||||||
|
entry.Key = 'tls.key';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this.formValues.Data has a key that matches an existing key, then replace it
|
||||||
|
const existingEntryIndex = this.formValues.Data.findIndex((data) => data.Key === entry.Key || (data.Value === '' && data.Key === ''));
|
||||||
|
if (existingEntryIndex !== -1) {
|
||||||
|
this.formValues.Data[existingEntryIndex] = entry;
|
||||||
|
} else {
|
||||||
|
this.formValues.Data.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.formValues.Data.push(entry);
|
|
||||||
this.onChangeKey();
|
this.onChangeKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isEntryRequired() {
|
||||||
|
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
|
||||||
|
const typeValue = typeof this.formValues.Type === 'string' ? this.formValues.Type : this.formValues.Type.value;
|
||||||
|
if (this.formValues.Data.length === 1) {
|
||||||
|
if (typeValue !== this.KubernetesSecretTypes.SERVICEACCOUNTTOKEN.value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
onFileLoad(event) {
|
onFileLoad(event) {
|
||||||
return this.$async(this.onFileLoadAsync, event);
|
return this.$async(this.onFileLoadAsync, event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import _ from 'lodash-es';
|
import _ from 'lodash-es';
|
||||||
import { KubernetesConfiguration, KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
import { KubernetesConfiguration, KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
|
||||||
|
|
||||||
class KubernetesConfigurationConverter {
|
class KubernetesConfigurationConverter {
|
||||||
static secretToConfiguration(secret) {
|
static secretToConfiguration(secret) {
|
||||||
const res = new KubernetesConfiguration();
|
const res = new KubernetesConfiguration();
|
||||||
res.Type = KubernetesConfigurationTypes.SECRET;
|
res.Kind = KubernetesConfigurationKinds.SECRET;
|
||||||
res.Id = secret.Id;
|
res.Id = secret.Id;
|
||||||
res.Name = secret.Name;
|
res.Name = secret.Name;
|
||||||
|
res.Type = secret.Type;
|
||||||
res.Namespace = secret.Namespace;
|
res.Namespace = secret.Namespace;
|
||||||
res.CreationDate = secret.CreationDate;
|
res.CreationDate = secret.CreationDate;
|
||||||
res.Yaml = secret.Yaml;
|
res.Yaml = secret.Yaml;
|
||||||
|
@ -21,7 +22,7 @@ class KubernetesConfigurationConverter {
|
||||||
|
|
||||||
static configMapToConfiguration(configMap) {
|
static configMapToConfiguration(configMap) {
|
||||||
const res = new KubernetesConfiguration();
|
const res = new KubernetesConfiguration();
|
||||||
res.Type = KubernetesConfigurationTypes.CONFIGMAP;
|
res.Kind = KubernetesConfigurationKinds.CONFIGMAP;
|
||||||
res.Id = configMap.Id;
|
res.Id = configMap.Id;
|
||||||
res.Name = configMap.Name;
|
res.Name = configMap.Name;
|
||||||
res.Namespace = configMap.Namespace;
|
res.Namespace = configMap.Namespace;
|
||||||
|
|
|
@ -4,12 +4,13 @@ import { KubernetesApplicationSecret } from 'Kubernetes/models/secret/models';
|
||||||
import { KubernetesPortainerConfigurationDataAnnotation } from 'Kubernetes/models/configuration/models';
|
import { KubernetesPortainerConfigurationDataAnnotation } from 'Kubernetes/models/configuration/models';
|
||||||
import { KubernetesPortainerConfigurationOwnerLabel } from 'Kubernetes/models/configuration/models';
|
import { KubernetesPortainerConfigurationOwnerLabel } from 'Kubernetes/models/configuration/models';
|
||||||
import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
|
import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
|
||||||
|
import { KubernetesSecretTypes } from 'Kubernetes/models/configuration/models';
|
||||||
class KubernetesSecretConverter {
|
class KubernetesSecretConverter {
|
||||||
static createPayload(secret) {
|
static createPayload(secret) {
|
||||||
const res = new KubernetesSecretCreatePayload();
|
const res = new KubernetesSecretCreatePayload();
|
||||||
res.metadata.name = secret.Name;
|
res.metadata.name = secret.Name;
|
||||||
res.metadata.namespace = secret.Namespace;
|
res.metadata.namespace = secret.Namespace;
|
||||||
|
res.type = secret.Type.value;
|
||||||
const configurationOwner = _.truncate(secret.ConfigurationOwner, { length: 63, omission: '' });
|
const configurationOwner = _.truncate(secret.ConfigurationOwner, { length: 63, omission: '' });
|
||||||
res.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] = configurationOwner;
|
res.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] = configurationOwner;
|
||||||
|
|
||||||
|
@ -25,6 +26,11 @@ class KubernetesSecretConverter {
|
||||||
if (annotation !== '') {
|
if (annotation !== '') {
|
||||||
res.metadata.annotations[KubernetesPortainerConfigurationDataAnnotation] = annotation;
|
res.metadata.annotations[KubernetesPortainerConfigurationDataAnnotation] = annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_.forEach(secret.Annotations, (entry) => {
|
||||||
|
res.metadata.annotations[entry.name] = entry.value;
|
||||||
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +38,7 @@ class KubernetesSecretConverter {
|
||||||
const res = new KubernetesSecretUpdatePayload();
|
const res = new KubernetesSecretUpdatePayload();
|
||||||
res.metadata.name = secret.Name;
|
res.metadata.name = secret.Name;
|
||||||
res.metadata.namespace = secret.Namespace;
|
res.metadata.namespace = secret.Namespace;
|
||||||
|
res.type = secret.Type;
|
||||||
res.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] = secret.ConfigurationOwner;
|
res.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] = secret.ConfigurationOwner;
|
||||||
|
|
||||||
let annotation = '';
|
let annotation = '';
|
||||||
|
@ -54,6 +61,7 @@ class KubernetesSecretConverter {
|
||||||
res.Id = payload.metadata.uid;
|
res.Id = payload.metadata.uid;
|
||||||
res.Name = payload.metadata.name;
|
res.Name = payload.metadata.name;
|
||||||
res.Namespace = payload.metadata.namespace;
|
res.Namespace = payload.metadata.namespace;
|
||||||
|
res.Type = payload.type;
|
||||||
res.ConfigurationOwner = payload.metadata.labels ? payload.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] : '';
|
res.ConfigurationOwner = payload.metadata.labels ? payload.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] : '';
|
||||||
res.CreationDate = payload.metadata.creationTimestamp;
|
res.CreationDate = payload.metadata.creationTimestamp;
|
||||||
|
|
||||||
|
@ -84,8 +92,19 @@ class KubernetesSecretConverter {
|
||||||
const res = new KubernetesApplicationSecret();
|
const res = new KubernetesApplicationSecret();
|
||||||
res.Name = formValues.Name;
|
res.Name = formValues.Name;
|
||||||
res.Namespace = formValues.ResourcePool.Namespace.Name;
|
res.Namespace = formValues.ResourcePool.Namespace.Name;
|
||||||
|
res.Type = formValues.Type;
|
||||||
res.ConfigurationOwner = formValues.ConfigurationOwner;
|
res.ConfigurationOwner = formValues.ConfigurationOwner;
|
||||||
res.Data = formValues.Data;
|
res.Data = formValues.Data;
|
||||||
|
|
||||||
|
switch (formValues.Type) {
|
||||||
|
case KubernetesSecretTypes.CUSTOM:
|
||||||
|
res.Type.value = formValues.customType;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KubernetesSecretTypes.SERVICEACCOUNTTOKEN:
|
||||||
|
res.Annotations = [{ name: 'kubernetes.io/service-account.name', value: formValues.ServiceAccountName }];
|
||||||
|
break;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
|
||||||
|
|
||||||
angular.module('portainer.kubernetes').filter('kubernetesConfigurationTypeText', function () {
|
angular.module('portainer.kubernetes').filter('kubernetesConfigurationKindText', function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
return function (type) {
|
return function (type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case KubernetesConfigurationTypes.SECRET:
|
case KubernetesConfigurationKinds.SECRET:
|
||||||
return 'Secret';
|
return 'Secret';
|
||||||
case KubernetesConfigurationTypes.CONFIGMAP:
|
case KubernetesConfigurationKinds.CONFIGMAP:
|
||||||
return 'ConfigMap';
|
return 'ConfigMap';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import _ from 'lodash-es';
|
import _ from 'lodash-es';
|
||||||
import { KubernetesPortMapping, KubernetesPortMappingPort } from 'Kubernetes/models/port/models';
|
import { KubernetesPortMapping, KubernetesPortMappingPort } from 'Kubernetes/models/port/models';
|
||||||
import { KubernetesService, KubernetesServicePort, KubernetesServiceTypes } from 'Kubernetes/models/service/models';
|
import { KubernetesService, KubernetesServicePort, KubernetesServiceTypes } from 'Kubernetes/models/service/models';
|
||||||
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
|
||||||
import {
|
import {
|
||||||
KubernetesApplicationAutoScalerFormValue,
|
KubernetesApplicationAutoScalerFormValue,
|
||||||
KubernetesApplicationConfigurationFormValue,
|
KubernetesApplicationConfigurationFormValue,
|
||||||
|
@ -147,7 +147,7 @@ class KubernetesApplicationHelper {
|
||||||
/* #region CONFIGURATIONS FV <> ENV & VOLUMES */
|
/* #region CONFIGURATIONS FV <> ENV & VOLUMES */
|
||||||
static generateConfigurationFormValuesFromEnvAndVolumes(env, volumes, configurations) {
|
static generateConfigurationFormValuesFromEnvAndVolumes(env, volumes, configurations) {
|
||||||
const finalRes = _.flatMap(configurations, (cfg) => {
|
const finalRes = _.flatMap(configurations, (cfg) => {
|
||||||
const filterCondition = cfg.Type === KubernetesConfigurationTypes.CONFIGMAP ? 'valueFrom.configMapKeyRef.name' : 'valueFrom.secretKeyRef.name';
|
const filterCondition = cfg.Type === KubernetesConfigurationKinds.CONFIGMAP ? 'valueFrom.configMapKeyRef.name' : 'valueFrom.secretKeyRef.name';
|
||||||
|
|
||||||
const cfgEnv = _.filter(env, [filterCondition, cfg.Name]);
|
const cfgEnv = _.filter(env, [filterCondition, cfg.Name]);
|
||||||
const cfgVol = _.filter(volumes, { configurationName: cfg.Name });
|
const cfgVol = _.filter(volumes, { configurationName: cfg.Name });
|
||||||
|
@ -207,7 +207,7 @@ class KubernetesApplicationHelper {
|
||||||
let finalMounts = [];
|
let finalMounts = [];
|
||||||
|
|
||||||
_.forEach(configurations, (config) => {
|
_.forEach(configurations, (config) => {
|
||||||
const isBasic = config.SelectedConfiguration.Type === KubernetesConfigurationTypes.CONFIGMAP;
|
const isBasic = config.SelectedConfiguration.Type === KubernetesConfigurationKinds.CONFIGMAP;
|
||||||
|
|
||||||
if (!config.Overriden) {
|
if (!config.Overriden) {
|
||||||
const envKeys = _.keys(config.SelectedConfiguration.Data);
|
const envKeys = _.keys(config.SelectedConfiguration.Data);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import _ from 'lodash-es';
|
import _ from 'lodash-es';
|
||||||
import YAML from 'yaml';
|
import YAML from 'yaml';
|
||||||
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
|
||||||
import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
|
import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
|
||||||
|
|
||||||
class KubernetesConfigurationHelper {
|
class KubernetesConfigurationHelper {
|
||||||
|
@ -8,7 +8,7 @@ class KubernetesConfigurationHelper {
|
||||||
return _.filter(applications, (app) => {
|
return _.filter(applications, (app) => {
|
||||||
let envFind;
|
let envFind;
|
||||||
let volumeFind;
|
let volumeFind;
|
||||||
if (config.Type === KubernetesConfigurationTypes.CONFIGMAP) {
|
if (config.Type === KubernetesConfigurationKinds.CONFIGMAP) {
|
||||||
envFind = _.find(app.Env, { valueFrom: { configMapKeyRef: { name: config.Name } } });
|
envFind = _.find(app.Env, { valueFrom: { configMapKeyRef: { name: config.Name } } });
|
||||||
volumeFind = _.find(app.Volumes, { configMap: { name: config.Name } });
|
volumeFind = _.find(app.Volumes, { configMap: { name: config.Name } });
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { KubernetesConfigurationTypes } from './models';
|
import { KubernetesConfigurationKinds, KubernetesSecretTypes } from './models';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KubernetesConfigurationFormValues Model
|
* KubernetesConfigurationFormValues Model
|
||||||
|
@ -8,15 +8,17 @@ const _KubernetesConfigurationFormValues = Object.freeze({
|
||||||
ResourcePool: '',
|
ResourcePool: '',
|
||||||
Name: '',
|
Name: '',
|
||||||
ConfigurationOwner: '',
|
ConfigurationOwner: '',
|
||||||
Type: KubernetesConfigurationTypes.CONFIGMAP,
|
Kind: KubernetesConfigurationKinds.CONFIGMAP,
|
||||||
Data: [],
|
Data: [],
|
||||||
DataYaml: '',
|
DataYaml: '',
|
||||||
IsSimple: true,
|
IsSimple: true,
|
||||||
|
ServiceAccountName: '',
|
||||||
|
Type: KubernetesSecretTypes.OPAQUE,
|
||||||
});
|
});
|
||||||
|
|
||||||
export class KubernetesConfigurationFormValues {
|
export class KubernetesConfigurationFormValues {
|
||||||
constructor() {
|
constructor() {
|
||||||
Object.assign(this, JSON.parse(JSON.stringify(_KubernetesConfigurationFormValues)));
|
Object.assign(this, _KubernetesConfigurationFormValues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ export const KubernetesPortainerConfigurationDataAnnotation = 'io.portainer.kube
|
||||||
const _KubernetesConfiguration = Object.freeze({
|
const _KubernetesConfiguration = Object.freeze({
|
||||||
Id: 0,
|
Id: 0,
|
||||||
Name: '',
|
Name: '',
|
||||||
Type: '',
|
Kind: '',
|
||||||
Namespace: '',
|
Namespace: '',
|
||||||
CreationDate: '',
|
CreationDate: '',
|
||||||
ConfigurationOwner: '',
|
ConfigurationOwner: '',
|
||||||
|
@ -23,7 +23,19 @@ export class KubernetesConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KubernetesConfigurationTypes = Object.freeze({
|
export const KubernetesConfigurationKinds = Object.freeze({
|
||||||
CONFIGMAP: 1,
|
CONFIGMAP: 1,
|
||||||
SECRET: 2,
|
SECRET: 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const KubernetesSecretTypes = Object.freeze({
|
||||||
|
OPAQUE: { name: 'Opaque', value: 'Opaque' },
|
||||||
|
SERVICEACCOUNTTOKEN: { name: 'Service account token', value: 'kubernetes.io/service-account-token' },
|
||||||
|
DOCKERCFG: { name: 'Dockercfg', value: 'kubernetes.io/dockercfg' },
|
||||||
|
DOCKERCONFIGJSON: { name: 'Dockerconfigjson', value: 'kubernetes.io/dockerconfigjson' },
|
||||||
|
BASICAUTH: { name: 'Basic auth', value: 'kubernetes.io/basic-auth' },
|
||||||
|
SSHAUTH: { name: 'SSH auth', value: 'kubernetes.io/ssh-auth' },
|
||||||
|
TLS: { name: 'TLS', value: 'kubernetes.io/tls' },
|
||||||
|
BOOTSTRAPTOKEN: { name: 'Bootstrap token', value: 'bootstrap.kubernetes.io/token' },
|
||||||
|
CUSTOM: { name: 'Custom', value: 'Custom' },
|
||||||
|
});
|
||||||
|
|
|
@ -5,11 +5,13 @@ const _KubernetesApplicationSecret = Object.freeze({
|
||||||
Id: 0,
|
Id: 0,
|
||||||
Name: '',
|
Name: '',
|
||||||
Namespace: '',
|
Namespace: '',
|
||||||
|
Type: '',
|
||||||
CreationDate: '',
|
CreationDate: '',
|
||||||
ConfigurationOwner: '',
|
ConfigurationOwner: '',
|
||||||
Yaml: '',
|
Yaml: '',
|
||||||
Data: [],
|
Data: [],
|
||||||
SecretType: '',
|
SecretType: '',
|
||||||
|
Annotations: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
export class KubernetesApplicationSecret {
|
export class KubernetesApplicationSecret {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { KubernetesCommonMetadataPayload } from 'Kubernetes/models/common/payloa
|
||||||
*/
|
*/
|
||||||
const _KubernetesSecretCreatePayload = Object.freeze({
|
const _KubernetesSecretCreatePayload = Object.freeze({
|
||||||
metadata: new KubernetesCommonMetadataPayload(),
|
metadata: new KubernetesCommonMetadataPayload(),
|
||||||
type: 'Opaque',
|
type: '',
|
||||||
data: {},
|
data: {},
|
||||||
stringData: {},
|
stringData: {},
|
||||||
});
|
});
|
||||||
|
@ -21,7 +21,7 @@ export class KubernetesSecretCreatePayload {
|
||||||
*/
|
*/
|
||||||
const _KubernetesSecretUpdatePayload = Object.freeze({
|
const _KubernetesSecretUpdatePayload = Object.freeze({
|
||||||
metadata: new KubernetesCommonMetadataPayload(),
|
metadata: new KubernetesCommonMetadataPayload(),
|
||||||
type: 'Opaque',
|
type: '',
|
||||||
data: {},
|
data: {},
|
||||||
stringData: {},
|
stringData: {},
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||||
|
|
||||||
|
export async function getServiceAccounts(environmentId, namespaceId) {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
data: { items },
|
||||||
|
} = await axios.get(urlBuilder(environmentId, namespaceId));
|
||||||
|
return items;
|
||||||
|
} catch (error) {
|
||||||
|
throw parseAxiosError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function urlBuilder(environmentId, namespaceId) {
|
||||||
|
return `endpoints/${environmentId}/kubernetes/api/v1/namespaces/${namespaceId}/serviceaccounts`;
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ import _ from 'lodash-es';
|
||||||
import KubernetesConfigurationConverter from 'Kubernetes/converters/configuration';
|
import KubernetesConfigurationConverter from 'Kubernetes/converters/configuration';
|
||||||
import KubernetesConfigMapConverter from 'Kubernetes/converters/configMap';
|
import KubernetesConfigMapConverter from 'Kubernetes/converters/configMap';
|
||||||
import KubernetesSecretConverter from 'Kubernetes/converters/secret';
|
import KubernetesSecretConverter from 'Kubernetes/converters/secret';
|
||||||
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
|
||||||
import KubernetesCommonHelper from 'Kubernetes/helpers/commonHelper';
|
import KubernetesCommonHelper from 'Kubernetes/helpers/commonHelper';
|
||||||
|
|
||||||
class KubernetesConfigurationService {
|
class KubernetesConfigurationService {
|
||||||
|
@ -62,7 +62,7 @@ class KubernetesConfigurationService {
|
||||||
async createAsync(formValues) {
|
async createAsync(formValues) {
|
||||||
formValues.ConfigurationOwner = KubernetesCommonHelper.ownerToLabel(formValues.ConfigurationOwner);
|
formValues.ConfigurationOwner = KubernetesCommonHelper.ownerToLabel(formValues.ConfigurationOwner);
|
||||||
|
|
||||||
if (formValues.Type === KubernetesConfigurationTypes.CONFIGMAP) {
|
if (formValues.Kind === KubernetesConfigurationKinds.CONFIGMAP) {
|
||||||
const configMap = KubernetesConfigMapConverter.configurationFormValuesToConfigMap(formValues);
|
const configMap = KubernetesConfigMapConverter.configurationFormValuesToConfigMap(formValues);
|
||||||
await this.KubernetesConfigMapService.create(configMap);
|
await this.KubernetesConfigMapService.create(configMap);
|
||||||
} else {
|
} else {
|
||||||
|
@ -79,7 +79,7 @@ class KubernetesConfigurationService {
|
||||||
* UPDATE
|
* UPDATE
|
||||||
*/
|
*/
|
||||||
async updateAsync(formValues, configuration) {
|
async updateAsync(formValues, configuration) {
|
||||||
if (formValues.Type === KubernetesConfigurationTypes.CONFIGMAP) {
|
if (formValues.Kind === KubernetesConfigurationKinds.CONFIGMAP) {
|
||||||
const configMap = KubernetesConfigMapConverter.configurationFormValuesToConfigMap(formValues);
|
const configMap = KubernetesConfigMapConverter.configurationFormValuesToConfigMap(formValues);
|
||||||
configMap.ConfigurationOwner = configuration.ConfigurationOwner;
|
configMap.ConfigurationOwner = configuration.ConfigurationOwner;
|
||||||
await this.KubernetesConfigMapService.update(configMap);
|
await this.KubernetesConfigMapService.update(configMap);
|
||||||
|
@ -98,7 +98,7 @@ class KubernetesConfigurationService {
|
||||||
* DELETE
|
* DELETE
|
||||||
*/
|
*/
|
||||||
async deleteAsync(config) {
|
async deleteAsync(config) {
|
||||||
if (config.Type === KubernetesConfigurationTypes.CONFIGMAP) {
|
if (config.Kind == KubernetesConfigurationKinds.CONFIGMAP) {
|
||||||
await this.KubernetesConfigMapService.delete(config);
|
await this.KubernetesConfigMapService.delete(config);
|
||||||
} else {
|
} else {
|
||||||
await this.KubernetesSecretService.delete(config);
|
await this.KubernetesSecretService.delete(config);
|
||||||
|
|
|
@ -14,9 +14,9 @@
|
||||||
<rd-widget-body>
|
<rd-widget-body>
|
||||||
<form class="form-horizontal" name="kubernetesConfigurationCreationForm" autocomplete="off">
|
<form class="form-horizontal" name="kubernetesConfigurationCreationForm" autocomplete="off">
|
||||||
<!-- name -->
|
<!-- name -->
|
||||||
<div class="form-group">
|
<div class="form-group mb-0">
|
||||||
<label for="configuration_name" class="col-sm-3 col-lg-2 control-label text-left required">Name</label>
|
<label for="configuration_name" class="col-sm-3 col-lg-2 control-label text-left required">Name</label>
|
||||||
<div class="col-sm-8 col-lg-9">
|
<div class="col-sm-8 col-lg-9 mb-0">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
@ -29,21 +29,21 @@
|
||||||
required
|
required
|
||||||
data-cy="k8sConfigCreate-nameInput"
|
data-cy="k8sConfigCreate-nameInput"
|
||||||
/>
|
/>
|
||||||
</div>
|
<div ng-show="kubernetesConfigurationCreationForm.configuration_name.$invalid || ctrl.state.alreadyExist">
|
||||||
</div>
|
<div class="help-block small text-warning">
|
||||||
<div class="form-group" ng-show="kubernetesConfigurationCreationForm.configuration_name.$invalid || ctrl.state.alreadyExist">
|
<div ng-messages="kubernetesConfigurationCreationForm.configuration_name.$error">
|
||||||
<div class="col-sm-3 col-lg-2"></div>
|
<p ng-message="required" class="vertical-center"><pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> This field is required.</p>
|
||||||
<div class="col-sm-8 col-lg-9 small text-warning">
|
<p ng-message="pattern" class="vertical-center"
|
||||||
<div ng-messages="kubernetesConfigurationCreationForm.configuration_name.$error">
|
><pr-icon icon="'alert-triangle'" feather="true" mode="'warning'" class="vertical-center"></pr-icon> This field must consist of lower case alphanumeric
|
||||||
<p ng-message="required" class="vertical-center"><pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> This field is required.</p>
|
characters, '-' or '.', and contain at most 63 characters, and must start and end with an alphanumeric character.</p
|
||||||
<p ng-message="pattern" class="vertical-center"
|
>
|
||||||
><pr-icon icon="'alert-triangle'" feather="true" mode="'warning'" class="vertical-center"></pr-icon> This field must consist of lower case alphanumeric
|
</div>
|
||||||
characters, '-' or '.', and contain at most 63 characters, and must start and end with an alphanumeric character.</p
|
<p ng-if="ctrl.state.alreadyExist" class="vertical-center"
|
||||||
>
|
><pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> A configuration with the same name already exists inside the selected
|
||||||
|
namespace.</p
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p ng-if="ctrl.state.alreadyExist" class="vertical-center"
|
|
||||||
><pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> A configuration with the same name already exists inside the selected namespace.</p
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !name -->
|
<!-- !name -->
|
||||||
|
@ -80,56 +80,155 @@
|
||||||
<!-- !resource-pool -->
|
<!-- !resource-pool -->
|
||||||
|
|
||||||
<div ng-if="ctrl.formValues.ResourcePool">
|
<div ng-if="ctrl.formValues.ResourcePool">
|
||||||
<div class="col-sm-12 form-section-title"> Configuration type </div>
|
<div class="col-sm-12 form-section-title"> Configuration kind </div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-12 small text-muted"> Select the type of data that you want to save in the configuration. </div>
|
<div class="col-sm-12 small text-muted"> Select the kind of data that you want to save in the configuration. </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- type options -->
|
<!-- type options -->
|
||||||
<div class="form-group">
|
<div class="form-group px-[15px] mb-0">
|
||||||
<div class="col-sm-12">
|
<div class="boxselector_wrapper">
|
||||||
<div class="boxselector_wrapper">
|
<div>
|
||||||
<div>
|
<input type="radio" id="type_basic" ng-value="ctrl.KubernetesConfigurationKinds.CONFIGMAP" ng-model="ctrl.formValues.Kind" ng-change="ctrl.onChangeKind()" />
|
||||||
<input type="radio" id="type_basic" ng-value="ctrl.KubernetesConfigurationTypes.CONFIGMAP" ng-model="ctrl.formValues.Type" />
|
<label for="type_basic" data-cy="k8sConfigCreate-nonSensitiveButton">
|
||||||
<label for="type_basic" data-cy="k8sConfigCreate-nonSensitiveButton">
|
<div class="boxselector_header">
|
||||||
<div class="boxselector_header">
|
<pr-icon icon="'svg-filecode'"></pr-icon>
|
||||||
<pr-icon icon="'svg-filecode'"></pr-icon>
|
ConfigMap
|
||||||
ConfigMap
|
</div>
|
||||||
</div>
|
<p>This configuration holds non-sensitive information</p>
|
||||||
<p>This configuration holds non-sensitive information</p>
|
</label>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
<div>
|
||||||
<div>
|
<input type="radio" id="type_secret" ng-value="ctrl.KubernetesConfigurationKinds.SECRET" ng-model="ctrl.formValues.Kind" ng-change="ctrl.onChangeKind()" />
|
||||||
<input type="radio" id="type_secret" ng-value="ctrl.KubernetesConfigurationTypes.SECRET" ng-model="ctrl.formValues.Type" />
|
<label for="type_secret" data-cy="k8sConfigCreate-sensitiveButton">
|
||||||
<label for="type_secret" data-cy="k8sConfigCreate-sensitiveButton">
|
<div class="boxselector_header">
|
||||||
<div class="boxselector_header">
|
<pr-icon icon="'lock'" feather="true"></pr-icon>
|
||||||
<pr-icon icon="'lock'" feather="true"></pr-icon>
|
Secret
|
||||||
Secret
|
</div>
|
||||||
</div>
|
<p>This configuration holds sensitive information</p>
|
||||||
<p>This configuration holds sensitive information</p>
|
</label>
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !type options -->
|
<!-- !type options -->
|
||||||
|
|
||||||
<div class="col-sm-12 form-section-title" ng-if="ctrl.formValues.Type == ctrl.KubernetesConfigurationTypes.SECRET"> Information </div>
|
<div ng-if="ctrl.formValues.Kind === ctrl.KubernetesConfigurationKinds.SECRET">
|
||||||
<div class="form-group" ng-if="ctrl.formValues.Type == ctrl.KubernetesConfigurationTypes.SECRET">
|
<div class="col-sm-12 form-section-title"> Information </div>
|
||||||
<div class="col-sm-12 small text-muted">
|
<div class="form-group">
|
||||||
Creating a sensitive configuration will create a Kubernetes Secret of type <code>Opaque</code>. You can find more information about this in the
|
<div class="col-sm-12 small text-muted vertical-center">
|
||||||
<a href="https://kubernetes.io/docs/concepts/configuration/secret/#secret-types" target="_blank">official documentation</a>.
|
<pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon>
|
||||||
|
<span>
|
||||||
|
More information about types of secret can be found in the official
|
||||||
|
<a class="hyperlink" href="https://kubernetes.io/docs/concepts/configuration/secret/#secret-types" target="_blank">kubernetes documentation</a>.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="configuration_data_type" class="col-sm-3 col-lg-2 control-label text-left">Type</label>
|
||||||
|
<div class="col-sm-8 col-lg-9">
|
||||||
|
<select
|
||||||
|
class="form-control"
|
||||||
|
id="configuration_data_type"
|
||||||
|
ng-model="ctrl.formValues.Type"
|
||||||
|
ng-options="value.name for (name, value) in ctrl.KubernetesSecretTypes"
|
||||||
|
ng-change="ctrl.onSecretTypeChange()"
|
||||||
|
></select>
|
||||||
|
|
||||||
|
<div class="col-sm-3 col-lg-2"></div>
|
||||||
|
</div>
|
||||||
|
<div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.SERVICEACCOUNTTOKEN" class="col-sm-12 small text-warning vertical-center pt-5">
|
||||||
|
<pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon>
|
||||||
|
<span
|
||||||
|
>You should only create a service account token Secret object if you can't use the TokenRequest API to obtain a token, and the security exposure of persisting
|
||||||
|
a non-expiring token credential in a readable API object is acceptable to you. <br />See
|
||||||
|
<a href="https://kubernetes.io/docs/concepts/configuration/secret/#service-account-token-secrets" target="_blank">service account token secrets</a> in the
|
||||||
|
kubernetes documentation.</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.DOCKERCFG" class="col-sm-12 small text-muted vertical-center pt-5">
|
||||||
|
<pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon>
|
||||||
|
<span>Ensure the Secret data field contains a <code>.dockercfg</code> key whose value is content of a legacy <code>~/.dockercfg</code> file.</span>
|
||||||
|
</div>
|
||||||
|
<div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.DOCKERCONFIGJSON" class="col-sm-12 small text-muted vertical-center pt-5">
|
||||||
|
<pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon>
|
||||||
|
<span>Ensure the Secret data field contains a <code>.dockerconfigjson</code> key whose value is content of a <code>~/.docker/config.json</code> file.</span>
|
||||||
|
</div>
|
||||||
|
<div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.TLS" class="col-sm-12 small text-muted vertical-center pt-5">
|
||||||
|
<pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon>
|
||||||
|
<span>Ensure the Secret data field contains a <code>tls.key</code> key and a <code>tls.crt</code> key.</span>
|
||||||
|
</div>
|
||||||
|
<div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.BOOTSTRAPTOKEN" class="col-sm-12 small text-muted vertical-center pt-5">
|
||||||
|
<pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon>
|
||||||
|
<span
|
||||||
|
>Ensure the Secret data field contains a <code>token-id</code> key and a <code>token-secret</code> key. See
|
||||||
|
<a href="https://kubernetes.io/docs/concepts/configuration/secret/#bootstrap-token-secrets" target="_blank">bootstrap token secrets</a> in the kubernetes
|
||||||
|
documentation for optional keys.</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.CUSTOM">
|
||||||
|
<label for="configuration_data_customtype" class="col-sm-3 col-lg-2 control-label text-left required">Custom Type</label>
|
||||||
|
<div class="col-sm-8 col-lg-9">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="custom_type"
|
||||||
|
class="form-control"
|
||||||
|
id="configuration_data_customtype"
|
||||||
|
ng-model="ctrl.formValues.customType"
|
||||||
|
ng-pattern="/^[a-z0-9]([a-z0-9-.]{0,61}[a-z0-9])?$/"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<div ng-show="kubernetesConfigurationCreationForm.custom_type.$invalid">
|
||||||
|
<div class="help-block small text-warning">
|
||||||
|
<div ng-messages="kubernetesConfigurationCreationForm.custom_type.$error">
|
||||||
|
<p ng-message="required" class="vertical-center"><pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> This field is required.</p>
|
||||||
|
<p ng-message="pattern" class="vertical-center"
|
||||||
|
><pr-icon icon="'alert-triangle'" feather="true" mode="'warning'" class="vertical-center"></pr-icon> This field must consist of lower case alphanumeric
|
||||||
|
characters, '-' or '.', and contain at most 63 characters, and must start and end with an alphanumeric character.</p
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.SERVICEACCOUNTTOKEN">
|
||||||
|
<label for="service_account" class="col-sm-3 col-lg-2 control-label text-left required">Service Account</label>
|
||||||
|
<div class="col-sm-8 col-lg-9">
|
||||||
|
<select
|
||||||
|
class="form-control"
|
||||||
|
id="service_account"
|
||||||
|
ng-selected="$first"
|
||||||
|
ng-model="ctrl.formValues.ServiceAccountName"
|
||||||
|
ng-options="value.metadata.name as value.metadata.name for (name, value) in ctrl.availableServiceAccounts"
|
||||||
|
data-cy="k8sConfigCreate-serviceAccountDropdown"
|
||||||
|
ng-change="ctrl.onChangeServiceAccount()"
|
||||||
|
required
|
||||||
|
></select>
|
||||||
|
<div class="help-block small text-warning" ng-messages="kubernetesConfigurationCreationForm.service_account.$error">
|
||||||
|
<p class="vertical-center" ng-message="required"> <pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon>This field is required.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<kubernetes-configuration-data
|
<kubernetes-configuration-data
|
||||||
ng-if="ctrl.formValues"
|
ng-if="ctrl.formValues"
|
||||||
form-values="ctrl.formValues"
|
form-values="ctrl.formValues"
|
||||||
|
is-docker-config="ctrl.state.isDockerConfig"
|
||||||
is-valid="ctrl.state.isDataValid"
|
is-valid="ctrl.state.isDataValid"
|
||||||
|
on-change-validation="ctrl.isFormValid()"
|
||||||
is-creation="true"
|
is-creation="true"
|
||||||
is-editor-dirty="ctrl.state.isEditorDirty"
|
is-editor-dirty="ctrl.state.isEditorDirty"
|
||||||
></kubernetes-configuration-data>
|
></kubernetes-configuration-data>
|
||||||
|
|
||||||
|
<div class="form-group" ng-if="ctrl.state.secretWarningMessage">
|
||||||
|
<div class="col-sm-12 small text-warning vertical-center pt-5">
|
||||||
|
<pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon>
|
||||||
|
<span>{{ ctrl.state.secretWarningMessage }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- summary -->
|
<!-- summary -->
|
||||||
|
@ -150,7 +249,7 @@
|
||||||
button-spinner="ctrl.state.actionInProgress"
|
button-spinner="ctrl.state.actionInProgress"
|
||||||
data-cy="k8sConfigCreate-CreateConfigButton"
|
data-cy="k8sConfigCreate-CreateConfigButton"
|
||||||
>
|
>
|
||||||
<span ng-hide="ctrl.state.actionInProgress">Create {{ ctrl.formValues.Type | kubernetesConfigurationTypeText }}</span>
|
<span ng-hide="ctrl.state.actionInProgress">Create {{ ctrl.formValues.Kind | kubernetesConfigurationKindText }}</span>
|
||||||
<span ng-show="ctrl.state.actionInProgress">Creation in progress...</span>
|
<span ng-show="ctrl.state.actionInProgress">Creation in progress...</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,43 +1,134 @@
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import _ from 'lodash-es';
|
import _ from 'lodash-es';
|
||||||
import { KubernetesConfigurationFormValues, KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
|
import { KubernetesConfigurationFormValues, KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
|
||||||
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
import { KubernetesConfigurationKinds, KubernetesSecretTypes } from 'Kubernetes/models/configuration/models';
|
||||||
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
|
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
|
||||||
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
||||||
|
import { getServiceAccounts } from 'Kubernetes/rest/serviceAccount';
|
||||||
|
|
||||||
|
import { isConfigurationFormValid } from '../validation';
|
||||||
|
|
||||||
class KubernetesCreateConfigurationController {
|
class KubernetesCreateConfigurationController {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor($async, $state, $window, ModalService, Notifications, Authentication, KubernetesConfigurationService, KubernetesResourcePoolService) {
|
constructor($async, $state, $window, ModalService, Notifications, Authentication, KubernetesConfigurationService, KubernetesResourcePoolService, EndpointProvider) {
|
||||||
this.$async = $async;
|
this.$async = $async;
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
this.$window = $window;
|
this.$window = $window;
|
||||||
|
this.EndpointProvider = EndpointProvider;
|
||||||
this.ModalService = ModalService;
|
this.ModalService = ModalService;
|
||||||
this.Notifications = Notifications;
|
this.Notifications = Notifications;
|
||||||
this.Authentication = Authentication;
|
this.Authentication = Authentication;
|
||||||
this.KubernetesConfigurationService = KubernetesConfigurationService;
|
this.KubernetesConfigurationService = KubernetesConfigurationService;
|
||||||
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
|
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
|
||||||
this.KubernetesConfigurationTypes = KubernetesConfigurationTypes;
|
this.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
|
||||||
|
this.KubernetesSecretTypes = KubernetesSecretTypes;
|
||||||
|
|
||||||
this.onInit = this.onInit.bind(this);
|
this.onInit = this.onInit.bind(this);
|
||||||
this.createConfigurationAsync = this.createConfigurationAsync.bind(this);
|
this.createConfigurationAsync = this.createConfigurationAsync.bind(this);
|
||||||
this.getConfigurationsAsync = this.getConfigurationsAsync.bind(this);
|
this.getConfigurationsAsync = this.getConfigurationsAsync.bind(this);
|
||||||
|
this.onResourcePoolSelectionChangeAsync = this.onResourcePoolSelectionChangeAsync.bind(this);
|
||||||
|
this.onSecretTypeChange = this.onSecretTypeChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeName() {
|
onChangeName() {
|
||||||
const filteredConfigurations = _.filter(this.configurations, (config) => config.Namespace === this.formValues.ResourcePool.Namespace.Name);
|
const filteredConfigurations = _.filter(
|
||||||
|
this.configurations,
|
||||||
|
(config) => config.Namespace === this.formValues.ResourcePool.Namespace.Name && config.Kind === this.formValues.Kind
|
||||||
|
);
|
||||||
this.state.alreadyExist = _.find(filteredConfigurations, (config) => config.Name === this.formValues.Name) !== undefined;
|
this.state.alreadyExist = _.find(filteredConfigurations, (config) => config.Name === this.formValues.Name) !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
onResourcePoolSelectionChange() {
|
onChangeKind() {
|
||||||
this.onChangeName();
|
this.onChangeName();
|
||||||
|
// if there is no data field, add one
|
||||||
|
if (this.formValues.Data.length === 0) {
|
||||||
|
this.formValues.Data.push(new KubernetesConfigurationFormValuesEntry());
|
||||||
|
}
|
||||||
|
// if changing back to a secret, that is a service account token, remove the data field
|
||||||
|
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
|
||||||
|
this.onSecretTypeChange();
|
||||||
|
} else {
|
||||||
|
this.isDockerConfig = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onResourcePoolSelectionChangeAsync() {
|
||||||
|
try {
|
||||||
|
this.onChangeName();
|
||||||
|
this.availableServiceAccounts = await getServiceAccounts(this.environmentId, this.formValues.ResourcePool.Namespace.Name);
|
||||||
|
this.formValues.ServiceAccountName = this.availableServiceAccounts.length > 0 ? this.availableServiceAccounts[0].metadata.name : '';
|
||||||
|
} catch (err) {
|
||||||
|
this.Notifications.error('Failure', err, 'Unable load service accounts');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onResourcePoolSelectionChange() {
|
||||||
|
this.$async(this.onResourcePoolSelectionChangeAsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSecretTypeChange() {
|
||||||
|
switch (this.formValues.Type.value) {
|
||||||
|
case KubernetesSecretTypes.OPAQUE.value:
|
||||||
|
case KubernetesSecretTypes.CUSTOM.value:
|
||||||
|
this.formValues.Data = this.formValues.Data.filter((entry) => entry.Value !== '');
|
||||||
|
if (this.formValues.Data.length === 0) {
|
||||||
|
this.addRequiredKeysToForm(['']);
|
||||||
|
}
|
||||||
|
this.state.isDockerConfig = false;
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.SERVICEACCOUNTTOKEN.value:
|
||||||
|
// data isn't required for service account tokens, so remove the data fields if they are empty
|
||||||
|
this.addRequiredKeysToForm([]);
|
||||||
|
this.state.isDockerConfig = false;
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.DOCKERCONFIGJSON.value:
|
||||||
|
this.addRequiredKeysToForm(['.dockerconfigjson']);
|
||||||
|
this.state.isDockerConfig = true;
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.DOCKERCFG.value:
|
||||||
|
this.addRequiredKeysToForm(['.dockercfg']);
|
||||||
|
this.state.isDockerConfig = true;
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.BASICAUTH.value:
|
||||||
|
this.addRequiredKeysToForm(['username', 'password']);
|
||||||
|
this.state.isDockerConfig = false;
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.SSHAUTH.value:
|
||||||
|
this.addRequiredKeysToForm(['ssh-privatekey']);
|
||||||
|
this.state.isDockerConfig = false;
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.TLS.value:
|
||||||
|
this.addRequiredKeysToForm(['tls.crt', 'tls.key']);
|
||||||
|
this.state.isDockerConfig = false;
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.BOOTSTRAPTOKEN.value:
|
||||||
|
this.addRequiredKeysToForm(['token-id', 'token-secret']);
|
||||||
|
this.state.isDockerConfig = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.state.isDockerConfig = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.isFormValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
addRequiredKeysToForm(keys) {
|
||||||
|
// remove data entries that have an empty value
|
||||||
|
this.formValues.Data = this.formValues.Data.filter((entry) => entry.Value);
|
||||||
|
|
||||||
|
keys.forEach((key) => {
|
||||||
|
// if the key doesn't exist on the form, add a new formValues.Data entry
|
||||||
|
if (!this.formValues.Data.some((data) => data.Key === key)) {
|
||||||
|
this.formValues.Data.push(new KubernetesConfigurationFormValuesEntry());
|
||||||
|
const index = this.formValues.Data.length - 1;
|
||||||
|
this.formValues.Data[index].Key = key;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isFormValid() {
|
isFormValid() {
|
||||||
const uniqueCheck = !this.state.alreadyExist && this.state.isDataValid;
|
const [isValid, warningMessage] = isConfigurationFormValid(this.state.alreadyExist, this.state.isDataValid, this.formValues);
|
||||||
if (this.formValues.IsSimple) {
|
this.state.secretWarningMessage = warningMessage;
|
||||||
return this.formValues.Data.length > 0 && uniqueCheck;
|
return isValid;
|
||||||
}
|
|
||||||
return uniqueCheck;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createConfigurationAsync() {
|
async createConfigurationAsync() {
|
||||||
|
@ -47,6 +138,7 @@ class KubernetesCreateConfigurationController {
|
||||||
if (!this.formValues.IsSimple) {
|
if (!this.formValues.IsSimple) {
|
||||||
this.formValues.Data = KubernetesConfigurationHelper.parseYaml(this.formValues);
|
this.formValues.Data = KubernetesConfigurationHelper.parseYaml(this.formValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.KubernetesConfigurationService.create(this.formValues);
|
await this.KubernetesConfigurationService.create(this.formValues);
|
||||||
this.Notifications.success('Success', 'Configuration succesfully created');
|
this.Notifications.success('Success', 'Configuration succesfully created');
|
||||||
this.state.isEditorDirty = false;
|
this.state.isEditorDirty = false;
|
||||||
|
@ -87,10 +179,12 @@ class KubernetesCreateConfigurationController {
|
||||||
alreadyExist: false,
|
alreadyExist: false,
|
||||||
isDataValid: true,
|
isDataValid: true,
|
||||||
isEditorDirty: false,
|
isEditorDirty: false,
|
||||||
|
isDockerConfig: false,
|
||||||
|
secretWarningMessage: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.formValues = new KubernetesConfigurationFormValues();
|
this.formValues = new KubernetesConfigurationFormValues();
|
||||||
this.formValues.Data.push(new KubernetesConfigurationFormValuesEntry());
|
this.formValues.Data = [new KubernetesConfigurationFormValuesEntry()];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resourcePools = await this.KubernetesResourcePoolService.get();
|
const resourcePools = await this.KubernetesResourcePoolService.get();
|
||||||
|
@ -98,6 +192,10 @@ class KubernetesCreateConfigurationController {
|
||||||
|
|
||||||
this.formValues.ResourcePool = this.resourcePools[0];
|
this.formValues.ResourcePool = this.resourcePools[0];
|
||||||
await this.getConfigurations();
|
await this.getConfigurations();
|
||||||
|
|
||||||
|
this.environmentId = this.EndpointProvider.endpointID();
|
||||||
|
this.availableServiceAccounts = await getServiceAccounts(this.environmentId, this.resourcePools[0].Namespace.Name);
|
||||||
|
this.formValues.ServiceAccountName = this.availableServiceAccounts.length > 0 ? this.availableServiceAccounts[0].metadata.name : '';
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.Notifications.error('Failure', err, 'Unable to load view data');
|
this.Notifications.error('Failure', err, 'Unable to load view data');
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -46,9 +46,15 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="!pl-0">Configuration type</td>
|
<td class="!pl-0">Configuration kind</td>
|
||||||
<td>
|
<td>
|
||||||
{{ ctrl.configuration.Type | kubernetesConfigurationTypeText }}
|
{{ ctrl.configuration.Kind | kubernetesConfigurationKindText }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr ng-if="ctrl.secretTypeName">
|
||||||
|
<td class="!pl-0">Secret Type</td>
|
||||||
|
<td>
|
||||||
|
{{ ctrl.secretTypeName }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -99,11 +105,20 @@
|
||||||
<kubernetes-configuration-data
|
<kubernetes-configuration-data
|
||||||
ng-if="ctrl.formValues"
|
ng-if="ctrl.formValues"
|
||||||
form-values="ctrl.formValues"
|
form-values="ctrl.formValues"
|
||||||
|
is-docker-config="ctrl.state.isDockerConfig"
|
||||||
is-valid="ctrl.state.isDataValid"
|
is-valid="ctrl.state.isDataValid"
|
||||||
|
on-change-validation="ctrl.isFormValid()"
|
||||||
is-creation="false"
|
is-creation="false"
|
||||||
is-editor-dirty="ctrl.state.isEditorDirty"
|
is-editor-dirty="ctrl.state.isEditorDirty"
|
||||||
></kubernetes-configuration-data>
|
></kubernetes-configuration-data>
|
||||||
|
|
||||||
|
<div class="form-group" ng-if="ctrl.state.secretWarningMessage">
|
||||||
|
<div class="col-sm-12 small text-warning vertical-center pt-5">
|
||||||
|
<pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon>
|
||||||
|
<span>{{ ctrl.state.secretWarningMessage }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- summary -->
|
<!-- summary -->
|
||||||
<kubernetes-summary-view
|
<kubernetes-summary-view
|
||||||
ng-if="!(!ctrl.isFormValid() || !kubernetesConfigurationCreationForm.$valid || ctrl.state.actionInProgress)"
|
ng-if="!(!ctrl.isFormValid() || !kubernetesConfigurationCreationForm.$valid || ctrl.state.actionInProgress)"
|
||||||
|
@ -122,7 +137,7 @@
|
||||||
button-spinner="ctrl.state.actionInProgress"
|
button-spinner="ctrl.state.actionInProgress"
|
||||||
data-cy="k8sConfigDetail-updateConfig"
|
data-cy="k8sConfigDetail-updateConfig"
|
||||||
>
|
>
|
||||||
<span ng-hide="ctrl.state.actionInProgress">Update {{ ctrl.configuration.Type | kubernetesConfigurationTypeText }}</span>
|
<span ng-hide="ctrl.state.actionInProgress">Update {{ ctrl.configuration.Kind | kubernetesConfigurationKindText }}</span>
|
||||||
<span ng-show="ctrl.state.actionInProgress">Update in progress...</span>
|
<span ng-show="ctrl.state.actionInProgress">Update in progress...</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,12 +2,14 @@ import angular from 'angular';
|
||||||
import _ from 'lodash-es';
|
import _ from 'lodash-es';
|
||||||
|
|
||||||
import { KubernetesConfigurationFormValues } from 'Kubernetes/models/configuration/formvalues';
|
import { KubernetesConfigurationFormValues } from 'Kubernetes/models/configuration/formvalues';
|
||||||
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
import { KubernetesConfigurationKinds, KubernetesSecretTypes } from 'Kubernetes/models/configuration/models';
|
||||||
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
|
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
|
||||||
import KubernetesConfigurationConverter from 'Kubernetes/converters/configuration';
|
import KubernetesConfigurationConverter from 'Kubernetes/converters/configuration';
|
||||||
import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
|
import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
|
||||||
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
||||||
|
|
||||||
|
import { isConfigurationFormValid } from '../validation';
|
||||||
|
|
||||||
class KubernetesConfigurationController {
|
class KubernetesConfigurationController {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -36,7 +38,8 @@ class KubernetesConfigurationController {
|
||||||
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
|
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
|
||||||
this.KubernetesApplicationService = KubernetesApplicationService;
|
this.KubernetesApplicationService = KubernetesApplicationService;
|
||||||
this.KubernetesEventService = KubernetesEventService;
|
this.KubernetesEventService = KubernetesEventService;
|
||||||
this.KubernetesConfigurationTypes = KubernetesConfigurationTypes;
|
this.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
|
||||||
|
this.KubernetesSecretTypes = KubernetesSecretTypes;
|
||||||
this.KubernetesConfigMapService = KubernetesConfigMapService;
|
this.KubernetesConfigMapService = KubernetesConfigMapService;
|
||||||
this.KubernetesSecretService = KubernetesSecretService;
|
this.KubernetesSecretService = KubernetesSecretService;
|
||||||
|
|
||||||
|
@ -76,10 +79,9 @@ class KubernetesConfigurationController {
|
||||||
}
|
}
|
||||||
|
|
||||||
isFormValid() {
|
isFormValid() {
|
||||||
if (this.formValues.IsSimple) {
|
const [isValid, warningMessage] = isConfigurationFormValid(this.state.alreadyExist, this.state.isDataValid, this.formValues);
|
||||||
return this.formValues.Data.length > 0 && this.state.isDataValid;
|
this.state.secretWarningMessage = warningMessage;
|
||||||
}
|
return isValid;
|
||||||
return this.state.isDataValid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: refactor
|
// TODO: refactor
|
||||||
|
@ -89,7 +91,7 @@ class KubernetesConfigurationController {
|
||||||
try {
|
try {
|
||||||
this.state.actionInProgress = true;
|
this.state.actionInProgress = true;
|
||||||
if (
|
if (
|
||||||
this.formValues.Type !== this.configuration.Type ||
|
this.formValues.Kind !== this.configuration.Kind ||
|
||||||
this.formValues.ResourcePool.Namespace.Name !== this.configuration.Namespace ||
|
this.formValues.ResourcePool.Namespace.Name !== this.configuration.Namespace ||
|
||||||
this.formValues.Name !== this.configuration.Name
|
this.formValues.Name !== this.configuration.Name
|
||||||
) {
|
) {
|
||||||
|
@ -153,6 +155,7 @@ class KubernetesConfigurationController {
|
||||||
this.formValues.Id = this.configuration.Id;
|
this.formValues.Id = this.configuration.Id;
|
||||||
this.formValues.Name = this.configuration.Name;
|
this.formValues.Name = this.configuration.Name;
|
||||||
this.formValues.Type = this.configuration.Type;
|
this.formValues.Type = this.configuration.Type;
|
||||||
|
this.formValues.Kind = this.configuration.Kind;
|
||||||
this.oldDataYaml = this.formValues.DataYaml;
|
this.oldDataYaml = this.formValues.DataYaml;
|
||||||
|
|
||||||
return this.configuration;
|
return this.configuration;
|
||||||
|
@ -254,6 +257,8 @@ class KubernetesConfigurationController {
|
||||||
currentName: this.$state.$current.name,
|
currentName: this.$state.$current.name,
|
||||||
isDataValid: true,
|
isDataValid: true,
|
||||||
isEditorDirty: false,
|
isEditorDirty: false,
|
||||||
|
isDockerConfig: false,
|
||||||
|
secretWarningMessage: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.state.activeTab = this.LocalStorage.getActiveTab('configuration');
|
this.state.activeTab = this.LocalStorage.getActiveTab('configuration');
|
||||||
|
@ -267,6 +272,23 @@ class KubernetesConfigurationController {
|
||||||
await this.getEvents(this.configuration.Namespace);
|
await this.getEvents(this.configuration.Namespace);
|
||||||
await this.getConfigurations();
|
await this.getConfigurations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// after loading the configuration, check if it is a docker config secret type
|
||||||
|
if (
|
||||||
|
this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET &&
|
||||||
|
(this.formValues.Type === this.KubernetesSecretTypes.DOCKERCONFIGJSON.value || this.formValues.Type === this.KubernetesSecretTypes.DOCKERCFG.value)
|
||||||
|
) {
|
||||||
|
this.state.isDockerConfig = true;
|
||||||
|
}
|
||||||
|
// convert the secret type to a human readable value
|
||||||
|
if (this.formValues.Type) {
|
||||||
|
const secretTypeValues = Object.values(this.KubernetesSecretTypes);
|
||||||
|
const secretType = secretTypeValues.find((secretType) => secretType.value === this.formValues.Type);
|
||||||
|
this.secretTypeName = secretType ? secretType.name : this.formValues.Type;
|
||||||
|
} else {
|
||||||
|
this.secretTypeName = '';
|
||||||
|
}
|
||||||
|
|
||||||
this.tagUsedDataKeys();
|
this.tagUsedDataKeys();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.Notifications.error('Failure', err, 'Unable to load view data');
|
this.Notifications.error('Failure', err, 'Unable to load view data');
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { KubernetesSecretTypes } from '@/kubernetes/models/configuration/models';
|
||||||
|
import { KubernetesConfigurationKinds } from '@/kubernetes/models/configuration/models';
|
||||||
|
|
||||||
|
export function isConfigurationFormValid(alreadyExist, isDataValid, formValues) {
|
||||||
|
const uniqueCheck = !alreadyExist && isDataValid;
|
||||||
|
let secretWarningMessage = '';
|
||||||
|
let isFormValid = false;
|
||||||
|
|
||||||
|
if (formValues.IsSimple) {
|
||||||
|
if (formValues.Kind === KubernetesConfigurationKinds.SECRET) {
|
||||||
|
let isSecretDataValid = true;
|
||||||
|
const secretTypeValue = typeof formValues.Type === 'string' ? formValues.Type : formValues.Type.value;
|
||||||
|
|
||||||
|
switch (secretTypeValue) {
|
||||||
|
case KubernetesSecretTypes.SERVICEACCOUNTTOKEN.value:
|
||||||
|
// data isn't required for service account tokens
|
||||||
|
isFormValid = uniqueCheck && formValues.ResourcePool;
|
||||||
|
return [isFormValid, ''];
|
||||||
|
case KubernetesSecretTypes.DOCKERCFG.value:
|
||||||
|
// needs to contain a .dockercfg key
|
||||||
|
isSecretDataValid = formValues.Data.some((entry) => entry.Key === '.dockercfg');
|
||||||
|
secretWarningMessage = isSecretDataValid ? '' : 'A data entry with a .dockercfg key is required.';
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.DOCKERCONFIGJSON.value:
|
||||||
|
// needs to contain a .dockerconfigjson key
|
||||||
|
isSecretDataValid = formValues.Data.some((entry) => entry.Key === '.dockerconfigjson');
|
||||||
|
secretWarningMessage = isSecretDataValid ? '' : 'A data entry with a .dockerconfigjson key. is required.';
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.BASICAUTH.value:
|
||||||
|
isSecretDataValid = formValues.Data.some((entry) => entry.Key === 'username' || entry.Key === 'password');
|
||||||
|
secretWarningMessage = isSecretDataValid ? '' : 'A data entry with a username or password key is required.';
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.SSHAUTH.value:
|
||||||
|
isSecretDataValid = formValues.Data.some((entry) => entry.Key === 'ssh-privatekey');
|
||||||
|
secretWarningMessage = isSecretDataValid ? '' : `A data entry with a 'ssh-privatekey' key is required.`;
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.TLS.value:
|
||||||
|
isSecretDataValid = formValues.Data.some((entry) => entry.Key === 'tls.crt') && formValues.Data.some((entry) => entry.Key === 'tls.key');
|
||||||
|
secretWarningMessage = isSecretDataValid ? '' : `Data entries containing a 'tls.crt' key and a 'tls.key' key are required.`;
|
||||||
|
break;
|
||||||
|
case KubernetesSecretTypes.BOOTSTRAPTOKEN.value:
|
||||||
|
isSecretDataValid = formValues.Data.some((entry) => entry.Key === 'token-id') && formValues.Data.some((entry) => entry.Key === 'token-secret');
|
||||||
|
secretWarningMessage = isSecretDataValid ? '' : `Data entries containing a 'token-id' key and a 'token-secret' key are required.`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFormValid = uniqueCheck && formValues.ResourcePool && formValues.Data.length >= 1 && isSecretDataValid;
|
||||||
|
return [isFormValid, secretWarningMessage];
|
||||||
|
}
|
||||||
|
|
||||||
|
isFormValid = formValues.Data.length > 0 && uniqueCheck && formValues.ResourcePool;
|
||||||
|
return [isFormValid, secretWarningMessage];
|
||||||
|
}
|
||||||
|
|
||||||
|
isFormValid = uniqueCheck && formValues.ResourcePool;
|
||||||
|
return [isFormValid, secretWarningMessage];
|
||||||
|
}
|
|
@ -1,13 +1,17 @@
|
||||||
import { KubernetesResourceTypes, KubernetesResourceActions } from 'Kubernetes/models/resource-types/models';
|
import { KubernetesResourceTypes, KubernetesResourceActions } from 'Kubernetes/models/resource-types/models';
|
||||||
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
|
||||||
|
|
||||||
const { CREATE, UPDATE } = KubernetesResourceActions;
|
const { CREATE, UPDATE } = KubernetesResourceActions;
|
||||||
|
|
||||||
export default function (formValues) {
|
export default function (formValues) {
|
||||||
const action = formValues.Id ? UPDATE : CREATE;
|
const action = formValues.Id ? UPDATE : CREATE;
|
||||||
if (formValues.Type === KubernetesConfigurationTypes.CONFIGMAP) {
|
if (formValues.Kind === KubernetesConfigurationKinds.CONFIGMAP) {
|
||||||
return [{ action, kind: KubernetesResourceTypes.CONFIGMAP, name: formValues.Name }];
|
return [{ action, kind: KubernetesResourceTypes.CONFIGMAP, name: formValues.Name }];
|
||||||
} else if (formValues.Type === KubernetesConfigurationTypes.SECRET) {
|
} else if (formValues.Kind === KubernetesConfigurationKinds.SECRET) {
|
||||||
return [{ action, kind: KubernetesResourceTypes.SECRET, name: formValues.Name }];
|
let type = typeof formValues.Type === 'string' ? formValues.Type : formValues.Type.name;
|
||||||
|
if (formValues.customType) {
|
||||||
|
type = formValues.customType;
|
||||||
|
}
|
||||||
|
return [{ action, kind: KubernetesResourceTypes.SECRET, name: formValues.Name, type }];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue