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 {
|
||||
$onInit() {
|
||||
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))
|
||||
.map(([key, value]) => ({ key, value }));
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import _ from 'lodash-es';
|
|||
import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
|
||||
import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
|
||||
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', [
|
||||
'$scope',
|
||||
|
@ -112,7 +112,7 @@ angular.module('portainer.docker').controller('KubernetesApplicationsDatatableCo
|
|||
};
|
||||
|
||||
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">
|
||||
<!-- table title and action menu -->
|
||||
<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="widget-icon space-right">
|
||||
<pr-icon icon="'lock'" feather="true"></pr-icon>
|
||||
|
@ -125,11 +125,11 @@
|
|||
</th>
|
||||
<th>
|
||||
<table-column-header
|
||||
col-title="'Type'"
|
||||
col-title="'Kind'"
|
||||
can-sort="true"
|
||||
is-sorted="$ctrl.state.orderBy === 'Type'"
|
||||
is-sorted-desc="$ctrl.state.orderBy === 'Type' && $ctrl.state.reverseOrder"
|
||||
ng-click="$ctrl.changeOrderBy('Type')"
|
||||
is-sorted="$ctrl.state.orderBy === 'Kind'"
|
||||
is-sorted-desc="$ctrl.state.orderBy === 'Kind' && $ctrl.state.reverseOrder"
|
||||
ng-click="$ctrl.changeOrderBy('Kind')"
|
||||
></table-column-header>
|
||||
</th>
|
||||
<th>
|
||||
|
@ -160,7 +160,7 @@
|
|||
<td>
|
||||
<a ui-sref="kubernetes.resourcePools.resourcePool({ id: item.Namespace })">{{ item.Namespace }}</a>
|
||||
</td>
|
||||
<td>{{ item.Type | kubernetesConfigurationTypeText }}</td>
|
||||
<td>{{ item.Kind | kubernetesConfigurationKindText }}</td>
|
||||
<td>{{ item.CreationDate | getisodate }} {{ item.ConfigurationOwner ? 'by ' + item.ConfigurationOwner : '' }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!$ctrl.dataset">
|
||||
|
|
|
@ -21,33 +21,70 @@
|
|||
</div>
|
||||
|
||||
<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">
|
||||
<pr-icon class="vertical-center" icon="'plus'" feather="true"></pr-icon> Create entry
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" ngf-select="$ctrl.addEntryFromFile($file)" style="margin-left: 0" data-cy="k8sConfigCreate-createConfigsFromFileButton">
|
||||
<pr-icon class="vertical-center" icon="'upload'" feather="true"></pr-icon> Create key/value from file
|
||||
<button
|
||||
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
|
||||
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 ng-repeat="(index, entry) in $ctrl.formValues.Data" ng-if="$ctrl.formValues.IsSimple">
|
||||
<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">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
maxlength="253"
|
||||
id="configuration_data_key_{{ index }}"
|
||||
name="configuration_data_key_{{ index }}"
|
||||
ng-model="$ctrl.formValues.Data[index].Key"
|
||||
ng-disabled="entry.Used"
|
||||
ng-disabled="entry.Used || $ctrl.isRequiredKey(entry.Key)"
|
||||
required
|
||||
ng-change="$ctrl.onChangeKey(entry)"
|
||||
/>
|
||||
<div
|
||||
class="small text-warning"
|
||||
style="margin-top: 5px"
|
||||
class="small text-warning mt-1"
|
||||
ng-show="
|
||||
kubernetesConfigurationDataCreationForm['configuration_data_key_' + index].$invalid ||
|
||||
(!entry.Used && $ctrl.state.duplicateKeys[index] !== undefined) ||
|
||||
|
@ -55,18 +92,16 @@
|
|||
"
|
||||
>
|
||||
<ng-messages for="kubernetesConfigurationDataCreationForm['configuration_data_key_' + index].$error">
|
||||
<p ng-message="required" class="vertical-center">
|
||||
<pr-icon class="vertical-center" icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> This field is required.
|
||||
</p>
|
||||
<p ng-message="required" class="vertical-center"> <pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon> This field is required. </p>
|
||||
</ng-messages>
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
<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
|
||||
consist of alphanumeric characters, '-', '_' or '.'
|
||||
<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
|
||||
characters, '-', '_' or '.'
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -106,10 +141,11 @@
|
|||
<div class="col-sm-3 col-lg-2"></div>
|
||||
<div class="col-sm-8">
|
||||
<button
|
||||
ng-if="!$ctrl.isRequiredKey(entry.Key) || $ctrl.state.duplicateKeys[index] !== undefined"
|
||||
type="button"
|
||||
class="btn btn-sm btn-dangerlight !ml-0"
|
||||
style="margin-left: 0"
|
||||
ng-disabled="entry.Used"
|
||||
ng-disabled="entry.Used || $ctrl.isEntryRequired()"
|
||||
ng-click="$ctrl.removeEntry(index, entry)"
|
||||
data-cy="k8sConfigDetail-removeEntryButton{{ index }}"
|
||||
>
|
||||
|
|
|
@ -3,6 +3,8 @@ angular.module('portainer.kubernetes').component('kubernetesConfigurationData',
|
|||
controller: 'KubernetesConfigurationDataController',
|
||||
bindings: {
|
||||
formValues: '=',
|
||||
isDockerConfig: '=',
|
||||
onChangeValidation: '&',
|
||||
isValid: '=',
|
||||
isCreation: '=',
|
||||
isEditorDirty: '=',
|
||||
|
|
|
@ -6,11 +6,12 @@ import { Base64 } from 'js-base64';
|
|||
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper';
|
||||
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
|
||||
import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
|
||||
import { KubernetesConfigurationKinds, KubernetesSecretTypes } from 'Kubernetes/models/configuration/models';
|
||||
|
||||
class KubernetesConfigurationDataController {
|
||||
/* @ngInject */
|
||||
constructor($async) {
|
||||
this.$async = $async;
|
||||
constructor($async, Notifications) {
|
||||
Object.assign(this, { $async, Notifications });
|
||||
|
||||
this.editorUpdate = this.editorUpdate.bind(this);
|
||||
this.editorUpdateAsync = this.editorUpdateAsync.bind(this);
|
||||
|
@ -18,6 +19,8 @@ class KubernetesConfigurationDataController {
|
|||
this.onFileLoadAsync = this.onFileLoadAsync.bind(this);
|
||||
this.showSimpleMode = this.showSimpleMode.bind(this);
|
||||
this.showAdvancedMode = this.showAdvancedMode.bind(this);
|
||||
this.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
|
||||
this.KubernetesSecretTypes = KubernetesSecretTypes;
|
||||
}
|
||||
|
||||
onChangeKey(entry) {
|
||||
|
@ -25,6 +28,8 @@ class KubernetesConfigurationDataController {
|
|||
return;
|
||||
}
|
||||
|
||||
this.onChangeValidation();
|
||||
|
||||
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.isValid = Object.keys(this.state.duplicateKeys).length === 0 && Object.keys(this.state.invalidKeys).length === 0;
|
||||
|
@ -32,6 +37,85 @@ class KubernetesConfigurationDataController {
|
|||
|
||||
addEntry() {
|
||||
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) {
|
||||
|
@ -55,24 +139,77 @@ class KubernetesConfigurationDataController {
|
|||
}
|
||||
|
||||
async onFileLoadAsync(event) {
|
||||
const entry = new KubernetesConfigurationFormValuesEntry();
|
||||
const encoding = chardet.detect(Buffer.from(event.target.result));
|
||||
const decoder = new TextDecoder(encoding);
|
||||
|
||||
entry.Key = event.target.fileName;
|
||||
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);
|
||||
// exit if the file is too big
|
||||
const maximumFileSizeBytes = 1024 * 1024; // 1MB
|
||||
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.');
|
||||
return;
|
||||
}
|
||||
|
||||
const entry = new KubernetesConfigurationFormValuesEntry();
|
||||
try {
|
||||
const encoding = chardet.detect(Buffer.from(event.target.result));
|
||||
const decoder = new TextDecoder(encoding);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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) {
|
||||
return this.$async(this.onFileLoadAsync, event);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import _ from 'lodash-es';
|
||||
import { KubernetesConfiguration, KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
||||
import { KubernetesConfiguration, KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
|
||||
|
||||
class KubernetesConfigurationConverter {
|
||||
static secretToConfiguration(secret) {
|
||||
const res = new KubernetesConfiguration();
|
||||
res.Type = KubernetesConfigurationTypes.SECRET;
|
||||
res.Kind = KubernetesConfigurationKinds.SECRET;
|
||||
res.Id = secret.Id;
|
||||
res.Name = secret.Name;
|
||||
res.Type = secret.Type;
|
||||
res.Namespace = secret.Namespace;
|
||||
res.CreationDate = secret.CreationDate;
|
||||
res.Yaml = secret.Yaml;
|
||||
|
@ -21,7 +22,7 @@ class KubernetesConfigurationConverter {
|
|||
|
||||
static configMapToConfiguration(configMap) {
|
||||
const res = new KubernetesConfiguration();
|
||||
res.Type = KubernetesConfigurationTypes.CONFIGMAP;
|
||||
res.Kind = KubernetesConfigurationKinds.CONFIGMAP;
|
||||
res.Id = configMap.Id;
|
||||
res.Name = configMap.Name;
|
||||
res.Namespace = configMap.Namespace;
|
||||
|
|
|
@ -4,12 +4,13 @@ import { KubernetesApplicationSecret } from 'Kubernetes/models/secret/models';
|
|||
import { KubernetesPortainerConfigurationDataAnnotation } from 'Kubernetes/models/configuration/models';
|
||||
import { KubernetesPortainerConfigurationOwnerLabel } from 'Kubernetes/models/configuration/models';
|
||||
import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
|
||||
|
||||
import { KubernetesSecretTypes } from 'Kubernetes/models/configuration/models';
|
||||
class KubernetesSecretConverter {
|
||||
static createPayload(secret) {
|
||||
const res = new KubernetesSecretCreatePayload();
|
||||
res.metadata.name = secret.Name;
|
||||
res.metadata.namespace = secret.Namespace;
|
||||
res.type = secret.Type.value;
|
||||
const configurationOwner = _.truncate(secret.ConfigurationOwner, { length: 63, omission: '' });
|
||||
res.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] = configurationOwner;
|
||||
|
||||
|
@ -25,6 +26,11 @@ class KubernetesSecretConverter {
|
|||
if (annotation !== '') {
|
||||
res.metadata.annotations[KubernetesPortainerConfigurationDataAnnotation] = annotation;
|
||||
}
|
||||
|
||||
_.forEach(secret.Annotations, (entry) => {
|
||||
res.metadata.annotations[entry.name] = entry.value;
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -32,6 +38,7 @@ class KubernetesSecretConverter {
|
|||
const res = new KubernetesSecretUpdatePayload();
|
||||
res.metadata.name = secret.Name;
|
||||
res.metadata.namespace = secret.Namespace;
|
||||
res.type = secret.Type;
|
||||
res.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] = secret.ConfigurationOwner;
|
||||
|
||||
let annotation = '';
|
||||
|
@ -54,6 +61,7 @@ class KubernetesSecretConverter {
|
|||
res.Id = payload.metadata.uid;
|
||||
res.Name = payload.metadata.name;
|
||||
res.Namespace = payload.metadata.namespace;
|
||||
res.Type = payload.type;
|
||||
res.ConfigurationOwner = payload.metadata.labels ? payload.metadata.labels[KubernetesPortainerConfigurationOwnerLabel] : '';
|
||||
res.CreationDate = payload.metadata.creationTimestamp;
|
||||
|
||||
|
@ -84,8 +92,19 @@ class KubernetesSecretConverter {
|
|||
const res = new KubernetesApplicationSecret();
|
||||
res.Name = formValues.Name;
|
||||
res.Namespace = formValues.ResourcePool.Namespace.Name;
|
||||
res.Type = formValues.Type;
|
||||
res.ConfigurationOwner = formValues.ConfigurationOwner;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
return function (type) {
|
||||
switch (type) {
|
||||
case KubernetesConfigurationTypes.SECRET:
|
||||
case KubernetesConfigurationKinds.SECRET:
|
||||
return 'Secret';
|
||||
case KubernetesConfigurationTypes.CONFIGMAP:
|
||||
case KubernetesConfigurationKinds.CONFIGMAP:
|
||||
return 'ConfigMap';
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import _ from 'lodash-es';
|
||||
import { KubernetesPortMapping, KubernetesPortMappingPort } from 'Kubernetes/models/port/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 {
|
||||
KubernetesApplicationAutoScalerFormValue,
|
||||
KubernetesApplicationConfigurationFormValue,
|
||||
|
@ -147,7 +147,7 @@ class KubernetesApplicationHelper {
|
|||
/* #region CONFIGURATIONS FV <> ENV & VOLUMES */
|
||||
static generateConfigurationFormValuesFromEnvAndVolumes(env, volumes, configurations) {
|
||||
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 cfgVol = _.filter(volumes, { configurationName: cfg.Name });
|
||||
|
@ -207,7 +207,7 @@ class KubernetesApplicationHelper {
|
|||
let finalMounts = [];
|
||||
|
||||
_.forEach(configurations, (config) => {
|
||||
const isBasic = config.SelectedConfiguration.Type === KubernetesConfigurationTypes.CONFIGMAP;
|
||||
const isBasic = config.SelectedConfiguration.Type === KubernetesConfigurationKinds.CONFIGMAP;
|
||||
|
||||
if (!config.Overriden) {
|
||||
const envKeys = _.keys(config.SelectedConfiguration.Data);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import _ from 'lodash-es';
|
||||
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';
|
||||
|
||||
class KubernetesConfigurationHelper {
|
||||
|
@ -8,7 +8,7 @@ class KubernetesConfigurationHelper {
|
|||
return _.filter(applications, (app) => {
|
||||
let envFind;
|
||||
let volumeFind;
|
||||
if (config.Type === KubernetesConfigurationTypes.CONFIGMAP) {
|
||||
if (config.Type === KubernetesConfigurationKinds.CONFIGMAP) {
|
||||
envFind = _.find(app.Env, { valueFrom: { configMapKeyRef: { name: config.Name } } });
|
||||
volumeFind = _.find(app.Volumes, { configMap: { name: config.Name } });
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { KubernetesConfigurationTypes } from './models';
|
||||
import { KubernetesConfigurationKinds, KubernetesSecretTypes } from './models';
|
||||
|
||||
/**
|
||||
* KubernetesConfigurationFormValues Model
|
||||
|
@ -8,15 +8,17 @@ const _KubernetesConfigurationFormValues = Object.freeze({
|
|||
ResourcePool: '',
|
||||
Name: '',
|
||||
ConfigurationOwner: '',
|
||||
Type: KubernetesConfigurationTypes.CONFIGMAP,
|
||||
Kind: KubernetesConfigurationKinds.CONFIGMAP,
|
||||
Data: [],
|
||||
DataYaml: '',
|
||||
IsSimple: true,
|
||||
ServiceAccountName: '',
|
||||
Type: KubernetesSecretTypes.OPAQUE,
|
||||
});
|
||||
|
||||
export class KubernetesConfigurationFormValues {
|
||||
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({
|
||||
Id: 0,
|
||||
Name: '',
|
||||
Type: '',
|
||||
Kind: '',
|
||||
Namespace: '',
|
||||
CreationDate: '',
|
||||
ConfigurationOwner: '',
|
||||
|
@ -23,7 +23,19 @@ export class KubernetesConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
export const KubernetesConfigurationTypes = Object.freeze({
|
||||
export const KubernetesConfigurationKinds = Object.freeze({
|
||||
CONFIGMAP: 1,
|
||||
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,
|
||||
Name: '',
|
||||
Namespace: '',
|
||||
Type: '',
|
||||
CreationDate: '',
|
||||
ConfigurationOwner: '',
|
||||
Yaml: '',
|
||||
Data: [],
|
||||
SecretType: '',
|
||||
Annotations: [],
|
||||
});
|
||||
|
||||
export class KubernetesApplicationSecret {
|
||||
|
|
|
@ -5,7 +5,7 @@ import { KubernetesCommonMetadataPayload } from 'Kubernetes/models/common/payloa
|
|||
*/
|
||||
const _KubernetesSecretCreatePayload = Object.freeze({
|
||||
metadata: new KubernetesCommonMetadataPayload(),
|
||||
type: 'Opaque',
|
||||
type: '',
|
||||
data: {},
|
||||
stringData: {},
|
||||
});
|
||||
|
@ -21,7 +21,7 @@ export class KubernetesSecretCreatePayload {
|
|||
*/
|
||||
const _KubernetesSecretUpdatePayload = Object.freeze({
|
||||
metadata: new KubernetesCommonMetadataPayload(),
|
||||
type: 'Opaque',
|
||||
type: '',
|
||||
data: {},
|
||||
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 KubernetesConfigMapConverter from 'Kubernetes/converters/configMap';
|
||||
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';
|
||||
|
||||
class KubernetesConfigurationService {
|
||||
|
@ -62,7 +62,7 @@ class KubernetesConfigurationService {
|
|||
async createAsync(formValues) {
|
||||
formValues.ConfigurationOwner = KubernetesCommonHelper.ownerToLabel(formValues.ConfigurationOwner);
|
||||
|
||||
if (formValues.Type === KubernetesConfigurationTypes.CONFIGMAP) {
|
||||
if (formValues.Kind === KubernetesConfigurationKinds.CONFIGMAP) {
|
||||
const configMap = KubernetesConfigMapConverter.configurationFormValuesToConfigMap(formValues);
|
||||
await this.KubernetesConfigMapService.create(configMap);
|
||||
} else {
|
||||
|
@ -79,7 +79,7 @@ class KubernetesConfigurationService {
|
|||
* UPDATE
|
||||
*/
|
||||
async updateAsync(formValues, configuration) {
|
||||
if (formValues.Type === KubernetesConfigurationTypes.CONFIGMAP) {
|
||||
if (formValues.Kind === KubernetesConfigurationKinds.CONFIGMAP) {
|
||||
const configMap = KubernetesConfigMapConverter.configurationFormValuesToConfigMap(formValues);
|
||||
configMap.ConfigurationOwner = configuration.ConfigurationOwner;
|
||||
await this.KubernetesConfigMapService.update(configMap);
|
||||
|
@ -98,7 +98,7 @@ class KubernetesConfigurationService {
|
|||
* DELETE
|
||||
*/
|
||||
async deleteAsync(config) {
|
||||
if (config.Type === KubernetesConfigurationTypes.CONFIGMAP) {
|
||||
if (config.Kind == KubernetesConfigurationKinds.CONFIGMAP) {
|
||||
await this.KubernetesConfigMapService.delete(config);
|
||||
} else {
|
||||
await this.KubernetesSecretService.delete(config);
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
<rd-widget-body>
|
||||
<form class="form-horizontal" name="kubernetesConfigurationCreationForm" autocomplete="off">
|
||||
<!-- 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>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<div class="col-sm-8 col-lg-9 mb-0">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
|
@ -29,21 +29,21 @@
|
|||
required
|
||||
data-cy="k8sConfigCreate-nameInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="kubernetesConfigurationCreationForm.configuration_name.$invalid || ctrl.state.alreadyExist">
|
||||
<div class="col-sm-3 col-lg-2"></div>
|
||||
<div class="col-sm-8 col-lg-9 small text-warning">
|
||||
<div ng-messages="kubernetesConfigurationCreationForm.configuration_name.$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 ng-show="kubernetesConfigurationCreationForm.configuration_name.$invalid || ctrl.state.alreadyExist">
|
||||
<div class="help-block small text-warning">
|
||||
<div ng-messages="kubernetesConfigurationCreationForm.configuration_name.$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>
|
||||
<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>
|
||||
<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>
|
||||
<!-- !name -->
|
||||
|
@ -80,56 +80,155 @@
|
|||
<!-- !resource-pool -->
|
||||
|
||||
<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="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>
|
||||
|
||||
<!-- type options -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<div class="boxselector_wrapper">
|
||||
<div>
|
||||
<input type="radio" id="type_basic" ng-value="ctrl.KubernetesConfigurationTypes.CONFIGMAP" ng-model="ctrl.formValues.Type" />
|
||||
<label for="type_basic" data-cy="k8sConfigCreate-nonSensitiveButton">
|
||||
<div class="boxselector_header">
|
||||
<pr-icon icon="'svg-filecode'"></pr-icon>
|
||||
ConfigMap
|
||||
</div>
|
||||
<p>This configuration holds non-sensitive information</p>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="type_secret" ng-value="ctrl.KubernetesConfigurationTypes.SECRET" ng-model="ctrl.formValues.Type" />
|
||||
<label for="type_secret" data-cy="k8sConfigCreate-sensitiveButton">
|
||||
<div class="boxselector_header">
|
||||
<pr-icon icon="'lock'" feather="true"></pr-icon>
|
||||
Secret
|
||||
</div>
|
||||
<p>This configuration holds sensitive information</p>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group px-[15px] mb-0">
|
||||
<div class="boxselector_wrapper">
|
||||
<div>
|
||||
<input type="radio" id="type_basic" ng-value="ctrl.KubernetesConfigurationKinds.CONFIGMAP" ng-model="ctrl.formValues.Kind" ng-change="ctrl.onChangeKind()" />
|
||||
<label for="type_basic" data-cy="k8sConfigCreate-nonSensitiveButton">
|
||||
<div class="boxselector_header">
|
||||
<pr-icon icon="'svg-filecode'"></pr-icon>
|
||||
ConfigMap
|
||||
</div>
|
||||
<p>This configuration holds non-sensitive information</p>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="type_secret" ng-value="ctrl.KubernetesConfigurationKinds.SECRET" ng-model="ctrl.formValues.Kind" ng-change="ctrl.onChangeKind()" />
|
||||
<label for="type_secret" data-cy="k8sConfigCreate-sensitiveButton">
|
||||
<div class="boxselector_header">
|
||||
<pr-icon icon="'lock'" feather="true"></pr-icon>
|
||||
Secret
|
||||
</div>
|
||||
<p>This configuration holds sensitive information</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !type options -->
|
||||
|
||||
<div class="col-sm-12 form-section-title" ng-if="ctrl.formValues.Type == ctrl.KubernetesConfigurationTypes.SECRET"> Information </div>
|
||||
<div class="form-group" ng-if="ctrl.formValues.Type == ctrl.KubernetesConfigurationTypes.SECRET">
|
||||
<div class="col-sm-12 small text-muted">
|
||||
Creating a sensitive configuration will create a Kubernetes Secret of type <code>Opaque</code>. You can find more information about this in the
|
||||
<a href="https://kubernetes.io/docs/concepts/configuration/secret/#secret-types" target="_blank">official documentation</a>.
|
||||
<div ng-if="ctrl.formValues.Kind === ctrl.KubernetesConfigurationKinds.SECRET">
|
||||
<div class="col-sm-12 form-section-title"> Information </div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 small text-muted vertical-center">
|
||||
<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>
|
||||
|
||||
<kubernetes-configuration-data
|
||||
ng-if="ctrl.formValues"
|
||||
form-values="ctrl.formValues"
|
||||
is-docker-config="ctrl.state.isDockerConfig"
|
||||
is-valid="ctrl.state.isDataValid"
|
||||
on-change-validation="ctrl.isFormValid()"
|
||||
is-creation="true"
|
||||
is-editor-dirty="ctrl.state.isEditorDirty"
|
||||
></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>
|
||||
|
||||
<!-- summary -->
|
||||
|
@ -150,7 +249,7 @@
|
|||
button-spinner="ctrl.state.actionInProgress"
|
||||
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>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,43 +1,134 @@
|
|||
import angular from 'angular';
|
||||
import _ from 'lodash-es';
|
||||
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 KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
||||
import { getServiceAccounts } from 'Kubernetes/rest/serviceAccount';
|
||||
|
||||
import { isConfigurationFormValid } from '../validation';
|
||||
|
||||
class KubernetesCreateConfigurationController {
|
||||
/* @ngInject */
|
||||
constructor($async, $state, $window, ModalService, Notifications, Authentication, KubernetesConfigurationService, KubernetesResourcePoolService) {
|
||||
constructor($async, $state, $window, ModalService, Notifications, Authentication, KubernetesConfigurationService, KubernetesResourcePoolService, EndpointProvider) {
|
||||
this.$async = $async;
|
||||
this.$state = $state;
|
||||
this.$window = $window;
|
||||
this.EndpointProvider = EndpointProvider;
|
||||
this.ModalService = ModalService;
|
||||
this.Notifications = Notifications;
|
||||
this.Authentication = Authentication;
|
||||
this.KubernetesConfigurationService = KubernetesConfigurationService;
|
||||
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
|
||||
this.KubernetesConfigurationTypes = KubernetesConfigurationTypes;
|
||||
this.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
|
||||
this.KubernetesSecretTypes = KubernetesSecretTypes;
|
||||
|
||||
this.onInit = this.onInit.bind(this);
|
||||
this.createConfigurationAsync = this.createConfigurationAsync.bind(this);
|
||||
this.getConfigurationsAsync = this.getConfigurationsAsync.bind(this);
|
||||
this.onResourcePoolSelectionChangeAsync = this.onResourcePoolSelectionChangeAsync.bind(this);
|
||||
this.onSecretTypeChange = this.onSecretTypeChange.bind(this);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
onResourcePoolSelectionChange() {
|
||||
onChangeKind() {
|
||||
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() {
|
||||
const uniqueCheck = !this.state.alreadyExist && this.state.isDataValid;
|
||||
if (this.formValues.IsSimple) {
|
||||
return this.formValues.Data.length > 0 && uniqueCheck;
|
||||
}
|
||||
return uniqueCheck;
|
||||
const [isValid, warningMessage] = isConfigurationFormValid(this.state.alreadyExist, this.state.isDataValid, this.formValues);
|
||||
this.state.secretWarningMessage = warningMessage;
|
||||
return isValid;
|
||||
}
|
||||
|
||||
async createConfigurationAsync() {
|
||||
|
@ -47,6 +138,7 @@ class KubernetesCreateConfigurationController {
|
|||
if (!this.formValues.IsSimple) {
|
||||
this.formValues.Data = KubernetesConfigurationHelper.parseYaml(this.formValues);
|
||||
}
|
||||
|
||||
await this.KubernetesConfigurationService.create(this.formValues);
|
||||
this.Notifications.success('Success', 'Configuration succesfully created');
|
||||
this.state.isEditorDirty = false;
|
||||
|
@ -87,10 +179,12 @@ class KubernetesCreateConfigurationController {
|
|||
alreadyExist: false,
|
||||
isDataValid: true,
|
||||
isEditorDirty: false,
|
||||
isDockerConfig: false,
|
||||
secretWarningMessage: '',
|
||||
};
|
||||
|
||||
this.formValues = new KubernetesConfigurationFormValues();
|
||||
this.formValues.Data.push(new KubernetesConfigurationFormValuesEntry());
|
||||
this.formValues.Data = [new KubernetesConfigurationFormValuesEntry()];
|
||||
|
||||
try {
|
||||
const resourcePools = await this.KubernetesResourcePoolService.get();
|
||||
|
@ -98,6 +192,10 @@ class KubernetesCreateConfigurationController {
|
|||
|
||||
this.formValues.ResourcePool = this.resourcePools[0];
|
||||
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) {
|
||||
this.Notifications.error('Failure', err, 'Unable to load view data');
|
||||
} finally {
|
||||
|
|
|
@ -46,9 +46,15 @@
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="!pl-0">Configuration type</td>
|
||||
<td class="!pl-0">Configuration kind</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>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -99,11 +105,20 @@
|
|||
<kubernetes-configuration-data
|
||||
ng-if="ctrl.formValues"
|
||||
form-values="ctrl.formValues"
|
||||
is-docker-config="ctrl.state.isDockerConfig"
|
||||
is-valid="ctrl.state.isDataValid"
|
||||
on-change-validation="ctrl.isFormValid()"
|
||||
is-creation="false"
|
||||
is-editor-dirty="ctrl.state.isEditorDirty"
|
||||
></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 -->
|
||||
<kubernetes-summary-view
|
||||
ng-if="!(!ctrl.isFormValid() || !kubernetesConfigurationCreationForm.$valid || ctrl.state.actionInProgress)"
|
||||
|
@ -122,7 +137,7 @@
|
|||
button-spinner="ctrl.state.actionInProgress"
|
||||
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>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -2,12 +2,14 @@ import angular from 'angular';
|
|||
import _ from 'lodash-es';
|
||||
|
||||
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 KubernetesConfigurationConverter from 'Kubernetes/converters/configuration';
|
||||
import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
|
||||
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
||||
|
||||
import { isConfigurationFormValid } from '../validation';
|
||||
|
||||
class KubernetesConfigurationController {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
|
@ -36,7 +38,8 @@ class KubernetesConfigurationController {
|
|||
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
|
||||
this.KubernetesApplicationService = KubernetesApplicationService;
|
||||
this.KubernetesEventService = KubernetesEventService;
|
||||
this.KubernetesConfigurationTypes = KubernetesConfigurationTypes;
|
||||
this.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
|
||||
this.KubernetesSecretTypes = KubernetesSecretTypes;
|
||||
this.KubernetesConfigMapService = KubernetesConfigMapService;
|
||||
this.KubernetesSecretService = KubernetesSecretService;
|
||||
|
||||
|
@ -76,10 +79,9 @@ class KubernetesConfigurationController {
|
|||
}
|
||||
|
||||
isFormValid() {
|
||||
if (this.formValues.IsSimple) {
|
||||
return this.formValues.Data.length > 0 && this.state.isDataValid;
|
||||
}
|
||||
return this.state.isDataValid;
|
||||
const [isValid, warningMessage] = isConfigurationFormValid(this.state.alreadyExist, this.state.isDataValid, this.formValues);
|
||||
this.state.secretWarningMessage = warningMessage;
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// TODO: refactor
|
||||
|
@ -89,7 +91,7 @@ class KubernetesConfigurationController {
|
|||
try {
|
||||
this.state.actionInProgress = true;
|
||||
if (
|
||||
this.formValues.Type !== this.configuration.Type ||
|
||||
this.formValues.Kind !== this.configuration.Kind ||
|
||||
this.formValues.ResourcePool.Namespace.Name !== this.configuration.Namespace ||
|
||||
this.formValues.Name !== this.configuration.Name
|
||||
) {
|
||||
|
@ -153,6 +155,7 @@ class KubernetesConfigurationController {
|
|||
this.formValues.Id = this.configuration.Id;
|
||||
this.formValues.Name = this.configuration.Name;
|
||||
this.formValues.Type = this.configuration.Type;
|
||||
this.formValues.Kind = this.configuration.Kind;
|
||||
this.oldDataYaml = this.formValues.DataYaml;
|
||||
|
||||
return this.configuration;
|
||||
|
@ -254,6 +257,8 @@ class KubernetesConfigurationController {
|
|||
currentName: this.$state.$current.name,
|
||||
isDataValid: true,
|
||||
isEditorDirty: false,
|
||||
isDockerConfig: false,
|
||||
secretWarningMessage: '',
|
||||
};
|
||||
|
||||
this.state.activeTab = this.LocalStorage.getActiveTab('configuration');
|
||||
|
@ -267,6 +272,23 @@ class KubernetesConfigurationController {
|
|||
await this.getEvents(this.configuration.Namespace);
|
||||
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();
|
||||
} catch (err) {
|
||||
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 { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
|
||||
import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
|
||||
|
||||
const { CREATE, UPDATE } = KubernetesResourceActions;
|
||||
|
||||
export default function (formValues) {
|
||||
const action = formValues.Id ? UPDATE : CREATE;
|
||||
if (formValues.Type === KubernetesConfigurationTypes.CONFIGMAP) {
|
||||
if (formValues.Kind === KubernetesConfigurationKinds.CONFIGMAP) {
|
||||
return [{ action, kind: KubernetesResourceTypes.CONFIGMAP, name: formValues.Name }];
|
||||
} else if (formValues.Type === KubernetesConfigurationTypes.SECRET) {
|
||||
return [{ action, kind: KubernetesResourceTypes.SECRET, name: formValues.Name }];
|
||||
} else if (formValues.Kind === KubernetesConfigurationKinds.SECRET) {
|
||||
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