Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 93 additions & 32 deletions pkg/capi/edit/turtles-capi.cattle.io.capiprovider/ProviderConfig.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
import Banner from '@components/Banner/Banner.vue';
import { _EDIT, _CREATE } from '@shell/config/query-params';
import { ToggleSwitch } from '@components/Form/ToggleSwitch';

import { allHash } from '@shell/utils/promise';
import { PROVIDER_TYPES, RANCHER_TURTLES_SYSTEM_NAMESPACE, RANCHER_TURTLES_SYSTEM_NAME } from '../../types/capi';
import { providerNameValidator, providerVersionValidator, urlValidator } from '../../util/validators';
Expand Down Expand Up @@ -43,7 +45,8 @@ const customProviderSpec = {
const CATEGORIES = ['infrastructure', 'bootstrap', 'controlPlane', 'addon', 'ipam', 'runtimeextension', 'core'];

export default {
name: 'ProviderConfig',
name: 'ProviderConfig',

components: {
CruResource,
Loading,
Expand All @@ -53,19 +56,25 @@ export default {
KeyValue,
LabeledInput,
LabeledSelect,
Banner
Banner,
ToggleSwitch
},

mixins: [CreateEditView, FormValidation],
emits: ['update:value'],

emits: ['update:value'],

props: {
mode: {
type: String,
required: true,
},

value: {
type: Object,
required: true,
},

provider: {
type: String,
required: true,
Expand All @@ -75,6 +84,7 @@ export default {
default: 'infrastructure',
}
},

beforeMount() {
this.getDependencies().then((hash) => {
this.allNamespaces = hash.namespaces || [];
Expand All @@ -87,6 +97,7 @@ export default {
this.loading = false;
});
},

data() {
const name = this.provider;
const providerDetails = PROVIDER_TYPES.find((p) => p.name === name) || { disabled: false, id: '0' };
Expand All @@ -101,52 +112,73 @@ export default {
],
allNamespaces: [],
credentialComponent: providerDetails?.credential,

useCredential: !!providerDetails?.credentialRequired,
credentialRequired: providerDetails?.credentialRequired
};
},

watch: {
// if a user toggles off credentials, clear them from the spec
useCredential(neu) {
if (!neu && !this.credentialRequired) {
this.value.spec.credentials.rancherCloudCredentialNamespaceName = '';
}
}
},

computed: {
...mapGetters(['namespaces']),

fvExtraRules() {
return {
name: providerNameValidator(this.$store.getters['i18n/t']),
version: providerVersionValidator(this.$store.getters['i18n/t']),
url: urlValidator(this.$store.getters['i18n/t'])
};
},

typeOptions() {
return CATEGORIES.map((type) => {
return { label: this.t(`capi.provider.type.${ type }.label`), value: type };
});
},
showForm() {
return !!this.value?.spec?.credentials?.rancherCloudCredentialNamespaceName || !this.credentialComponent;
},

isEdit() {
return this.mode === _EDIT;
},

isCreate() {
return this.mode === _CREATE;
},

hasFeatures() {
return !!this.value?.spec?.features;
},

hasVariables() {
return !!this.value?.spec?.variables;
},

isCustom() {
return this.provider === 'custom';
},

shouldShowBanner() {
return this.isEdit && (this.hasFeatures || this.hasVariables);
},

waitingForCredential() {
return this.credentialComponent && !this.value.spec.credentials?.rancherCloudCredentialNamespaceName;
},
rancherCloudCredentialNamespaceName() {
return this.value.spec?.credentials?.rancherCloudCredentialNamespaceName || '';
return this.useCredential && this.credentialComponent && !this.value.spec?.credentials?.rancherCloudCredentialNamespaceName;
},

providerDisplayName() {
return this.t(`capi.provider.providerDisplayNames.${ this.provider }`) || this.provider;
}
},

methods: {
initSpecs() {
if ( !this.value.spec ) {
Expand All @@ -164,6 +196,7 @@ export default {
set(this.value.spec.configSecret, 'name', this.generateName(this.provider)); // Defines the name of the secret that will be created or adjusted based on the content of the spec.features and spec.variables.
}
},

getSpecFromCoreSecret() {
const coreProviderSecretData = this.coreProviderSecret?.data;

Expand Down Expand Up @@ -191,9 +224,11 @@ export default {
variables: clone(defaultVariables)
};
},

generateName(name) {
return name ? `${ name }-credentials-${ randomStr(5).toLowerCase() }` : undefined;
},

async getDependencies() {
const inStore = this.$store.getters['currentStore'](NAMESPACE);
const { $store } = this;
Expand All @@ -211,7 +246,7 @@ export default {
if ( this.errors ) {
clear(this.errors);
}
if ( !this.credentialComponent && !this.value.spec?.credentials?.rancherCloudCredentialNamespaceName ) {
if ( !this.useCredential && !this.value.spec?.credentials?.rancherCloudCredentialNamespaceName ) {
this.value.spec.credentials = null;
}
if (this.value?.spec?.version === '') {
Expand All @@ -224,9 +259,14 @@ export default {
btnCb(false);
}
},

cancelCredential() {
if ( this.$refs.providercruresource ) {
this.$refs.providercruresource.emitOrRoute();
if (this.credentialRequired) {
if ( this.$refs.providercruresource ) {
this.$refs.providercruresource.emitOrRoute();
}
} else {
this.useCredential = false;
}
}
}
Expand Down Expand Up @@ -324,31 +364,14 @@ export default {
</div>
</div>
</div>
<div
v-if="credentialComponent"
class="mb-40"
/>
<h2
v-if="hasFeatures || hasVariables"
class="mb-20"
>
<t k="capi.provider.secret.title" />
</h2>
<div v-if="credentialComponent">
<h3 class="mb-20">
<t k="capi.provider.cloudCredential.title" />
</h3>
<SelectCredential
v-model:value="rancherCloudCredentialNamespaceName"
:mode="mode"
:provider="credentialComponent"
:cancel="cancelCredential"
:showing-form="showForm"
class="mb-40"
@update:value="$emit('update:value', {k: 'spec.credentials.rancherCloudCredentialNamespaceName', val: $event})"
/>
</div>
<div v-if="!waitingForCredential">

<div class="mb-20">
<Banner
v-if="shouldShowBanner"
color="info"
Expand Down Expand Up @@ -399,11 +422,49 @@ export default {
/>
</div>
</div>
<div
v-if="credentialComponent"
class="credential-container"
>
<h3 class="mt-20">
<t k="capi.provider.cloudCredential.title" />
</h3>
<div
v-if="!credentialRequired"
class="row mt-10"
>
<ToggleSwitch
v-model:value="useCredential"
name="credential-toggle"
:on-label="t('capi.provider.cloudCredential.toggle', {provider: providerDisplayName})"
/>
</div>

<template v-if="useCredential">
<SelectCredential
v-model:value="value.spec.credentials.rancherCloudCredentialNamespaceName"
:mode="mode"
:provider="credentialComponent"
:cancel="cancelCredential"
:showing-form="false"
class="credential"
@update:value="$emit('update:value', {k: 'spec.credentials.rancherCloudCredentialNamespaceName', val: $event})"
/>
</template>
</div>
<template
v-if="waitingForCredential"
v-if="waitingForCredential && useCredential"
#form-footer
>
<div><!-- Hide the outer footer --></div>
<div></div>
</template>
</CruResource>
</template>

<style lang="scss" scoped>
.credential-container {
flex: 1 1 100%;
display: flex;
flex-direction: column;
}
</style>
4 changes: 2 additions & 2 deletions pkg/capi/edit/turtles-capi.cattle.io.capiprovider/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export default {
return out;

function addType(id, name, category, disabled = false) {
const label = getters['i18n/withFallback'](`cluster.provider."${ name }"`, null, name);
const label = getters['i18n/withFallback'](`capi.provider.providerDisplayNames."${ name }"`, null, name);
let icon;

try {
Expand Down Expand Up @@ -164,7 +164,7 @@ export default {
this.subType = name;
this.category = category;

this.$emit('set-subtype', this.$store.getters['i18n/withFallback'](`cluster.provider."${ name }"`, null, name));
this.$emit('set-subtype', this.$store.getters['i18n/withFallback'](`capi.provider.providerDisplayNames."${ name }"`, null, name));
},
},
};
Expand Down
16 changes: 12 additions & 4 deletions pkg/capi/l10n/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ capi:
placeholder: https://github.com/example/releases/latest/client.yaml
cloudCredential:
title: Cloud Credential
toggle: Use a Rancher cloud credential to configure access for all {provider} CAPI clusters
features:
title: Features
clusterResourceSet: Enable cluster resource set
Expand All @@ -142,10 +143,17 @@ capi:
create: Create new secret
reuse: Use core provider secret
data: Data
label: Secret Name
cluster:
provider:
kubeadm: Kubeadm
label: Secret Name
providerDisplayNames:
digitalocean: Digital Ocean
aws: Amazon
azure: Azure
docker: Docker
gcp: Google Cloud Platform
vsphere: vSphere
kubeadm: Kubeadm
rke2: RKE2

nav:
group:
CAPITurtles: CAPI
Expand Down
10 changes: 5 additions & 5 deletions pkg/capi/types/capi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,22 @@ export const CREDENTIALS_NOT_REQUIRED = ['docker'];

export const PROVIDER_TYPES = [
{
name: 'aws', type: 'infrastructure', disabled: false, credential: 'aws'
name: 'aws', type: 'infrastructure', disabled: false, credential: 'aws', credentialRequired: true
},
{
name: 'azure', type: 'infrastructure', disabled: false, credential: 'azure'
name: 'azure', type: 'infrastructure', disabled: false, credential: 'azure', credentialRequired: false
},
{
name: 'digitalocean', type: 'infrastructure', disabled: false, credential: 'digitalocean'
name: 'digitalocean', type: 'infrastructure', disabled: false, credential: 'digitalocean', credentialRequired: true
},
{
name: 'docker', type: 'infrastructure', disabled: false
},
{
name: 'gcp', type: 'infrastructure', disabled: false, credential: 'gcp'
name: 'gcp', type: 'infrastructure', disabled: false, credential: 'gcp', credentialRequired: true
},
{
name: 'vsphere', type: 'infrastructure', disabled: false, credential: 'vmwarevsphere'
name: 'vsphere', type: 'infrastructure', disabled: false, credential: 'vmwarevsphere', credentialRequired: true
},
{
name: 'rke2', type: 'bootstrap', disabled: false
Expand Down