mirror of https://github.com/rancher/dashboard.git
349 lines
9.5 KiB
Vue
349 lines
9.5 KiB
Vue
<script>
|
|
import { SECRET_TYPES as TYPES } from '@shell/config/secret';
|
|
import { MANAGEMENT, NORMAN, SCHEMA, DEFAULT_WORKSPACE } from '@shell/config/types';
|
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
|
import CruResource from '@shell/components/CruResource';
|
|
import { _CREATE, _EDIT } from '@shell/config/query-params';
|
|
import Loading from '@shell/components/Loading';
|
|
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 SelectIconGrid from '@shell/components/SelectIconGrid';
|
|
import { sortBy } from '@shell/utils/sort';
|
|
import { ucFirst } from '@shell/utils/string';
|
|
import { set } from '@shell/utils/object';
|
|
|
|
export default {
|
|
name: 'CruCloudCredential',
|
|
|
|
emits: ['set-subtype', 'input'],
|
|
|
|
components: {
|
|
Loading,
|
|
NameNsDescription,
|
|
CruResource,
|
|
Labels,
|
|
SelectIconGrid
|
|
},
|
|
|
|
mixins: [CreateEditView],
|
|
|
|
async fetch() {
|
|
this.nodeDrivers = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_DRIVER });
|
|
this.kontainerDrivers = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.KONTAINER_DRIVER });
|
|
this.extensions = this.getExtensions();
|
|
// Force reload the cloud cred schema and any missing subtypes because there aren't change events sent when drivers come/go
|
|
try {
|
|
const schema = await this.$store.dispatch('rancher/find', {
|
|
type: SCHEMA,
|
|
id: NORMAN.CLOUD_CREDENTIAL,
|
|
opt: {
|
|
force: true,
|
|
url: `schemas/${ NORMAN.CLOUD_CREDENTIAL }`,
|
|
},
|
|
});
|
|
|
|
for ( const k in schema.resourceFields ) {
|
|
if ( !k.endsWith('Config') ) {
|
|
continue;
|
|
}
|
|
|
|
const id = schema.resourceFields[k].type;
|
|
|
|
if ( !this.$store.getters['rancher/schemaFor'](id) ) {
|
|
await this.$store.dispatch('rancher/find', {
|
|
type: SCHEMA,
|
|
id,
|
|
opt: {
|
|
force: true,
|
|
url: `schemas/${ id }`,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
} catch (e) {
|
|
}
|
|
|
|
if ( !this.value._name ) {
|
|
set(this.value, '_name', '');
|
|
}
|
|
|
|
if (this.mode === _EDIT && this.value?.name?.length) {
|
|
this.value._name = this.value.name;
|
|
this.nameRequiredValidation = true;
|
|
}
|
|
|
|
if ( this.value.provider ) {
|
|
this.selectType(this.value.provider);
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
credCustomComponentValidation: false,
|
|
nameRequiredValidation: false,
|
|
nodeDrivers: null,
|
|
kontainerDrivers: null,
|
|
extensions: null
|
|
};
|
|
},
|
|
|
|
watch: {
|
|
'value._name'(newValue) {
|
|
this.nameRequiredValidation = newValue?.length > 0;
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
|
|
hasCustomCloudCredentialComponent() {
|
|
return this.getCustomCloudCredentialComponent(this.driverName);
|
|
},
|
|
|
|
cloudCredentialComponent() {
|
|
const driverName = this.driverName;
|
|
|
|
return this.$store.getters['type-map/importCloudCredential'](driverName);
|
|
},
|
|
|
|
genericCloudCredentialComponent() {
|
|
return this.$store.getters['type-map/importCloudCredential']('generic');
|
|
},
|
|
|
|
cloudComponent() {
|
|
if (this.hasCustomCloudCredentialComponent) {
|
|
return this.cloudCredentialComponent;
|
|
}
|
|
|
|
return this.genericCloudCredentialComponent;
|
|
},
|
|
|
|
validationPassed() {
|
|
return this.credCustomComponentValidation && this.nameRequiredValidation;
|
|
},
|
|
|
|
storeOverride() {
|
|
return 'rancher';
|
|
},
|
|
|
|
driverName() {
|
|
return this.value?.provider;
|
|
},
|
|
|
|
// array of id, label, description, initials for type selection step
|
|
secretSubTypes() {
|
|
const out = [];
|
|
|
|
const fromDrivers = [...this.nodeDrivers, ...this.kontainerDrivers]
|
|
.filter((x) => x.spec.active && x.id !== 'rancherkubernetesengine')
|
|
.map((x) => x.spec.displayName || x.id);
|
|
|
|
const fromExtensions = this.extensions?.filter((x) => !!this.getCustomCloudCredentialComponent(x?.id)).map((x) => x?.id) || [];
|
|
const providers = [...fromDrivers, ...fromExtensions];
|
|
|
|
let types = uniq(providers.map((x) => this.$store.getters['plugins/credentialDriverFor'](x)));
|
|
|
|
const schema = this.$store.getters['rancher/schemaFor'](NORMAN.CLOUD_CREDENTIAL);
|
|
|
|
types = types.filter((name) => {
|
|
const key = this.$store.getters['plugins/credentialFieldForDriver'](name);
|
|
const subSchemaName = schema.resourceFields[`${ key }credentialConfig`]?.type;
|
|
|
|
if ( !subSchemaName ) {
|
|
return;
|
|
}
|
|
|
|
const subSchema = this.$store.getters['rancher/schemaFor'](subSchemaName);
|
|
|
|
if ( !subSchema ) {
|
|
return false;
|
|
}
|
|
|
|
const fields = subSchema.resourceFields;
|
|
|
|
return fields && Object.keys(fields).length > 0;
|
|
});
|
|
|
|
if ( schema.resourceFields['s3credentialConfig'] ) {
|
|
types.push('s3');
|
|
}
|
|
|
|
for ( const id of types ) {
|
|
let bannerAbbrv;
|
|
|
|
let bannerImage = this.$store.app.$extension.getDynamic('image', `providers/${ id }.svg`);
|
|
|
|
if (!bannerImage) {
|
|
try {
|
|
bannerImage = require(`~shell/assets/images/providers/${ id }.svg`);
|
|
} catch (e) {
|
|
bannerImage = null;
|
|
bannerAbbrv = this.initialDisplayFor(id);
|
|
}
|
|
}
|
|
|
|
out.push({
|
|
id,
|
|
label: this.typeDisplay(id),
|
|
bannerImage,
|
|
bannerAbbrv
|
|
});
|
|
}
|
|
|
|
return sortBy(out, 'label');
|
|
},
|
|
|
|
hideSensitiveData() {
|
|
return this.$store.getters['prefs/get'](HIDE_SENSITIVE);
|
|
},
|
|
|
|
doneRoute() {
|
|
return 'c-cluster-manager-cloudCredential';
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
createValidationChanged(passed) {
|
|
this.credCustomComponentValidation = passed;
|
|
},
|
|
|
|
getExtensions() {
|
|
const context = {
|
|
dispatch: this.$store.dispatch,
|
|
getters: this.$store.getters,
|
|
axios: this.$store.$axios,
|
|
$extension: this.$store.app.$extension,
|
|
t: (...args) => this.t.apply(this, args),
|
|
isCreate: this.isCreate,
|
|
isEdit: this.isEdit,
|
|
isView: this.isView,
|
|
};
|
|
|
|
return this.$extension.getProviders(context);
|
|
},
|
|
|
|
getCustomCloudCredentialComponent(driverName) {
|
|
return this.$store.getters['type-map/hasCustomCloudCredentialComponent'](driverName);
|
|
},
|
|
|
|
async saveCredential(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 ( 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.value.metadata.generateName = 'cc-';
|
|
this.value.metadata.namespace = DEFAULT_WORKSPACE;
|
|
|
|
const field = this.$store.getters['plugins/credentialFieldForDriver'](driver);
|
|
|
|
set(this.value, `${ field }credentialConfig`, {});
|
|
}
|
|
|
|
this.value['_type'] = type;
|
|
this.$emit('set-subtype', this.typeDisplay(driver));
|
|
},
|
|
|
|
typeDisplay(driver) {
|
|
return this.$store.getters['i18n/withFallback'](`cluster.provider."${ driver }"`, null, driver);
|
|
},
|
|
|
|
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);
|
|
},
|
|
|
|
updateValue(key, value) {
|
|
this.value.setData(key, value);
|
|
}
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<form class="filled-height">
|
|
<Loading v-if="$fetchState.pending" />
|
|
<CruResource
|
|
v-else
|
|
:mode="mode"
|
|
:validation-passed="validationPassed"
|
|
:selected-subtype="value._type"
|
|
:resource="value"
|
|
:errors="errors"
|
|
:done-route="doneRoute"
|
|
:subtypes="secretSubTypes"
|
|
:can-yaml="false"
|
|
@finish="saveCredential"
|
|
@select-type="selectType"
|
|
@error="e=>errors = e"
|
|
>
|
|
<NameNsDescription
|
|
:value="value"
|
|
:name-editable="true"
|
|
name-key="_name"
|
|
description-key="description"
|
|
name-label="cluster.credential.name.label"
|
|
name-placeholder="cluster.credential.name.placeholder"
|
|
:mode="mode"
|
|
:namespaced="false"
|
|
@update:value="$emit('input', $event)"
|
|
/>
|
|
<keep-alive>
|
|
<component
|
|
:is="cloudComponent"
|
|
ref="cloudComponent"
|
|
:driver-name="driverName"
|
|
:value="value"
|
|
:mode="mode"
|
|
:hide-sensitive-data="hideSensitiveData"
|
|
@validationChanged="createValidationChanged"
|
|
@valueChanged="updateValue"
|
|
/>
|
|
</keep-alive>
|
|
</CruResource>
|
|
</form>
|
|
</template>
|
|
|
|
<style lang='scss'>
|
|
</style>
|