diff --git a/assets/images/providers/gcp.svg b/assets/images/providers/gcp.svg new file mode 100644 index 0000000000..d8ba0b39ef --- /dev/null +++ b/assets/images/providers/gcp.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/providers/oracle.svg b/assets/images/providers/oracle.svg new file mode 100644 index 0000000000..832aad49d8 --- /dev/null +++ b/assets/images/providers/oracle.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/translations/en-us.yaml b/assets/translations/en-us.yaml index 84514cf555..bd7b198f94 100644 --- a/assets/translations/en-us.yaml +++ b/assets/translations/en-us.yaml @@ -946,6 +946,21 @@ cluster: password: label: Password note: 'Note: The free ESXi license does not support API access. Only servers with a valid or evaluation license are supported.' + gcp: + authEncodedJson: + label: Service Account + placeholder: Service Account private key JSON file + help: |- +

Create a Service Account with a JSON private key and provide the JSON here. + These IAM roles are required:

+ + More info on roles can be found here. + harvester: import: Imported Harvester external: External Harvester @@ -1179,7 +1194,7 @@ cluster: aliyun: Alibaba ACK amazonec2: Amazon EC2 amazoneks: Amazon EKS - aws: Amazon AWS + aws: Amazon azure: Azure azureaks: Azure AKS aks: Azure AKS @@ -1192,9 +1207,11 @@ cluster: docker: Docker eks: Amazon EKS exoscale: Exoscale + gcp: Google google: Google GCE googlegke: Google GKE gke: Google GKE + harvester: Harvester huaweicce: Huawei CCE import: Generic imported: Imported @@ -1208,6 +1225,7 @@ cluster: openstack: OpenStack opentelekomcloudcontainerengine: Open Telekom Cloud CCE otccce: Open Telekom Cloud CCE + oracle: Oracle oracleoke: Oracle OKE otc: Open Telekom Cloud other: Other @@ -1222,7 +1240,7 @@ cluster: softlayer: IBM Cloud tencenttke: Tencent TKE upcloud: UpCloud - vmwarevsphere: vSphere + vmwarevsphere: VMware vSphere zstack: ZStack providerGroup: create-custom1: Use existing nodes and create a cluster using RKE @@ -4632,16 +4650,21 @@ typeLabel: one { Namespaced Repo } other { Namespaced Repos } } - chartInstallAction: |- + chartinstallaction: |- {count, plural, one { App } other { Apps } } - chartUpgradeAction: |- + chartupgradeaction: |- {count, plural, one { App } other { Apps } } + cloudcredential: |- + {count, plural, + one { Cloud Credential } + other { Cloud Credentials } + } endpoints: |- {count, plural, one { Endpoint } diff --git a/cloud-credential/gcp.vue b/cloud-credential/gcp.vue new file mode 100644 index 0000000000..4d01469bc9 --- /dev/null +++ b/cloud-credential/gcp.vue @@ -0,0 +1,64 @@ + + + diff --git a/cloud-credential/generic.vue b/cloud-credential/generic.vue index b247e8da57..d51e429590 100644 --- a/cloud-credential/generic.vue +++ b/cloud-credential/generic.vue @@ -17,7 +17,16 @@ export default { }, data() { - const keyOptions = this.$store.getters['plugins/fieldNamesForDriver'](this.driverName); + let keyOptions = []; + + const normanType = this.$store.getters['plugins/credentialFieldForDriver'](this.driverName); + const normanSchema = this.$store.getters['rancher/schemaFor'](`${ normanType }credentialconfig`); + + if ( normanSchema ) { + keyOptions = Object.keys(normanSchema.resourceFields || {}); + } else { + 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 @@ -27,7 +36,7 @@ export default { for ( const k of keyOptions ) { const sk = simplify(k); - if ( likelyFields.includes(sk) || iffyFields.includes(sk) ) { + if ( normanSchema || likelyFields.includes(sk) || iffyFields.includes(sk) ) { keys.push(k); } } @@ -40,8 +49,9 @@ export default { } return { + hasSupport: !!normanSchema, keyOptions, - errors: null, + errors: null, }; }, @@ -62,12 +72,15 @@ export default { diff --git a/edit/provisioning.cattle.io.cluster/SelectCredential.vue b/edit/provisioning.cattle.io.cluster/SelectCredential.vue index 57e9f02fc0..d7bcb6226c 100644 --- a/edit/provisioning.cattle.io.cluster/SelectCredential.vue +++ b/edit/provisioning.cattle.io.cluster/SelectCredential.vue @@ -1,7 +1,7 @@ - - diff --git a/machine-config/amazonec2.vue b/machine-config/amazonec2.vue index f2c8c320a8..c58d97054b 100644 --- a/machine-config/amazonec2.vue +++ b/machine-config/amazonec2.vue @@ -8,7 +8,7 @@ import KeyValue from '@/components/form/KeyValue'; import UnitInput from '@/components/form/UnitInput'; import RadioGroup from '@/components/form/RadioGroup'; import Checkbox from '@/components/form/Checkbox'; -import { SECRET } from '@/config/types'; +import { NORMAN } from '@/config/types'; import { allHash } from '@/utils/promise'; import { addObject, addObjects, findBy } from '@/utils/array'; import { sortBy } from '@/utils/sort'; @@ -43,7 +43,7 @@ export default { try { if ( this.credential?.id !== this.credentialId ) { - this.credential = await this.$store.dispatch('management/find', { type: SECRET, id: this.credentialId }); + this.credential = await this.$store.dispatch('rancher/find', { type: NORMAN.CLOUD_CREDENTIAL, id: this.credentialId }); } if ( !this.instanceInfo ) { diff --git a/machine-config/azure.vue b/machine-config/azure.vue index 579392bcb8..2bf126ff84 100644 --- a/machine-config/azure.vue +++ b/machine-config/azure.vue @@ -1,7 +1,7 @@ + + diff --git a/pages/c/_cluster/manager/cloudcredential/create.vue b/pages/c/_cluster/manager/cloudcredential/create.vue new file mode 100644 index 0000000000..3be265e211 --- /dev/null +++ b/pages/c/_cluster/manager/cloudcredential/create.vue @@ -0,0 +1,16 @@ + + + diff --git a/pages/c/_cluster/manager/secret.vue b/pages/c/_cluster/manager/cloudcredential/index.vue similarity index 59% rename from pages/c/_cluster/manager/secret.vue rename to pages/c/_cluster/manager/cloudcredential/index.vue index d0edbebed9..087f081997 100644 --- a/pages/c/_cluster/manager/secret.vue +++ b/pages/c/_cluster/manager/cloudcredential/index.vue @@ -2,8 +2,8 @@ import Loading from '@/components/Loading'; import ResourceTable from '@/components/ResourceTable'; import Masthead from '@/components/ResourceList/Masthead'; -import { SECRET } from '@/config/types'; -import { AGE, NAME, STATE } from '@/config/table-headers'; +import { NORMAN, SECRET } from '@/config/types'; +import { AGE_NORMAN, DESCRIPTION, NAME_UNLINKED } from '@/config/table-headers'; import { CLOUD_CREDENTIAL, _FLAGGED } from '@/config/query-params'; export default { @@ -13,48 +13,41 @@ export default { async fetch() { this.allSecrets = await this.$store.dispatch('management/findAll', { type: SECRET }); + this.allCredentials = await this.$store.dispatch('rancher/findAll', { type: NORMAN.CLOUD_CREDENTIAL }); }, data() { return { - allSecrets: null, - resource: SECRET, - schema: this.$store.getters['management/schemaFor'](SECRET), + allCredentials: null, + resource: NORMAN.CLOUD_CREDENTIAL, + schema: this.$store.getters['rancher/schemaFor'](NORMAN.CLOUD_CREDENTIAL), }; }, computed: { rows() { - return this.allSecrets.filter(x => x.isCloudCredential); + return this.allCredentials || []; }, headers() { return [ - STATE, - NAME, - { - name: 'provider', - label: 'Provider', - value: 'cloudCredentialProviderDisplay', - sort: 'cloudCredentialProviderDisplay', - search: 'cloudCredentialProviderDisplay', - dashIfEmpty: true, - }, + NAME_UNLINKED, { name: 'apikey', label: 'API Key', - value: 'cloudCredentialPublicData', - sort: 'cloudCredentialPublicData', - search: 'cloudCredentialPublicData', + value: 'publicData', + sort: 'publicData', + search: 'publicData', dashIfEmpty: true, }, - AGE + DESCRIPTION, + AGE_NORMAN, ]; }, createLocation() { return { - name: 'c-cluster-product-resource-create', + name: 'c-cluster-manager-cloudCredential-create', params: { product: this.$store.getters['currentProduct'].name, resource: this.resource @@ -80,9 +73,10 @@ export default { type-display="Cloud Credentials" /> - + diff --git a/pages/prefs.vue b/pages/prefs.vue index d1d425a23c..75c79ac4a0 100644 --- a/pages/prefs.vue +++ b/pages/prefs.vue @@ -209,8 +209,8 @@ export default {
-
-
+
+

diff --git a/plugins/steve/resource-instance.js b/plugins/steve/resource-instance.js index f163b6327a..99d5377a2e 100644 --- a/plugins/steve/resource-instance.js +++ b/plugins/steve/resource-instance.js @@ -320,7 +320,7 @@ export default { }, nameDisplay() { - return this.displayName || this.spec?.displayName || this.metadata?.annotations?.[NORMAN_NAME] || this.metadata?.name || this.name || this.id; + return this.displayName || this.spec?.displayName || this.metadata?.annotations?.[NORMAN_NAME] || this.name || this.metadata?.name || this.id; }, nameSort() { @@ -883,6 +883,10 @@ export default { opt.data.type = opt.data._type; } + if (opt?.data._name) { + opt.data.name = opt.data._name; + } + try { const res = await this.$dispatch('request', opt); diff --git a/store/plugins.js b/store/plugins.js index 7052eaa795..459e977134 100644 --- a/store/plugins.js +++ b/store/plugins.js @@ -22,10 +22,36 @@ const credentialOptions = { }, }; +export const rke1Supports = [ + 'aws', + 'azure', + 'digitalocean', + 'gcp', + 'harvester', + 'linode', + 'oracle', + 'pnap', + 'vmwarevsphere' +]; + const driverMap = { - amazonec2: 'aws', - amazoneks: 'aws', - aks: 'azure', + aks: 'azure', + amazonec2: 'aws', + amazoneks: 'aws', + amazonelasticcontainerservice: 'aws', + azurekubernetesservice: 'azure', + google: 'gcp', + googlekubernetesengine: 'gcp', + huaweicontainercloudengine: 'huawei', + linodekubernetesengine: 'linode', + oci: 'oracle', + opentelekomcloudcontainerengine: 'otc', + oraclecontainerengine: 'oracle', +}; + +const driverToFieldMap = { + aws: 'amazonec2', + gcp: 'google', }; export const likelyFields = [ @@ -82,16 +108,28 @@ export const getters = { credentialOptions() { return (name) => { + name = (name || '').toLowerCase(); + return credentialOptions[name] || {}; }; }, credentialDriverFor() { return (name) => { + name = (name || '').toLowerCase(); + return driverMap[name] || name; }; }, + credentialFieldForDriver() { + return (name) => { + name = (name || '').toLowerCase(); + + return driverToFieldMap[name] || name; + }; + }, + machineDrivers() { // The subset of drivers supported by Vue components const ctx = require.context('@/machine-config', true, /.*/); @@ -120,7 +158,10 @@ export const getters = { const schema = getters.schemaForDriver(name); if ( !schema ) { - throw new Error(`Machine Driver Config schema not found for ${ name }`); + // eslint-disable-next-line no-console + console.error(`Machine Driver Config schema not found for ${ name }`); + + return []; } const out = Object.keys(schema?.resourceFields || {}); diff --git a/store/type-map.js b/store/type-map.js index 85fde61772..51c3ed9e58 100644 --- a/store/type-map.js +++ b/store/type-map.js @@ -337,7 +337,7 @@ export const getters = { labelFor(state, getters, rootState, rootGetters) { return (schema, count = 1) => { return _applyMapping(schema, state.typeMappings, 'id', false, () => { - const key = `typeLabel."${ schema.id }"`; + const key = `typeLabel."${ schema.id.toLowerCase() }"`; if ( rootGetters['i18n/exists'](key) ) { return rootGetters['i18n/t'](key, { count }).trim();