fix(secrets): allow edit sa token, refactor (#7732)

pull/7752/head
Ali 2022-09-29 09:57:39 +13:00 committed by GitHub
parent cb79dc18f8
commit a1a88eb5e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 78 additions and 78 deletions

View File

@ -27,10 +27,7 @@
</button> </button>
<button <button
ng-if=" ng-if="
!( !(($ctrl.isDockerConfig || $ctrl.formValues.Type === $ctrl.KubernetesSecretTypeOptions.TLS.value) && $ctrl.formValues.Kind === $ctrl.KubernetesConfigurationKinds.SECRET)
($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" type="button"
class="btn btn-sm btn-default ml-0" class="btn btn-sm btn-default ml-0"
@ -50,10 +47,7 @@
<pr-icon icon="'upload'" feather="true" class="vertical-center"></pr-icon> Upload docker config file <pr-icon icon="'upload'" feather="true" class="vertical-center"></pr-icon> Upload docker config file
</button> </button>
<button <button
ng-if=" ng-if="$ctrl.formValues.Type === $ctrl.KubernetesSecretTypeOptions.TLS.value && $ctrl.formValues.Kind === $ctrl.KubernetesConfigurationKinds.SECRET"
($ctrl.formValues.Type.name === $ctrl.KubernetesSecretTypes.TLS.name || $ctrl.formValues.Type === $ctrl.KubernetesSecretTypes.TLS.value) &&
$ctrl.formValues.Kind === $ctrl.KubernetesConfigurationKinds.SECRET
"
type="button" type="button"
class="btn btn-sm btn-default ml-0" class="btn btn-sm btn-default ml-0"
ngf-select="$ctrl.addEntryFromFile($file)" ngf-select="$ctrl.addEntryFromFile($file)"

View File

@ -6,7 +6,7 @@ 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'; import { KubernetesConfigurationKinds, KubernetesSecretTypeOptions } from 'Kubernetes/models/configuration/models';
class KubernetesConfigurationDataController { class KubernetesConfigurationDataController {
/* @ngInject */ /* @ngInject */
@ -20,7 +20,7 @@ class KubernetesConfigurationDataController {
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.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
this.KubernetesSecretTypes = KubernetesSecretTypes; this.KubernetesSecretTypeOptions = KubernetesSecretTypeOptions;
} }
onChangeKey(entry) { onChangeKey(entry) {
@ -41,27 +41,26 @@ class KubernetesConfigurationDataController {
// logic for setting required keys for new entries, based on the secret type // logic for setting required keys for new entries, based on the secret type
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) { if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
const newDataIndex = this.formValues.Data.length - 1; const newDataIndex = this.formValues.Data.length - 1;
const typeValue = typeof this.formValues.Type === 'string' ? this.formValues.Type : this.formValues.Type.value; switch (this.formValues.Type) {
switch (typeValue) { case this.KubernetesSecretTypeOptions.DOCKERCFG.value:
case this.KubernetesSecretTypes.DOCKERCFG.value:
this.addMissingKeys(['dockercfg'], newDataIndex); this.addMissingKeys(['dockercfg'], newDataIndex);
break; break;
case this.KubernetesSecretTypes.DOCKERCONFIGJSON.value: case this.KubernetesSecretTypeOptions.DOCKERCONFIGJSON.value:
this.addMissingKeys(['.dockerconfigjson'], newDataIndex); this.addMissingKeys(['.dockerconfigjson'], newDataIndex);
break; break;
case this.KubernetesSecretTypes.BASICAUTH.value: case this.KubernetesSecretTypeOptions.BASICAUTH.value:
// only add a required key if there is no required key out of username and password // 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')) { if (!this.formValues.Data.some((entry) => entry.Key === 'username' || entry.Key === 'password')) {
this.addMissingKeys(['username', 'password'], newDataIndex); this.addMissingKeys(['username', 'password'], newDataIndex);
} }
break; break;
case this.KubernetesSecretTypes.SSHAUTH.value: case this.KubernetesSecretTypeOptions.SSHAUTH.value:
this.addMissingKeys(['ssh-privatekey'], newDataIndex); this.addMissingKeys(['ssh-privatekey'], newDataIndex);
break; break;
case this.KubernetesSecretTypes.TLS.value: case this.KubernetesSecretTypeOptions.TLS.value:
this.addMissingKeys(['tls.crt', 'tls.key'], newDataIndex); this.addMissingKeys(['tls.crt', 'tls.key'], newDataIndex);
break; break;
case this.KubernetesSecretTypes.BOOTSTRAPTOKEN.value: case this.KubernetesSecretTypeOptions.BOOTSTRAPTOKEN.value:
this.addMissingKeys(['token-id', 'token-secret'], newDataIndex); this.addMissingKeys(['token-id', 'token-secret'], newDataIndex);
break; break;
default: default:
@ -84,29 +83,28 @@ class KubernetesConfigurationDataController {
isRequiredKey(key) { isRequiredKey(key) {
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) { if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
const secretTypeValue = typeof this.formValues.Type === 'string' ? this.formValues.Type : this.formValues.Type.value; switch (this.formValues.Type) {
switch (secretTypeValue) { case this.KubernetesSecretTypeOptions.DOCKERCONFIGJSON.value:
case this.KubernetesSecretTypes.DOCKERCONFIGJSON.value:
if (key === '.dockerconfigjson') { if (key === '.dockerconfigjson') {
return true; return true;
} }
break; break;
case this.KubernetesSecretTypes.DOCKERCFG.value: case this.KubernetesSecretTypeOptions.DOCKERCFG.value:
if (key === '.dockercfg') { if (key === '.dockercfg') {
return true; return true;
} }
break; break;
case this.KubernetesSecretTypes.SSHAUTH.value: case this.KubernetesSecretTypeOptions.SSHAUTH.value:
if (key === 'ssh-privatekey') { if (key === 'ssh-privatekey') {
return true; return true;
} }
break; break;
case this.KubernetesSecretTypes.TLS.value: case this.KubernetesSecretTypeOptions.TLS.value:
if (key === 'tls.crt' || key === 'tls.key') { if (key === 'tls.crt' || key === 'tls.key') {
return true; return true;
} }
break; break;
case this.KubernetesSecretTypes.BOOTSTRAPTOKEN.value: case this.KubernetesSecretTypeOptions.BOOTSTRAPTOKEN.value:
if (key === 'token-id' || key === 'token-secret') { if (key === 'token-id' || key === 'token-secret') {
return true; return true;
} }
@ -168,14 +166,14 @@ class KubernetesConfigurationDataController {
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) { if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
if (this.isDockerConfig) { if (this.isDockerConfig) {
if (this.formValues.Type.name === this.KubernetesSecretTypes.DOCKERCFG.name) { if (this.formValues.Type === this.KubernetesSecretTypeOptions.DOCKERCFG.value) {
entry.Key = '.dockercfg'; entry.Key = '.dockercfg';
} else { } else {
entry.Key = '.dockerconfigjson'; entry.Key = '.dockerconfigjson';
} }
} }
if (this.formValues.Type.name === this.KubernetesSecretTypes.TLS.name) { if (this.formValues.Type === this.KubernetesSecretTypeOptions.TLS.value) {
const isCrt = entry.Value.indexOf('BEGIN CERTIFICATE') !== -1; const isCrt = entry.Value.indexOf('BEGIN CERTIFICATE') !== -1;
if (isCrt) { if (isCrt) {
entry.Key = 'tls.crt'; entry.Key = 'tls.crt';
@ -200,9 +198,8 @@ class KubernetesConfigurationDataController {
isEntryRequired() { isEntryRequired() {
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) { 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 (this.formValues.Data.length === 1) {
if (typeValue !== this.KubernetesSecretTypes.SERVICEACCOUNTTOKEN.value) { if (this.formValues.Type !== this.KubernetesSecretTypeOptions.SERVICEACCOUNTTOKEN.value) {
return true; return true;
} }
} }

View File

@ -17,6 +17,9 @@ class KubernetesConfigurationConverter {
res.ConfigurationOwner = secret.ConfigurationOwner; res.ConfigurationOwner = secret.ConfigurationOwner;
res.IsRegistrySecret = secret.IsRegistrySecret; res.IsRegistrySecret = secret.IsRegistrySecret;
res.SecretType = secret.SecretType; res.SecretType = secret.SecretType;
if (secret.Annotations) {
res.ServiceAccountName = secret.Annotations['kubernetes.io/service-account.name'];
}
return res; return res;
} }

View File

@ -4,13 +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'; import { KubernetesSecretTypeOptions } 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; res.type = secret.Type;
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;
@ -53,6 +53,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;
} }
@ -64,6 +69,7 @@ class KubernetesSecretConverter {
res.Type = payload.type; 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;
res.Annotations = payload.metadata.annotations;
res.IsRegistrySecret = payload.metadata.annotations && !!payload.metadata.annotations['portainer.io/registry.id']; res.IsRegistrySecret = payload.metadata.annotations && !!payload.metadata.annotations['portainer.io/registry.id'];
@ -96,14 +102,11 @@ class KubernetesSecretConverter {
res.ConfigurationOwner = formValues.ConfigurationOwner; res.ConfigurationOwner = formValues.ConfigurationOwner;
res.Data = formValues.Data; res.Data = formValues.Data;
switch (formValues.Type) { if (formValues.Type === KubernetesSecretTypeOptions.CUSTOM.value) {
case KubernetesSecretTypes.CUSTOM: res.Type = formValues.customType;
res.Type.value = formValues.customType; }
break; if (formValues.Type === KubernetesSecretTypeOptions.SERVICEACCOUNTTOKEN.value) {
res.Annotations = [{ name: 'kubernetes.io/service-account.name', value: formValues.ServiceAccountName }];
case KubernetesSecretTypes.SERVICEACCOUNTTOKEN:
res.Annotations = [{ name: 'kubernetes.io/service-account.name', value: formValues.ServiceAccountName }];
break;
} }
return res; return res;
} }

View File

@ -1,4 +1,4 @@
import { KubernetesConfigurationKinds, KubernetesSecretTypes } from './models'; import { KubernetesConfigurationKinds, KubernetesSecretTypeOptions } from './models';
/** /**
* KubernetesConfigurationFormValues Model * KubernetesConfigurationFormValues Model
@ -13,7 +13,7 @@ const _KubernetesConfigurationFormValues = Object.freeze({
DataYaml: '', DataYaml: '',
IsSimple: true, IsSimple: true,
ServiceAccountName: '', ServiceAccountName: '',
Type: KubernetesSecretTypes.OPAQUE, Type: KubernetesSecretTypeOptions.OPAQUE.value,
}); });
export class KubernetesConfigurationFormValues { export class KubernetesConfigurationFormValues {

View File

@ -28,7 +28,7 @@ export const KubernetesConfigurationKinds = Object.freeze({
SECRET: 2, SECRET: 2,
}); });
export const KubernetesSecretTypes = Object.freeze({ export const KubernetesSecretTypeOptions = Object.freeze({
OPAQUE: { name: 'Opaque', value: 'Opaque' }, OPAQUE: { name: 'Opaque', value: 'Opaque' },
SERVICEACCOUNTTOKEN: { name: 'Service account token', value: 'kubernetes.io/service-account-token' }, SERVICEACCOUNTTOKEN: { name: 'Service account token', value: 'kubernetes.io/service-account-token' },
DOCKERCFG: { name: 'Dockercfg', value: 'kubernetes.io/dockercfg' }, DOCKERCFG: { name: 'Dockercfg', value: 'kubernetes.io/dockercfg' },

View File

@ -132,13 +132,13 @@
class="form-control" class="form-control"
id="configuration_data_type" id="configuration_data_type"
ng-model="ctrl.formValues.Type" ng-model="ctrl.formValues.Type"
ng-options="value.name for (name, value) in ctrl.KubernetesSecretTypes" ng-options="value.value as value.name for (name, value) in ctrl.KubernetesSecretTypeOptions"
ng-change="ctrl.onSecretTypeChange()" ng-change="ctrl.onSecretTypeChange()"
></select> ></select>
<div class="col-sm-3 col-lg-2"></div> <div class="col-sm-3 col-lg-2"></div>
</div> </div>
<div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.SERVICEACCOUNTTOKEN" class="col-sm-12 small text-warning vertical-center pt-5"> <div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypeOptions.SERVICEACCOUNTTOKEN.value" class="col-sm-12 small text-warning vertical-center pt-5">
<pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon>
<span <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 >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
@ -147,19 +147,19 @@
kubernetes documentation.</span kubernetes documentation.</span
> >
</div> </div>
<div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.DOCKERCFG" class="col-sm-12 small text-muted vertical-center pt-5"> <div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypeOptions.DOCKERCFG.value" class="col-sm-12 small text-muted vertical-center pt-5">
<pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon> <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> <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>
<div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.DOCKERCONFIGJSON" class="col-sm-12 small text-muted vertical-center pt-5"> <div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypeOptions.DOCKERCONFIGJSON.value" class="col-sm-12 small text-muted vertical-center pt-5">
<pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon> <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> <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>
<div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.TLS" class="col-sm-12 small text-muted vertical-center pt-5"> <div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypeOptions.TLS.value" class="col-sm-12 small text-muted vertical-center pt-5">
<pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon> <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> <span>Ensure the Secret data field contains a <code>tls.key</code> key and a <code>tls.crt</code> key.</span>
</div> </div>
<div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.BOOTSTRAPTOKEN" class="col-sm-12 small text-muted vertical-center pt-5"> <div ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypeOptions.BOOTSTRAPTOKEN.value" class="col-sm-12 small text-muted vertical-center pt-5">
<pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon> <pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon>
<span <span
>Ensure the Secret data field contains a <code>token-id</code> key and a <code>token-secret</code> key. See >Ensure the Secret data field contains a <code>token-id</code> key and a <code>token-secret</code> key. See
@ -168,7 +168,7 @@
> >
</div> </div>
</div> </div>
<div class="form-group" ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.CUSTOM"> <div class="form-group" ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypeOptions.CUSTOM.value">
<label for="configuration_data_customtype" class="col-sm-3 col-lg-2 control-label text-left required">Custom Type</label> <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"> <div class="col-sm-8 col-lg-9">
<input <input
@ -193,7 +193,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="form-group" ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypes.SERVICEACCOUNTTOKEN"> <div class="form-group" ng-if="ctrl.formValues.Type === ctrl.KubernetesSecretTypeOptions.SERVICEACCOUNTTOKEN.value">
<label for="service_account" class="col-sm-3 col-lg-2 control-label text-left required">Service Account</label> <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"> <div class="col-sm-8 col-lg-9">
<select <select

View File

@ -1,7 +1,7 @@
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 { KubernetesConfigurationKinds, KubernetesSecretTypes } from 'Kubernetes/models/configuration/models'; import { KubernetesConfigurationKinds, KubernetesSecretTypeOptions } 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 { getServiceAccounts } from 'Kubernetes/rest/serviceAccount';
@ -21,7 +21,7 @@ class KubernetesCreateConfigurationController {
this.KubernetesConfigurationService = KubernetesConfigurationService; this.KubernetesConfigurationService = KubernetesConfigurationService;
this.KubernetesResourcePoolService = KubernetesResourcePoolService; this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.KubernetesConfigurationKinds = KubernetesConfigurationKinds; this.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
this.KubernetesSecretTypes = KubernetesSecretTypes; this.KubernetesSecretTypeOptions = KubernetesSecretTypeOptions;
this.onInit = this.onInit.bind(this); this.onInit = this.onInit.bind(this);
this.createConfigurationAsync = this.createConfigurationAsync.bind(this); this.createConfigurationAsync = this.createConfigurationAsync.bind(this);
@ -66,41 +66,41 @@ class KubernetesCreateConfigurationController {
} }
onSecretTypeChange() { onSecretTypeChange() {
switch (this.formValues.Type.value) { switch (this.formValues.Type) {
case KubernetesSecretTypes.OPAQUE.value: case KubernetesSecretTypeOptions.OPAQUE.value:
case KubernetesSecretTypes.CUSTOM.value: case KubernetesSecretTypeOptions.CUSTOM.value:
this.formValues.Data = this.formValues.Data.filter((entry) => entry.Value !== ''); this.formValues.Data = this.formValues.Data.filter((entry) => entry.Value !== '');
if (this.formValues.Data.length === 0) { if (this.formValues.Data.length === 0) {
this.addRequiredKeysToForm(['']); this.addRequiredKeysToForm(['']);
} }
this.state.isDockerConfig = false; this.state.isDockerConfig = false;
break; break;
case KubernetesSecretTypes.SERVICEACCOUNTTOKEN.value: case KubernetesSecretTypeOptions.SERVICEACCOUNTTOKEN.value:
// data isn't required for service account tokens, so remove the data fields if they are empty // data isn't required for service account tokens, so remove the data fields if they are empty
this.addRequiredKeysToForm([]); this.addRequiredKeysToForm([]);
this.state.isDockerConfig = false; this.state.isDockerConfig = false;
break; break;
case KubernetesSecretTypes.DOCKERCONFIGJSON.value: case KubernetesSecretTypeOptions.DOCKERCONFIGJSON.value:
this.addRequiredKeysToForm(['.dockerconfigjson']); this.addRequiredKeysToForm(['.dockerconfigjson']);
this.state.isDockerConfig = true; this.state.isDockerConfig = true;
break; break;
case KubernetesSecretTypes.DOCKERCFG.value: case KubernetesSecretTypeOptions.DOCKERCFG.value:
this.addRequiredKeysToForm(['.dockercfg']); this.addRequiredKeysToForm(['.dockercfg']);
this.state.isDockerConfig = true; this.state.isDockerConfig = true;
break; break;
case KubernetesSecretTypes.BASICAUTH.value: case KubernetesSecretTypeOptions.BASICAUTH.value:
this.addRequiredKeysToForm(['username', 'password']); this.addRequiredKeysToForm(['username', 'password']);
this.state.isDockerConfig = false; this.state.isDockerConfig = false;
break; break;
case KubernetesSecretTypes.SSHAUTH.value: case KubernetesSecretTypeOptions.SSHAUTH.value:
this.addRequiredKeysToForm(['ssh-privatekey']); this.addRequiredKeysToForm(['ssh-privatekey']);
this.state.isDockerConfig = false; this.state.isDockerConfig = false;
break; break;
case KubernetesSecretTypes.TLS.value: case KubernetesSecretTypeOptions.TLS.value:
this.addRequiredKeysToForm(['tls.crt', 'tls.key']); this.addRequiredKeysToForm(['tls.crt', 'tls.key']);
this.state.isDockerConfig = false; this.state.isDockerConfig = false;
break; break;
case KubernetesSecretTypes.BOOTSTRAPTOKEN.value: case KubernetesSecretTypeOptions.BOOTSTRAPTOKEN.value:
this.addRequiredKeysToForm(['token-id', 'token-secret']); this.addRequiredKeysToForm(['token-id', 'token-secret']);
this.state.isDockerConfig = false; this.state.isDockerConfig = false;
break; break;

View File

@ -2,7 +2,7 @@ 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 { KubernetesConfigurationKinds, KubernetesSecretTypes } from 'Kubernetes/models/configuration/models'; import { KubernetesConfigurationKinds, KubernetesSecretTypeOptions } 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';
@ -39,7 +39,7 @@ class KubernetesConfigurationController {
this.KubernetesApplicationService = KubernetesApplicationService; this.KubernetesApplicationService = KubernetesApplicationService;
this.KubernetesEventService = KubernetesEventService; this.KubernetesEventService = KubernetesEventService;
this.KubernetesConfigurationKinds = KubernetesConfigurationKinds; this.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
this.KubernetesSecretTypes = KubernetesSecretTypes; this.KubernetesSecretTypeOptions = KubernetesSecretTypeOptions;
this.KubernetesConfigMapService = KubernetesConfigMapService; this.KubernetesConfigMapService = KubernetesConfigMapService;
this.KubernetesSecretService = KubernetesSecretService; this.KubernetesSecretService = KubernetesSecretService;
@ -147,6 +147,7 @@ class KubernetesConfigurationController {
if (secret.status === 'fulfilled') { if (secret.status === 'fulfilled') {
this.configuration = KubernetesConfigurationConverter.secretToConfiguration(secret.value); this.configuration = KubernetesConfigurationConverter.secretToConfiguration(secret.value);
this.formValues.Data = secret.value.Data; this.formValues.Data = secret.value.Data;
// this.formValues.ServiceAccountName = secret.value.ServiceAccountName;
} else { } else {
this.configuration = KubernetesConfigurationConverter.configMapToConfiguration(configMap.value); this.configuration = KubernetesConfigurationConverter.configMapToConfiguration(configMap.value);
this.formValues.Data = configMap.value.Data; this.formValues.Data = configMap.value.Data;
@ -276,19 +277,23 @@ class KubernetesConfigurationController {
// after loading the configuration, check if it is a docker config secret type // after loading the configuration, check if it is a docker config secret type
if ( if (
this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET && this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET &&
(this.formValues.Type === this.KubernetesSecretTypes.DOCKERCONFIGJSON.value || this.formValues.Type === this.KubernetesSecretTypes.DOCKERCFG.value) (this.formValues.Type === this.KubernetesSecretTypeOptions.DOCKERCONFIGJSON.value || this.formValues.Type === this.KubernetesSecretTypeOptions.DOCKERCFG.value)
) { ) {
this.state.isDockerConfig = true; this.state.isDockerConfig = true;
} }
// convert the secret type to a human readable value // convert the secret type to a human readable value
if (this.formValues.Type) { if (this.formValues.Type) {
const secretTypeValues = Object.values(this.KubernetesSecretTypes); const secretTypeValues = Object.values(this.KubernetesSecretTypeOptions);
const secretType = secretTypeValues.find((secretType) => secretType.value === this.formValues.Type); const secretType = secretTypeValues.find((secretType) => secretType.value === this.formValues.Type);
this.secretTypeName = secretType ? secretType.name : this.formValues.Type; this.secretTypeName = secretType ? secretType.name : this.formValues.Type;
} else { } else {
this.secretTypeName = ''; this.secretTypeName = '';
} }
if (this.formValues.Type === this.KubernetesSecretTypeOptions.SERVICEACCOUNTTOKEN.value) {
this.formValues.ServiceAccountName = configuration.ServiceAccountName;
}
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');

View File

@ -1,4 +1,4 @@
import { KubernetesSecretTypes } from '@/kubernetes/models/configuration/models'; import { KubernetesSecretTypeOptions } from '@/kubernetes/models/configuration/models';
import { KubernetesConfigurationKinds } from '@/kubernetes/models/configuration/models'; import { KubernetesConfigurationKinds } from '@/kubernetes/models/configuration/models';
export function isConfigurationFormValid(alreadyExist, isDataValid, formValues) { export function isConfigurationFormValid(alreadyExist, isDataValid, formValues) {
@ -9,36 +9,35 @@ export function isConfigurationFormValid(alreadyExist, isDataValid, formValues)
if (formValues.IsSimple) { if (formValues.IsSimple) {
if (formValues.Kind === KubernetesConfigurationKinds.SECRET) { if (formValues.Kind === KubernetesConfigurationKinds.SECRET) {
let isSecretDataValid = true; let isSecretDataValid = true;
const secretTypeValue = typeof formValues.Type === 'string' ? formValues.Type : formValues.Type.value;
switch (secretTypeValue) { switch (formValues.Type) {
case KubernetesSecretTypes.SERVICEACCOUNTTOKEN.value: case KubernetesSecretTypeOptions.SERVICEACCOUNTTOKEN.value:
// data isn't required for service account tokens // data isn't required for service account tokens
isFormValid = uniqueCheck && formValues.ResourcePool; isFormValid = uniqueCheck && formValues.ResourcePool;
return [isFormValid, '']; return [isFormValid, ''];
case KubernetesSecretTypes.DOCKERCFG.value: case KubernetesSecretTypeOptions.DOCKERCFG.value:
// needs to contain a .dockercfg key // needs to contain a .dockercfg key
isSecretDataValid = formValues.Data.some((entry) => entry.Key === '.dockercfg'); isSecretDataValid = formValues.Data.some((entry) => entry.Key === '.dockercfg');
secretWarningMessage = isSecretDataValid ? '' : 'A data entry with a .dockercfg key is required.'; secretWarningMessage = isSecretDataValid ? '' : 'A data entry with a .dockercfg key is required.';
break; break;
case KubernetesSecretTypes.DOCKERCONFIGJSON.value: case KubernetesSecretTypeOptions.DOCKERCONFIGJSON.value:
// needs to contain a .dockerconfigjson key // needs to contain a .dockerconfigjson key
isSecretDataValid = formValues.Data.some((entry) => entry.Key === '.dockerconfigjson'); isSecretDataValid = formValues.Data.some((entry) => entry.Key === '.dockerconfigjson');
secretWarningMessage = isSecretDataValid ? '' : 'A data entry with a .dockerconfigjson key. is required.'; secretWarningMessage = isSecretDataValid ? '' : 'A data entry with a .dockerconfigjson key. is required.';
break; break;
case KubernetesSecretTypes.BASICAUTH.value: case KubernetesSecretTypeOptions.BASICAUTH.value:
isSecretDataValid = formValues.Data.some((entry) => entry.Key === 'username' || entry.Key === 'password'); isSecretDataValid = formValues.Data.some((entry) => entry.Key === 'username' || entry.Key === 'password');
secretWarningMessage = isSecretDataValid ? '' : 'A data entry with a username or password key is required.'; secretWarningMessage = isSecretDataValid ? '' : 'A data entry with a username or password key is required.';
break; break;
case KubernetesSecretTypes.SSHAUTH.value: case KubernetesSecretTypeOptions.SSHAUTH.value:
isSecretDataValid = formValues.Data.some((entry) => entry.Key === 'ssh-privatekey'); isSecretDataValid = formValues.Data.some((entry) => entry.Key === 'ssh-privatekey');
secretWarningMessage = isSecretDataValid ? '' : `A data entry with a 'ssh-privatekey' key is required.`; secretWarningMessage = isSecretDataValid ? '' : `A data entry with a 'ssh-privatekey' key is required.`;
break; break;
case KubernetesSecretTypes.TLS.value: case KubernetesSecretTypeOptions.TLS.value:
isSecretDataValid = formValues.Data.some((entry) => entry.Key === 'tls.crt') && formValues.Data.some((entry) => entry.Key === 'tls.key'); 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.`; secretWarningMessage = isSecretDataValid ? '' : `Data entries containing a 'tls.crt' key and a 'tls.key' key are required.`;
break; break;
case KubernetesSecretTypes.BOOTSTRAPTOKEN.value: case KubernetesSecretTypeOptions.BOOTSTRAPTOKEN.value:
isSecretDataValid = formValues.Data.some((entry) => entry.Key === 'token-id') && formValues.Data.some((entry) => entry.Key === 'token-secret'); 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.`; secretWarningMessage = isSecretDataValid ? '' : `Data entries containing a 'token-id' key and a 'token-secret' key are required.`;
break; break;

View File

@ -8,7 +8,7 @@ export default function (formValues) {
if (formValues.Kind === KubernetesConfigurationKinds.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.Kind === KubernetesConfigurationKinds.SECRET) { } else if (formValues.Kind === KubernetesConfigurationKinds.SECRET) {
let type = typeof formValues.Type === 'string' ? formValues.Type : formValues.Type.name; let type = formValues.Type;
if (formValues.customType) { if (formValues.customType) {
type = formValues.customType; type = formValues.customType;
} }

View File

@ -38,7 +38,6 @@ angular.module('portainer.app').controller('StackDuplicationFormController', [
} }
function onChangeEnvironment(endpointId) { function onChangeEnvironment(endpointId) {
console.log({ endpointId });
return $scope.$evalAsync(() => { return $scope.$evalAsync(() => {
ctrl.formValues.endpointId = endpointId; ctrl.formValues.endpointId = endpointId;
}); });