1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 20:35:25 +02:00

feat(secrets): allow creating secrets beyond opaque [EE-2625] (#7709)

This commit is contained in:
Ali 2022-09-23 16:35:47 +12:00 committed by GitHub
parent 3b2f0ff9eb
commit 4e20d70a99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 659 additions and 135 deletions

View file

@ -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>

View file

@ -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 {

View file

@ -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>

View file

@ -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');

View file

@ -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];
}