mirror of https://github.com/rancher/dashboard.git
Generic cloud credential and machine drivers
This commit is contained in:
parent
7e1b925979
commit
31e0a7973e
|
|
@ -1106,6 +1106,8 @@ cluster:
|
||||||
rke2-ingress-nginx: 'NGINX Ingress Controller'
|
rke2-ingress-nginx: 'NGINX Ingress Controller'
|
||||||
rke2-kube-proxy: 'Kube Proxy'
|
rke2-kube-proxy: 'Kube Proxy'
|
||||||
rke2-metrics-server: 'Metrics Server'
|
rke2-metrics-server: 'Metrics Server'
|
||||||
|
selectCredential:
|
||||||
|
genericDescription: "{vendor} has no built-in support for this driver. We've taken a guess, but consult the driver's documentation for the fields required for authentication."
|
||||||
tabs:
|
tabs:
|
||||||
ace: Authorized Endpoint
|
ace: Authorized Endpoint
|
||||||
advanced: Advanced
|
advanced: Advanced
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
<script>
|
||||||
|
import CreateEditView from '@/mixins/create-edit-view';
|
||||||
|
import KeyValue from '@/components/form/KeyValue';
|
||||||
|
import Banner from '@/components/Banner';
|
||||||
|
import { _CREATE } from '@/config/query-params';
|
||||||
|
import { simplify, iffyFields, likelyFields } from '@/store/plugins';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { KeyValue, Banner },
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
driverName: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
const keyOptions = this.$store.getters['plugins/fieldNamesForDriver'](this.driverName);
|
||||||
|
|
||||||
|
if ( this.mode === _CREATE ) {
|
||||||
|
// Prepopulate empty values for keys that sound like they're cloud-credential-ey
|
||||||
|
|
||||||
|
const keys = [];
|
||||||
|
|
||||||
|
for ( const k of keyOptions ) {
|
||||||
|
const sk = simplify(k);
|
||||||
|
|
||||||
|
if ( likelyFields.includes(sk) || iffyFields.includes(sk) ) {
|
||||||
|
keys.push(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( const k of keys ) {
|
||||||
|
if ( !this.value.decodedData[k] ) {
|
||||||
|
this.value.setData(k, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
keyOptions,
|
||||||
|
errors: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
'value.decodedData'(neu) {
|
||||||
|
this.$emit('validationChanged', !!neu);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
update(val) {
|
||||||
|
this.value.setData(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Banner color="info" label-key="cluster.selectCredential.genericDescription" class="mt-0" />
|
||||||
|
<KeyValue
|
||||||
|
:value="value.decodedData"
|
||||||
|
:key-options="keyOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:read-allowed="true"
|
||||||
|
:initial-empty-row="true"
|
||||||
|
@input="update"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -39,15 +39,15 @@ export function componentForQuestion(q) {
|
||||||
return 'string';
|
return 'string';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function schemaToQuestions(schema) {
|
export function schemaToQuestions(fields) {
|
||||||
const keys = Object.keys(schema.resourceFields);
|
const keys = Object.keys(fields);
|
||||||
const out = [];
|
const out = [];
|
||||||
|
|
||||||
for ( const k of keys ) {
|
for ( const k of keys ) {
|
||||||
out.push({
|
out.push({
|
||||||
variable: k,
|
variable: k,
|
||||||
label: k,
|
label: k,
|
||||||
...schema.resourceFields[k],
|
...fields[k],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,12 +192,14 @@ export default {
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
allQuestions() {
|
allQuestions() {
|
||||||
if ( this.source.type === 'schema' ) {
|
if ( this.source.questions?.questions ) {
|
||||||
return schemaToQuestions(this.source);
|
|
||||||
} else if ( this.source.questions?.questions ) {
|
|
||||||
return this.chartVersion.questions.questions;
|
return this.chartVersion.questions.questions;
|
||||||
|
} else if ( this.source.type === 'schema' && this.source.resourceFields ) {
|
||||||
|
return schemaToQuestions(this.source.resourceFields);
|
||||||
|
} else if ( typeof this.source === 'object' ) {
|
||||||
|
return schemaToQuestions(this.source);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Must specify sourec as a chartVersion or Schema resource');
|
return [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,14 @@ export default {
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
configComponent() {
|
configComponent() {
|
||||||
|
const haveProviders = this.$store.getters['plugins/machineDrivers'];
|
||||||
|
|
||||||
|
if ( haveProviders.includes(this.provider) ) {
|
||||||
return importMachineConfig(this.provider);
|
return importMachineConfig(this.provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return importMachineConfig('generic');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -84,6 +90,7 @@ export default {
|
||||||
:uuid="uuid"
|
:uuid="uuid"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:value="value.config"
|
:value="value.config"
|
||||||
|
:provider="provider"
|
||||||
:credential-id="credentialId"
|
:credential-id="credentialId"
|
||||||
@error="e=>errors = e"
|
@error="e=>errors = e"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -85,16 +85,9 @@ export default {
|
||||||
|
|
||||||
driverName() {
|
driverName() {
|
||||||
let driver = this.provider;
|
let driver = this.provider;
|
||||||
const azureDrivers = ['azure', 'aks'];
|
|
||||||
|
|
||||||
// Map providers that share a common credential to one driver
|
// Map providers that share a common credential to one driver
|
||||||
if ( driver === 'amazonec2' || driver === 'amazoneks' ) {
|
driver = this.$store.getters['plugins/credentialDriverFor'](driver);
|
||||||
driver = 'aws';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( azureDrivers.includes(driver) ) {
|
|
||||||
driver = 'azure';
|
|
||||||
}
|
|
||||||
|
|
||||||
return driver;
|
return driver;
|
||||||
},
|
},
|
||||||
|
|
@ -135,7 +128,13 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
createComponent() {
|
createComponent() {
|
||||||
|
const haveDrivers = this.$store.getters['plugins/credentialDrivers'];
|
||||||
|
|
||||||
|
if ( haveDrivers.includes(this.driverName) ) {
|
||||||
return importCloudCredential(this.driverName);
|
return importCloudCredential(this.driverName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return importCloudCredential('generic');
|
||||||
},
|
},
|
||||||
|
|
||||||
validationPassed() {
|
validationPassed() {
|
||||||
|
|
@ -244,6 +243,8 @@ export default {
|
||||||
:is="createComponent"
|
:is="createComponent"
|
||||||
ref="create"
|
ref="create"
|
||||||
v-model="newCredential"
|
v-model="newCredential"
|
||||||
|
mode="create"
|
||||||
|
:driver-name="driverName"
|
||||||
@validationChanged="createValidationChanged"
|
@validationChanged="createValidationChanged"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -175,11 +175,8 @@ export default {
|
||||||
const out = [];
|
const out = [];
|
||||||
|
|
||||||
const templates = this.templateOptions;
|
const templates = this.templateOptions;
|
||||||
const vueMachineTypes = getters['plugins/machineDrivers'];
|
|
||||||
const vueKontainerTypes = getters['plugins/clusterDrivers'];
|
const vueKontainerTypes = getters['plugins/clusterDrivers'];
|
||||||
const machineTypes = this.nodeDrivers.filter(x => x.spec.active).map((x) => {
|
const machineTypes = this.nodeDrivers.filter(x => x.spec.active && x.state === 'active').map(x => x.spec.displayName || x.id);
|
||||||
return !x.spec.builtin ? x.spec.displayName : x.id;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.kontainerDrivers.filter(x => (isImport ? x.showImport : x.showCreate)).forEach((obj) => {
|
this.kontainerDrivers.filter(x => (isImport ? x.showImport : x.showCreate)).forEach((obj) => {
|
||||||
if ( vueKontainerTypes.includes(obj.driverName) ) {
|
if ( vueKontainerTypes.includes(obj.driverName) ) {
|
||||||
|
|
@ -210,7 +207,7 @@ export default {
|
||||||
addType('custom', 'custom1', false, '/g/clusters/add/launch/custom');
|
addType('custom', 'custom1', false, '/g/clusters/add/launch/custom');
|
||||||
} else {
|
} else {
|
||||||
machineTypes.forEach((id) => {
|
machineTypes.forEach((id) => {
|
||||||
addType(id, 'rke2', !vueMachineTypes.includes(id));
|
addType(id, 'rke2', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
addType('custom', 'custom2', false);
|
addType('custom', 'custom2', false);
|
||||||
|
|
@ -263,6 +260,10 @@ export default {
|
||||||
entry.types.push(row);
|
entry.types.push(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for ( const k in out ) {
|
||||||
|
out[k].types = sortBy(out[k].types, 'label');
|
||||||
|
}
|
||||||
|
|
||||||
return sortBy(Object.values(out), 'sort');
|
return sortBy(Object.values(out), 'sort');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1157,7 +1157,7 @@ export default {
|
||||||
v-if="versionInfo[v.name].questions"
|
v-if="versionInfo[v.name].questions"
|
||||||
v-model="chartValues[v.name]"
|
v-model="chartValues[v.name]"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:chart-version="versionInfo[v.name]"
|
:source="versionInfo[v.name]"
|
||||||
:target-namespace="value.metadata.namespace"
|
:target-namespace="value.metadata.namespace"
|
||||||
/>
|
/>
|
||||||
<YamlEditor
|
<YamlEditor
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,23 @@
|
||||||
<script>
|
<script>
|
||||||
import { TYPES } from '@/models/secret';
|
import { TYPES } from '@/models/secret';
|
||||||
import { NAMESPACE } from '@/config/types';
|
import { MANAGEMENT, NAMESPACE } from '@/config/types';
|
||||||
import CreateEditView from '@/mixins/create-edit-view';
|
import CreateEditView from '@/mixins/create-edit-view';
|
||||||
import NameNsDescription from '@/components/form/NameNsDescription';
|
import NameNsDescription from '@/components/form/NameNsDescription';
|
||||||
import CruResource from '@/components/CruResource';
|
import CruResource from '@/components/CruResource';
|
||||||
import { CLOUD_CREDENTIAL, _CREATE, _EDIT, _FLAGGED } from '@/config/query-params';
|
import { CLOUD_CREDENTIAL, _CREATE, _EDIT, _FLAGGED } from '@/config/query-params';
|
||||||
|
import Loading from '@/components/Loading';
|
||||||
import Tabbed from '@/components/Tabbed';
|
import Tabbed from '@/components/Tabbed';
|
||||||
import Tab from '@/components/Tabbed/Tab';
|
import Tab from '@/components/Tabbed/Tab';
|
||||||
import Labels from '@/components/form/Labels';
|
import Labels from '@/components/form/Labels';
|
||||||
import { HIDE_SENSITIVE } from '@/store/prefs';
|
import { HIDE_SENSITIVE } from '@/store/prefs';
|
||||||
import { CAPI } from '@/config/labels-annotations';
|
import { CAPI } from '@/config/labels-annotations';
|
||||||
import { clear } from '@/utils/array';
|
import { clear, uniq } from '@/utils/array';
|
||||||
import { importCloudCredential } from '@/utils/dynamic-importer';
|
import { importCloudCredential } from '@/utils/dynamic-importer';
|
||||||
import { NAME as MANAGER } from '@/config/product/manager';
|
import { NAME as MANAGER } from '@/config/product/manager';
|
||||||
import SelectIconGrid from '@/components/SelectIconGrid';
|
import SelectIconGrid from '@/components/SelectIconGrid';
|
||||||
import { DEFAULT_WORKSPACE } from '@/models/provisioning.cattle.io.cluster';
|
import { DEFAULT_WORKSPACE } from '@/models/provisioning.cattle.io.cluster';
|
||||||
|
import { sortBy } from '@/utils/sort';
|
||||||
|
import { ucFirst } from '@/utils/string';
|
||||||
|
|
||||||
const creatableTypes = [
|
const creatableTypes = [
|
||||||
TYPES.OPAQUE,
|
TYPES.OPAQUE,
|
||||||
|
|
@ -28,6 +31,7 @@ export default {
|
||||||
name: 'CruSecret',
|
name: 'CruSecret',
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
Loading,
|
||||||
NameNsDescription,
|
NameNsDescription,
|
||||||
CruResource,
|
CruResource,
|
||||||
Tabbed,
|
Tabbed,
|
||||||
|
|
@ -38,6 +42,12 @@ export default {
|
||||||
|
|
||||||
mixins: [CreateEditView],
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
if ( this.isCloud ) {
|
||||||
|
this.nodeDrivers = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_DRIVER });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
const newCloudCred = this.$route.query[CLOUD_CREDENTIAL] === _FLAGGED;
|
const newCloudCred = this.$route.query[CLOUD_CREDENTIAL] === _FLAGGED;
|
||||||
const editCloudCred = this.mode === _EDIT && this.value._type === TYPES.CLOUD_CREDENTIAL;
|
const editCloudCred = this.mode === _EDIT && this.value._type === TYPES.CLOUD_CREDENTIAL;
|
||||||
|
|
@ -47,7 +57,11 @@ export default {
|
||||||
this.value.metadata.namespace = DEFAULT_WORKSPACE;
|
this.value.metadata.namespace = DEFAULT_WORKSPACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { isCloud };
|
return {
|
||||||
|
isCloud,
|
||||||
|
nodeDrivers: null,
|
||||||
|
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -70,14 +84,21 @@ export default {
|
||||||
return require(`@/edit/secret/${ this.typeKey }`).default;
|
return require(`@/edit/secret/${ this.typeKey }`).default;
|
||||||
},
|
},
|
||||||
|
|
||||||
cloudComponent() {
|
driverName() {
|
||||||
const driver = this.value.metadata?.annotations?.[CAPI.CREDENTIAL_DRIVER];
|
const driver = this.value.metadata?.annotations?.[CAPI.CREDENTIAL_DRIVER];
|
||||||
|
|
||||||
if ( driver ) {
|
return driver;
|
||||||
|
},
|
||||||
|
|
||||||
|
cloudComponent() {
|
||||||
|
const driver = this.driverName;
|
||||||
|
const haveProviders = this.$store.getters['plugins/credentialDrivers'];
|
||||||
|
|
||||||
|
if ( haveProviders.includes(driver) ) {
|
||||||
return importCloudCredential(driver);
|
return importCloudCredential(driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return importCloudCredential('generic');
|
||||||
},
|
},
|
||||||
|
|
||||||
// array of id, label, description, initials for type selection step
|
// array of id, label, description, initials for type selection step
|
||||||
|
|
@ -86,7 +107,13 @@ export default {
|
||||||
|
|
||||||
// Cloud credentials
|
// Cloud credentials
|
||||||
if ( this.isCloud ) {
|
if ( this.isCloud ) {
|
||||||
for ( const id of this.$store.getters['plugins/credentialDrivers'] ) {
|
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;
|
let bannerImage, bannerAbbrv;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -114,7 +141,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return sortBy(out, 'label');
|
||||||
},
|
},
|
||||||
|
|
||||||
namespaces() {
|
namespaces() {
|
||||||
|
|
@ -206,7 +233,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
initialDisplayFor(type) {
|
initialDisplayFor(type) {
|
||||||
const fallback = (this.typeDisplay(type) || '').replace(/[^A-Z]/g, '') || 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);
|
return this.$store.getters['i18n/withFallback'](`secret.initials."${ type }"`, null, fallback);
|
||||||
},
|
},
|
||||||
|
|
@ -216,7 +243,9 @@ export default {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<form>
|
<form>
|
||||||
|
<Loading v-if="$fetchState.pending" />
|
||||||
<CruResource
|
<CruResource
|
||||||
|
v-else
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:validation-passed="true"
|
:validation-passed="true"
|
||||||
:selected-subtype="value._type"
|
:selected-subtype="value._type"
|
||||||
|
|
@ -235,6 +264,7 @@ export default {
|
||||||
:is="cloudComponent"
|
:is="cloudComponent"
|
||||||
v-if="isCloud"
|
v-if="isCloud"
|
||||||
ref="cloudComponent"
|
ref="cloudComponent"
|
||||||
|
:driver-name="driverName"
|
||||||
:value="value"
|
:value="value"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:hide-sensitive-data="hideSensitiveData"
|
:hide-sensitive-data="hideSensitiveData"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
<script>
|
||||||
|
import Loading from '@/components/Loading';
|
||||||
|
import Banner from '@/components/Banner';
|
||||||
|
import CreateEditView from '@/mixins/create-edit-view';
|
||||||
|
import { exceptionToErrorsArray, stringify } from '@/utils/error';
|
||||||
|
import { SECRET } from '@/config/types';
|
||||||
|
import Questions from '@/components/Questions';
|
||||||
|
import { iffyFields, simplify } from '@/store/plugins';
|
||||||
|
import { isEmpty } from '@/utils/object';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Loading, Banner, Questions
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
credentialId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
provider: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
this.errors = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.credential = await this.$store.dispatch('management/find', { type: SECRET, id: this.credentialId });
|
||||||
|
this.fields = this.$store.getters['plugins/fieldsForDriver'](this.provider);
|
||||||
|
const name = `rke-machine-config.cattle.io.${ this.provider }config`;
|
||||||
|
|
||||||
|
if ( !this.fields ) {
|
||||||
|
throw new Error(`Machine Driver config schema not found for ${ name }`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.errors = exceptionToErrorsArray(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
errors: null,
|
||||||
|
fields: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
cloudCredentialKeys() {
|
||||||
|
const out = [];
|
||||||
|
const data = this.credential?.decodedData || {};
|
||||||
|
|
||||||
|
for ( const k in data ) {
|
||||||
|
if ( isEmpty(data[k]) || iffyFields.includes(simplify(k)) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
'credentialId'() {
|
||||||
|
this.$fetch();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: { stringify },
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Loading v-if="$fetchState.pending" :delayed="true" />
|
||||||
|
<div v-else-if="errors.length">
|
||||||
|
<div
|
||||||
|
v-for="(err, idx) in errors"
|
||||||
|
:key="idx"
|
||||||
|
>
|
||||||
|
<Banner
|
||||||
|
color="error"
|
||||||
|
:label="stringify(err)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<Questions
|
||||||
|
v-model="value"
|
||||||
|
:mode="mode"
|
||||||
|
:tabbed="false"
|
||||||
|
:source="fields"
|
||||||
|
:ignore-variables="cloudCredentialKeys"
|
||||||
|
:target-namespace="value.metadata.namespace"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -3,8 +3,9 @@ import { CAPI, CERTMANAGER, KUBERNETES } from '@/config/labels-annotations';
|
||||||
import { base64Decode, base64Encode } from '@/utils/crypto';
|
import { base64Decode, base64Encode } from '@/utils/crypto';
|
||||||
import { removeObjects } from '@/utils/array';
|
import { removeObjects } from '@/utils/array';
|
||||||
import { SERVICE_ACCOUNT } from '@/config/types';
|
import { SERVICE_ACCOUNT } from '@/config/types';
|
||||||
import { set } from '@/utils/object';
|
import { isEmpty, set } from '@/utils/object';
|
||||||
import { escapeHtml } from '@/utils/string';
|
import { escapeHtml } from '@/utils/string';
|
||||||
|
import { fullFields, prefixFields, simplify, suffixFields } from '@/store/plugins';
|
||||||
|
|
||||||
export const TYPES = {
|
export const TYPES = {
|
||||||
OPAQUE: 'Opaque',
|
OPAQUE: 'Opaque',
|
||||||
|
|
@ -312,13 +313,25 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
setData() {
|
setData() {
|
||||||
return (key, value) => {
|
return (key, value) => { // or (mapOfNewData)
|
||||||
if ( !this.data ) {
|
const isMap = key && typeof key === 'object';
|
||||||
|
|
||||||
|
if ( !this.data || isMap ) {
|
||||||
set(this, 'data', {});
|
set(this, 'data', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let neu;
|
||||||
|
|
||||||
|
if ( isMap ) {
|
||||||
|
neu = key;
|
||||||
|
} else {
|
||||||
|
neu = { [key]: value };
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( const k in neu ) {
|
||||||
// The key is quoted so that keys like '.dockerconfigjson' that contain dot don't get parsed into an object path
|
// The key is quoted so that keys like '.dockerconfigjson' that contain dot don't get parsed into an object path
|
||||||
set(this.data, `"${ key }"`, base64Encode(value));
|
set(this.data, `"${ k }"`, base64Encode(neu[k]));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -333,7 +346,33 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
cloudCredentialPublicData() {
|
cloudCredentialPublicData() {
|
||||||
const { publicKey, publicMode } = this.$rootGetters['plugins/credentialOptions'](this.cloudCredentialProvider);
|
let { publicKey, publicMode } = this.$rootGetters['plugins/credentialOptions'](this.cloudCredentialProvider);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
full: fullFields,
|
||||||
|
prefix: prefixFields,
|
||||||
|
suffix: suffixFields,
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( !publicKey ) {
|
||||||
|
for ( const k in this.decodedData || {} ) {
|
||||||
|
if ( publicKey ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isEmpty(this.decodedData[k]) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( const mode in options ) {
|
||||||
|
if ( options[mode].includes( simplify(k) ) ) {
|
||||||
|
publicKey = k;
|
||||||
|
publicMode = mode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( !publicKey ) {
|
if ( !publicKey ) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -1112,7 +1112,7 @@ export default {
|
||||||
<Questions
|
<Questions
|
||||||
v-model="chartValues"
|
v-model="chartValues"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:chart-version="versionInfo"
|
:source="versionInfo"
|
||||||
:target-namespace="targetNamespace"
|
:target-namespace="targetNamespace"
|
||||||
/>
|
/>
|
||||||
</Tabbed>
|
</Tabbed>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import Loading from '@/components/Loading';
|
||||||
import ResourceTable from '@/components/ResourceTable';
|
import ResourceTable from '@/components/ResourceTable';
|
||||||
import Masthead from '@/components/ResourceList/Masthead';
|
import Masthead from '@/components/ResourceList/Masthead';
|
||||||
import { SECRET } from '@/config/types';
|
import { SECRET } from '@/config/types';
|
||||||
|
|
@ -6,7 +7,9 @@ import { AGE, NAME, STATE } from '@/config/table-headers';
|
||||||
import { CLOUD_CREDENTIAL, _FLAGGED } from '@/config/query-params';
|
import { CLOUD_CREDENTIAL, _FLAGGED } from '@/config/query-params';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { ResourceTable, Masthead },
|
components: {
|
||||||
|
Loading, ResourceTable, Masthead
|
||||||
|
},
|
||||||
|
|
||||||
async fetch() {
|
async fetch() {
|
||||||
this.allSecrets = await this.$store.dispatch('management/findAll', { type: SECRET });
|
this.allSecrets = await this.$store.dispatch('management/findAll', { type: SECRET });
|
||||||
|
|
@ -14,7 +17,7 @@ export default {
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
allSecrets: [],
|
allSecrets: null,
|
||||||
resource: SECRET,
|
resource: SECRET,
|
||||||
schema: this.$store.getters['management/schemaFor'](SECRET),
|
schema: this.$store.getters['management/schemaFor'](SECRET),
|
||||||
};
|
};
|
||||||
|
|
@ -68,7 +71,8 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<Loading v-if="$fetchState.pending" />
|
||||||
|
<div v-else>
|
||||||
<Masthead
|
<Masthead
|
||||||
:schema="schema"
|
:schema="schema"
|
||||||
:resource="resource"
|
:resource="resource"
|
||||||
|
|
|
||||||
114
store/plugins.js
114
store/plugins.js
|
|
@ -1,14 +1,72 @@
|
||||||
|
import { removeObjects } from '@/utils/array';
|
||||||
|
|
||||||
|
export function simplify(key) {
|
||||||
|
return key.toLowerCase().replace(/[^a-z0-9]/ig, '');
|
||||||
|
}
|
||||||
|
|
||||||
const credentialOptions = {
|
const credentialOptions = {
|
||||||
aws: { publicKey: 'accessKey', publicMode: 'full' },
|
aws: {
|
||||||
digitalocean: { publicKey: 'accessToken', publicMode: 'prefix' },
|
publicKey: 'accessKey',
|
||||||
azure: { publicKey: 'clientId', publicMode: 'full' },
|
publicMode: 'full',
|
||||||
|
keys: ['region', 'accessKey', 'secretKey']
|
||||||
|
},
|
||||||
|
digitalocean: {
|
||||||
|
publicKey: 'accessToken',
|
||||||
|
publicMode: 'prefix',
|
||||||
|
keys: 'accessToken'
|
||||||
|
},
|
||||||
|
azure: {
|
||||||
|
publicKey: 'clientId',
|
||||||
|
publicMode: 'full',
|
||||||
|
keys: ['subscriptionId', 'tenantId', 'clientId', 'clientSecret']
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const driverMap = {
|
||||||
|
amazonec2: 'aws',
|
||||||
|
amazoneks: 'aws',
|
||||||
|
aks: 'azure',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const likelyFields = [
|
||||||
|
'username', 'password',
|
||||||
|
'accesskey', 'secretkey',
|
||||||
|
'accesskeyid', 'secretkeyid', 'accesskeysecret',
|
||||||
|
'token', 'apikey',
|
||||||
|
'secret',
|
||||||
|
'clientid', 'clientsecret', 'subscriptionid', 'tenantid',
|
||||||
|
].map(x => simplify(x));
|
||||||
|
|
||||||
|
export const iffyFields = [
|
||||||
|
'location', 'region',
|
||||||
|
].map(x => simplify(x));
|
||||||
|
|
||||||
|
export const fullFields = [
|
||||||
|
'username',
|
||||||
|
'accesskey',
|
||||||
|
'accesskeyid',
|
||||||
|
'clientid'
|
||||||
|
].map(x => simplify(x));
|
||||||
|
|
||||||
|
export const prefixFields = [
|
||||||
|
'token',
|
||||||
|
'apikey',
|
||||||
|
'secret',
|
||||||
|
].map(x => simplify(x));
|
||||||
|
|
||||||
|
export const suffixFields = [
|
||||||
|
].map(x => simplify(x));
|
||||||
|
|
||||||
// Dynamically loaded drivers can call this eventually to register thier options
|
// Dynamically loaded drivers can call this eventually to register thier options
|
||||||
export function configureCredential(name, opt) {
|
export function configureCredential(name, opt) {
|
||||||
credentialOptions[name] = opt;
|
credentialOptions[name] = opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map a driver to a different credential name, e.g. amazonec2 and amazoneks both use the 'aws' credential type.
|
||||||
|
export function mapDriver(name, to) {
|
||||||
|
driverMap[name] = to;
|
||||||
|
}
|
||||||
|
|
||||||
export const state = function() {
|
export const state = function() {
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
@ -28,8 +86,14 @@ export const getters = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
credentialDriverFor() {
|
||||||
|
return (name) => {
|
||||||
|
return driverMap[name] || name;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
machineDrivers() {
|
machineDrivers() {
|
||||||
// The subset of drivers supported by Vue
|
// The subset of drivers supported by Vue components
|
||||||
const ctx = require.context('@/machine-config', true, /.*/);
|
const ctx = require.context('@/machine-config', true, /.*/);
|
||||||
|
|
||||||
const drivers = ctx.keys().filter(path => !path.match(/\.(vue|js)$/)).map(path => path.substr(2));
|
const drivers = ctx.keys().filter(path => !path.match(/\.(vue|js)$/)).map(path => path.substr(2));
|
||||||
|
|
@ -38,7 +102,47 @@ export const getters = {
|
||||||
},
|
},
|
||||||
|
|
||||||
clusterDrivers() {
|
clusterDrivers() {
|
||||||
// The subset of drivers supported by Vue
|
// The subset of drivers supported by Vue components
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
schemaForDriver(state, getters, rootState, rootGetters) {
|
||||||
|
return (name) => {
|
||||||
|
const id = `rke-machine-config.cattle.io.${ name }config`;
|
||||||
|
const schema = rootGetters['management/schemaFor'](id);
|
||||||
|
|
||||||
|
return schema;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
fieldNamesForDriver(state, getters) {
|
||||||
|
return (name) => {
|
||||||
|
const schema = getters.schemaForDriver(name);
|
||||||
|
|
||||||
|
if ( !schema ) {
|
||||||
|
throw new Error(`Machine Driver Config schema not found for ${ name }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const out = Object.keys(schema?.resourceFields || {});
|
||||||
|
|
||||||
|
removeObjects(out, ['apiVersion', 'dockerPort', 'kind', 'metadata']);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
fieldsForDriver(state, getters) {
|
||||||
|
return (name) => {
|
||||||
|
const schema = getters.schemaForDriver(name);
|
||||||
|
const names = getters.fieldNamesForDriver(name);
|
||||||
|
|
||||||
|
const out = {};
|
||||||
|
|
||||||
|
for ( const n of names ) {
|
||||||
|
out[n] = schema.resourceFields[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue