mirror of https://github.com/rancher/dashboard.git
404 lines
10 KiB
Vue
404 lines
10 KiB
Vue
<script>
|
|
import { SECRET_TYPES as TYPES } from '@shell/config/secret';
|
|
import { MANAGEMENT, NAMESPACE, DEFAULT_WORKSPACE } from '@shell/config/types';
|
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
import CruResource from '@shell/components/CruResource';
|
|
import {
|
|
CLOUD_CREDENTIAL, _CLONE, _CREATE, _EDIT, _FLAGGED
|
|
} from '@shell/config/query-params';
|
|
import Loading from '@shell/components/Loading';
|
|
import Tabbed from '@shell/components/Tabbed';
|
|
import Tab from '@shell/components/Tabbed/Tab';
|
|
import Labels from '@shell/components/form/Labels';
|
|
import { HIDE_SENSITIVE } from '@shell/store/prefs';
|
|
import { CAPI } from '@shell/config/labels-annotations';
|
|
import { clear, uniq } from '@shell/utils/array';
|
|
import { NAME as MANAGER } from '@shell/config/product/manager';
|
|
import SelectIconGrid from '@shell/components/SelectIconGrid';
|
|
import { sortBy } from '@shell/utils/sort';
|
|
import { ucFirst } from '@shell/utils/string';
|
|
|
|
const creatableTypes = [
|
|
TYPES.OPAQUE,
|
|
TYPES.DOCKER_JSON,
|
|
TYPES.TLS,
|
|
TYPES.SSH,
|
|
TYPES.BASIC,
|
|
];
|
|
|
|
export default {
|
|
name: 'CruSecret',
|
|
|
|
components: {
|
|
LabeledInput,
|
|
LabeledSelect,
|
|
Loading,
|
|
NameNsDescription,
|
|
CruResource,
|
|
Tabbed,
|
|
Tab,
|
|
Labels,
|
|
SelectIconGrid
|
|
},
|
|
|
|
mixins: [CreateEditView],
|
|
|
|
async fetch() {
|
|
if ( this.isCloud ) {
|
|
this.nodeDrivers = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_DRIVER });
|
|
}
|
|
},
|
|
|
|
data() {
|
|
const newCloudCred = this.$route.query[CLOUD_CREDENTIAL] === _FLAGGED;
|
|
const editCloudCred = this.mode === _EDIT && this.value._type === TYPES.CLOUD_CREDENTIAL;
|
|
const cloneCloudCred = this.realMode === _CLONE && this.liveValue._type === TYPES.CLOUD_CREDENTIAL;
|
|
const isCloud = newCloudCred || editCloudCred || cloneCloudCred;
|
|
|
|
if ( newCloudCred ) {
|
|
this.value.metadata.namespace = DEFAULT_WORKSPACE;
|
|
|
|
this.$set(this.value.metadata, 'name', '');
|
|
|
|
this.$set(this.value, 'data', {});
|
|
}
|
|
|
|
const secretTypes = [
|
|
{
|
|
label: 'Custom',
|
|
value: 'custom'
|
|
},
|
|
{
|
|
label: 'divider',
|
|
disabled: true,
|
|
kind: 'divider'
|
|
}
|
|
];
|
|
|
|
Object.values(TYPES).forEach((t) => {
|
|
secretTypes.push({
|
|
label: t,
|
|
value: t
|
|
});
|
|
});
|
|
|
|
return {
|
|
isCloud,
|
|
nodeDrivers: null,
|
|
secretTypes,
|
|
secretType: this.value._type,
|
|
initialSecretType: this.value._type
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
isCustomSecretCreate() {
|
|
return this.mode === _CREATE && this.$route.query.type === 'custom';
|
|
},
|
|
showCustomSecretType() {
|
|
return this.secretType === 'custom';
|
|
},
|
|
typeKey() {
|
|
if ( this.isCloud ) {
|
|
return 'cloud';
|
|
}
|
|
|
|
switch ( this.value._type ) {
|
|
case TYPES.TLS: return 'tls';
|
|
case TYPES.BASIC: return 'basic';
|
|
case TYPES.DOCKER_JSON: return 'registry';
|
|
case TYPES.SSH: return 'ssh';
|
|
}
|
|
|
|
return 'generic';
|
|
},
|
|
|
|
dataComponent() {
|
|
return require(`@shell/edit/secret/${ this.typeKey }`).default;
|
|
},
|
|
|
|
driverName() {
|
|
const driver = this.value.metadata?.annotations?.[CAPI.CREDENTIAL_DRIVER];
|
|
|
|
return driver;
|
|
},
|
|
|
|
cloudComponent() {
|
|
if (this.$store.getters['type-map/hasCustomCloudCredentialComponent'](this.driverName)) {
|
|
return this.$store.getters['type-map/importCloudCredential'](this.driverName);
|
|
}
|
|
|
|
return this.$store.getters['type-map/importCloudCredential']('generic');
|
|
},
|
|
|
|
// array of id, label, description, initials for type selection step
|
|
secretSubTypes() {
|
|
const out = [];
|
|
|
|
// Cloud credentials
|
|
if ( this.isCloud ) {
|
|
const machineTypes = uniq(this.nodeDrivers
|
|
.filter(x => x.spec.active)
|
|
.map(x => x.spec.displayName || x.id)
|
|
.map(x => this.$store.getters['plugins/credentialDriverFor'](x))
|
|
);
|
|
|
|
for ( const id of machineTypes ) {
|
|
let bannerImage, bannerAbbrv;
|
|
|
|
try {
|
|
bannerImage = require(`~shell/assets/images/providers/${ id }.svg`);
|
|
} catch (e) {
|
|
bannerImage = null;
|
|
bannerAbbrv = this.initialDisplayFor(id);
|
|
}
|
|
|
|
out.push({
|
|
id,
|
|
label: this.typeDisplay(CAPI.CREDENTIAL_DRIVER, id),
|
|
bannerImage,
|
|
bannerAbbrv
|
|
});
|
|
}
|
|
} else {
|
|
// Other kinds
|
|
for ( const id of creatableTypes ) {
|
|
out.push({
|
|
id,
|
|
label: this.typeDisplay(id),
|
|
bannerAbbrv: this.initialDisplayFor(id),
|
|
description: this.t(`secret.typeDescriptions.'${ id }'.description`),
|
|
docLink: this.t(`secret.typeDescriptions.'${ id }'.docLink`)
|
|
});
|
|
}
|
|
|
|
out.push({
|
|
id: 'custom',
|
|
label: this.t('secret.customType'),
|
|
bannerAbbrv: this.initialDisplayFor('custom'),
|
|
description: this.t('secret.typeDescriptions.custom.description')
|
|
});
|
|
}
|
|
|
|
return sortBy(out, 'label');
|
|
},
|
|
|
|
namespaces() {
|
|
return this.$store.getters['cluster/all'](NAMESPACE).map((obj) => {
|
|
return {
|
|
label: obj.nameDisplay,
|
|
value: obj.id,
|
|
};
|
|
});
|
|
},
|
|
|
|
hideSensitiveData() {
|
|
return this.$store.getters['prefs/get'](HIDE_SENSITIVE);
|
|
},
|
|
|
|
dataLabel() {
|
|
switch (this.value._type) {
|
|
case TYPES.TLS:
|
|
return this.t('secret.certificate.certificate');
|
|
case TYPES.SSH:
|
|
return this.t('secret.ssh.keys');
|
|
case TYPES.BASIC:
|
|
return this.t('secret.authentication');
|
|
default:
|
|
return this.t('secret.data');
|
|
}
|
|
},
|
|
|
|
doneRoute() {
|
|
if ( this.$store.getters['currentProduct'].name === MANAGER ) {
|
|
return 'c-cluster-manager-secret';
|
|
} else {
|
|
return 'c-cluster-product-resource';
|
|
}
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
async saveSecret(btnCb) {
|
|
if ( this.errors ) {
|
|
clear(this.errors);
|
|
}
|
|
|
|
if ( typeof this.$refs.cloudComponent?.test === 'function' ) {
|
|
try {
|
|
const res = await this.$refs.cloudComponent.test();
|
|
|
|
if ( !res || res?.errors) {
|
|
if (res?.errors) {
|
|
this.errors = res.errors;
|
|
} else {
|
|
this.errors = ['Authentication test failed, please check your credentials'];
|
|
}
|
|
btnCb(false);
|
|
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
this.errors = [e];
|
|
btnCb(false);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
return this.save(btnCb);
|
|
},
|
|
|
|
selectType(type) {
|
|
let driver;
|
|
|
|
if ( this.isCloud ) {
|
|
if ( type === TYPES.CLOUD_CREDENTIAL ) {
|
|
// Clone goes through here
|
|
driver = this.driverName;
|
|
} else {
|
|
driver = type;
|
|
type = TYPES.CLOUD_CREDENTIAL;
|
|
}
|
|
|
|
if ( this.mode === _CREATE ) {
|
|
this.value.setAnnotation(CAPI.CREDENTIAL_DRIVER, driver);
|
|
}
|
|
}
|
|
|
|
this.$set(this.value, '_type', type);
|
|
this.$emit('set-subtype', this.typeDisplay(type, driver));
|
|
|
|
this.secretType = type;
|
|
|
|
if (this.mode === _CREATE && type === 'custom') {
|
|
this.$set(this.value, '_type', '');
|
|
}
|
|
},
|
|
|
|
typeDisplay(type, driver) {
|
|
if ( type === CAPI.CREDENTIAL_DRIVER ) {
|
|
return this.$store.getters['i18n/withFallback'](`cluster.provider."${ driver }"`, null, driver);
|
|
} else {
|
|
const fallback = type.replace(/^kubernetes.io\//, '');
|
|
|
|
return this.$store.getters['i18n/withFallback'](`secret.types."${ type }"`, null, fallback);
|
|
}
|
|
},
|
|
|
|
initialDisplayFor(type) {
|
|
const fallback = (ucFirst(this.typeDisplay(type) || '').replace(/[^A-Z]/g, '') || type).substr(0, 3);
|
|
|
|
return this.$store.getters['i18n/withFallback'](`secret.initials."${ type }"`, null, fallback);
|
|
},
|
|
|
|
selectCustomType(type) {
|
|
if (type !== 'custom') {
|
|
this.$set(this.value, '_type', type);
|
|
}
|
|
}
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<form class="filled-height">
|
|
<Loading v-if="$fetchState.pending" />
|
|
<CruResource
|
|
v-else
|
|
:mode="mode"
|
|
:validation-passed="true"
|
|
:selected-subtype="value._type"
|
|
:resource="value"
|
|
:errors="errors"
|
|
:done-route="doneRoute"
|
|
:subtypes="secretSubTypes"
|
|
@finish="saveSecret"
|
|
@select-type="selectType"
|
|
@error="e=>errors = e"
|
|
>
|
|
<NameNsDescription
|
|
v-model="value"
|
|
:mode="mode"
|
|
:namespaced="!isCloud"
|
|
/>
|
|
|
|
<div
|
|
v-if="isCustomSecretCreate"
|
|
class="row"
|
|
>
|
|
<div class="col span-3">
|
|
<LabeledSelect
|
|
v-model="secretType"
|
|
:options="secretTypes"
|
|
:searchable="false"
|
|
:mode="mode"
|
|
:multiple="false"
|
|
:reduce="(e) => e.value"
|
|
label-key="secret.type"
|
|
required
|
|
@input="selectCustomType"
|
|
/>
|
|
</div>
|
|
|
|
<div class="col span-3">
|
|
<LabeledInput
|
|
v-if="showCustomSecretType"
|
|
ref="customType"
|
|
v-model="value._type"
|
|
v-focus
|
|
label-key="secret.customType"
|
|
:mode="mode"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="spacer" />
|
|
<component
|
|
:is="cloudComponent"
|
|
v-if="isCloud"
|
|
ref="cloudComponent"
|
|
:driver-name="driverName"
|
|
:value="value"
|
|
:mode="mode"
|
|
:hide-sensitive-data="hideSensitiveData"
|
|
/>
|
|
<Tabbed
|
|
v-else
|
|
:side-tabs="true"
|
|
default-tab="data"
|
|
>
|
|
<Tab
|
|
name="data"
|
|
:label="dataLabel"
|
|
:weight="99"
|
|
>
|
|
<component
|
|
:is="dataComponent"
|
|
:value="value"
|
|
:mode="mode"
|
|
:hide-sensitive-data="hideSensitiveData"
|
|
/>
|
|
</Tab>
|
|
<Tab
|
|
name="labels"
|
|
label-key="generic.labelsAndAnnotations"
|
|
:weight="-1"
|
|
>
|
|
<Labels
|
|
v-model="value"
|
|
:mode="mode"
|
|
/>
|
|
</Tab>
|
|
</Tabbed>
|
|
</CruResource>
|
|
</form>
|
|
</template>
|
|
|
|
<style lang='scss'>
|
|
</style>
|