mirror of https://github.com/rancher/dashboard.git
Merge pull request #3632 from vincent99/master
[Master] Use Norman cloud creds for all the things
This commit is contained in:
commit
d3f40fef23
|
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="206" viewBox="0 0 256 206">
|
||||
<path fill="#EA4335" d="M170.2517,56.8186 L192.5047,34.5656 L193.9877,25.1956 C153.4367,-11.6774 88.9757,-7.4964 52.4207,33.9196 C42.2667,45.4226 34.7337,59.7636 30.7167,74.5726 L38.6867,73.4496 L83.1917,66.1106 L86.6277,62.5966 C106.4247,40.8546 139.8977,37.9296 162.7557,56.4286 L170.2517,56.8186 Z"/>
|
||||
<path fill="#4285F4" d="M224.2048,73.9182 C219.0898,55.0822 208.5888,38.1492 193.9878,25.1962 L162.7558,56.4282 C175.9438,67.2042 183.4568,83.4382 183.1348,100.4652 L183.1348,106.0092 C198.4858,106.0092 210.9318,118.4542 210.9318,133.8052 C210.9318,149.1572 198.4858,161.2902 183.1348,161.2902 L127.4638,161.2902 L121.9978,167.2242 L121.9978,200.5642 L127.4638,205.7952 L183.1348,205.7952 C223.0648,206.1062 255.6868,174.3012 255.9978,134.3712 C256.1858,110.1682 244.2528,87.4782 224.2048,73.9182"/>
|
||||
<path fill="#34A853" d="M71.8704,205.7957 L127.4634,205.7957 L127.4634,161.2897 L71.8704,161.2897 C67.9094,161.2887 64.0734,160.4377 60.4714,158.7917 L52.5844,161.2117 L30.1754,183.4647 L28.2234,191.0387 C40.7904,200.5277 56.1234,205.8637 71.8704,205.7957"/>
|
||||
<path fill="#FBBC05" d="M71.8704,61.4250342 C31.9394,61.6635 -0.2366,94.2275 0.0014,134.1575 C0.1344,156.4555 10.5484,177.4455 28.2234,191.0385 L60.4714,158.7915 C46.4804,152.4705 40.2634,136.0055 46.5844,122.0155 C52.9044,108.0255 69.3704,101.8085 83.3594,108.1285 C89.5244,110.9135 94.4614,115.8515 97.2464,122.0155 L129.4944,89.7685 C115.7734,71.8315 94.4534,61.3445 71.8704,61.4250342"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.0"
|
||||
width="662.84644"
|
||||
height="94.145668"
|
||||
id="svg115845">
|
||||
<defs
|
||||
id="defs115847">
|
||||
<clipPath
|
||||
id="clp82">
|
||||
<path
|
||||
d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z "
|
||||
id="path1826" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clp83">
|
||||
<path
|
||||
d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z "
|
||||
id="path1835" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clp84">
|
||||
<path
|
||||
d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z "
|
||||
id="path1844" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clp81">
|
||||
<path
|
||||
d="M 1000.9,934.34 L 1038.6,934.34 L 1038.6,922.24 L 1000.9,922.24 L 1000.9,934.34 z "
|
||||
id="path1800" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath116030">
|
||||
<path
|
||||
d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z "
|
||||
id="path116032" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath116038">
|
||||
<path
|
||||
d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z "
|
||||
id="path116040" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath116046">
|
||||
<path
|
||||
d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z "
|
||||
id="path116048" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g
|
||||
transform="translate(-702.6538,-712.5837)"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g16337">
|
||||
<path
|
||||
d="M 980.65099,771.70039 L 1021.3029,771.70039 L 999.80762,737.1177 L 960.35637,799.64472 L 942.40142,799.64472 L 990.38729,724.53644 C 992.47368,721.50175 995.95082,719.66834 999.80762,719.66834 C 1003.5375,719.66834 1007.0147,721.43856 1009.0379,724.41 L 1057.2134,799.64472 L 1039.2584,799.64472 L 1030.7865,785.67256 L 989.62847,785.67256 L 980.65099,771.70039 z M 1167.1573,785.67256 L 1167.1573,720.42701 L 1151.9207,720.42701 L 1151.9207,792.05805 C 1151.9207,794.01795 1152.6795,795.9146 1154.1335,797.3687 C 1155.5874,798.82285 1157.5474,799.64472 1159.697,799.64472 L 1229.1786,799.64472 L 1238.1561,785.67256 L 1167.1573,785.67256 z M 915.08933,773.97641 C 929.88361,773.97641 941.89588,762.02739 941.89588,747.23331 C 941.89588,732.43928 929.88361,720.42701 915.08933,720.42701 L 848.43367,720.42701 L 848.43367,799.64472 L 863.66423,799.64472 L 863.66423,734.39918 L 914.07773,734.39918 C 921.15891,734.39918 926.84882,740.15238 926.84882,747.23331 C 926.84882,754.31423 921.15891,760.06749 914.07773,760.06749 L 871.12457,760.00424 L 916.60647,799.64472 L 938.7347,799.64472 L 908.13505,773.97641 L 915.08933,773.97641 z M 754.67521,799.64472 C 732.80632,799.64472 715.05966,781.94244 715.05966,760.06749 C 715.05966,738.19249 732.80632,720.42701 754.67521,720.42701 L 800.71978,720.42701 C 822.59473,720.42701 840.32876,738.19249 840.32876,760.06749 C 840.32876,781.94244 822.59473,799.64472 800.71978,799.64472 L 754.67521,799.64472 z M 799.69555,785.67256 C 813.86396,785.67256 825.33883,774.22928 825.33883,760.06749 C 825.33883,745.90564 813.86396,734.39918 799.69555,734.39918 L 755.69287,734.39918 C 741.53103,734.39918 730.04958,745.90564 730.04958,760.06749 C 730.04958,774.22928 741.53103,785.67256 755.69287,785.67256 L 799.69555,785.67256 z M 1089.0142,799.64472 C 1067.1392,799.64472 1049.3739,781.94244 1049.3739,760.06749 C 1049.3739,738.19249 1067.1392,720.42701 1089.0142,720.42701 L 1143.7016,720.42701 L 1134.7873,734.39918 L 1090.0258,734.39918 C 1075.8639,734.39918 1064.3577,745.90564 1064.3577,760.06749 C 1064.3577,774.22928 1075.8639,785.67256 1090.0258,785.67256 L 1144.9659,785.67256 L 1135.9885,799.64472 L 1089.0142,799.64472 z M 1275.3309,785.67256 C 1263.6346,785.67256 1253.7087,777.83296 1250.6739,767.02192 L 1315.7932,767.02192 L 1324.7707,753.04976 L 1250.6739,753.04976 C 1253.7087,742.30196 1263.6346,734.39918 1275.3309,734.39918 L 1320.0292,734.39918 L 1329.0699,720.42701 L 1274.3193,720.42701 C 1252.4443,720.42701 1234.679,738.19249 1234.679,760.06749 C 1234.679,781.94244 1252.4443,799.64472 1274.3193,799.64472 L 1321.2936,799.64472 L 1330.271,785.67256 L 1275.3309,785.67256"
|
||||
style="fill:#f80000;fill-rule:nonzero;stroke:none"
|
||||
id="path16197" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
|
|
@ -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: |-
|
||||
<p>Create a <a href="https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts" target="_blank" rel="noopener noreferrer nofollow">Service Account</a> with a JSON private key and provide the JSON here.
|
||||
These IAM roles are required:</p>
|
||||
<ul>
|
||||
<li><b>Compute Engine:</b> Compute Viewer (roles/compute.viewer)</li>
|
||||
<li><b>Project:</b> Viewer (roles/viewer)</li>
|
||||
<li><b>Kubernetes Engine:</b> Kubernetes Engine Admin (roles/container.admin)</li>
|
||||
<li><b>Service Accounts:</b> Service Account User (roles/iam.serviceAccountUser)</li>
|
||||
</ul>
|
||||
More info on roles can be found <a href="https://cloud.google.com/kubernetes-engine/docs/how-to/iam-integration" target="_blank" rel="noopener noreferrer nofollow">here</a>.
|
||||
|
||||
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 }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
<script>
|
||||
import CreateEditView from '@/mixins/create-edit-view';
|
||||
import LabeledInput from '@/components/form/LabeledInput';
|
||||
import FileSelector from '@/components/form/FileSelector';
|
||||
|
||||
export default {
|
||||
components: { LabeledInput, FileSelector },
|
||||
mixins: [CreateEditView],
|
||||
|
||||
watch: {
|
||||
'value.decodedData.authEncodedJson'(neu) {
|
||||
this.$emit('validationChanged', !!neu);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onFileSelected(data) {
|
||||
this.value.setData('authEncodedJson', data);
|
||||
},
|
||||
|
||||
async test() {
|
||||
let credentials = null;
|
||||
let config = null;
|
||||
let projectId = null;
|
||||
|
||||
try {
|
||||
credentials = this.value.decodedData.authEncodedJson;
|
||||
config = JSON.parse(credentials || '{}');
|
||||
projectId = config?.project_id;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.$store.dispatch('management/request', {
|
||||
url: '/meta/gkeZones',
|
||||
method: 'POST',
|
||||
data: { credentials, projectId },
|
||||
redirectUnauthorized: false,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<LabeledInput
|
||||
:value="value.decodedData.authEncodedJson"
|
||||
label-key="cluster.credential.gcp.authEncodedJson.label"
|
||||
placeholder-key="cluster.credential.gcp.authEncodedJson.placeholder"
|
||||
type="multiline"
|
||||
:mode="mode"
|
||||
@input="value.setData('authEncodedJson', $event);"
|
||||
/>
|
||||
<FileSelector class="role-primary btn-sm mt-20 mb-20" :label="t('generic.readFromFile')" @selected="onFileSelected" />
|
||||
<p class="text-muted" v-html="t('cluster.credential.gcp.authEncodedJson.help', {}, true)" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -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 {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<Banner color="info" label-key="cluster.selectCredential.genericDescription" class="mt-0" />
|
||||
<Banner v-if="!hasSupport" color="info" label-key="cluster.selectCredential.genericDescription" class="mt-0" />
|
||||
<KeyValue
|
||||
:value="value.decodedData"
|
||||
:key-options="keyOptions"
|
||||
:key-options="hasSupport || !keyOptions.length ? null : keyOptions"
|
||||
:key-editable="!hasSupport"
|
||||
:mode="mode"
|
||||
:read-allowed="true"
|
||||
:add-allowed="!hasSupport"
|
||||
:remove-allowed="!hasSupport"
|
||||
:initial-empty-row="true"
|
||||
@input="update"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ export default {
|
|||
parentRouteOverride: {
|
||||
type: String,
|
||||
default: null,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@ export default {
|
|||
},
|
||||
},
|
||||
|
||||
keyEditable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
|
||||
// Offer a set of suggestions for the keys as a Select instead of Input
|
||||
keyOptions: {
|
||||
type: Array,
|
||||
|
|
@ -523,7 +528,7 @@ export default {
|
|||
v-else
|
||||
ref="key"
|
||||
v-model="row[keyName]"
|
||||
:disabled="isView"
|
||||
:disabled="isView || !keyEditable"
|
||||
:placeholder="keyPlaceholder"
|
||||
@input="queueUpdate"
|
||||
@paste="onPaste(i, $event)"
|
||||
|
|
|
|||
|
|
@ -69,8 +69,10 @@ export default {
|
|||
};
|
||||
});
|
||||
|
||||
if (this.clusterFilter.length > 0) {
|
||||
out = out.filter(item => item.label.indexOf(this.clusterFilter) === 0);
|
||||
const search = (this.clusterFilter || '').toLowerCase();
|
||||
|
||||
if ( search ) {
|
||||
out = out.filter(item => item.label.toLowerCase().includes(search));
|
||||
}
|
||||
|
||||
const sorted = sortBy(out, ['ready:desc', 'label']);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { AGE, NAME as NAME_COL, STATE } from '@/config/table-headers';
|
||||
import { CAPI } from '@/config/types';
|
||||
import { CAPI, NORMAN } from '@/config/types';
|
||||
import { MULTI_CLUSTER } from '@/store/features';
|
||||
import { DSL } from '@/store/type-map';
|
||||
|
||||
|
|
@ -12,7 +12,8 @@ export function init(store) {
|
|||
headers,
|
||||
configureType,
|
||||
virtualType,
|
||||
weightType
|
||||
weightType,
|
||||
weightGroup
|
||||
} = DSL(store, NAME);
|
||||
|
||||
product({
|
||||
|
|
@ -24,8 +25,40 @@ export function init(store) {
|
|||
showClusterSwitcher: false,
|
||||
});
|
||||
|
||||
virtualType({
|
||||
name: 'cloud-credentials',
|
||||
label: 'Cloud Credentials',
|
||||
group: 'Root',
|
||||
namespaced: false,
|
||||
icon: 'globe',
|
||||
weight: 99,
|
||||
route: { name: 'c-cluster-manager-cloudCredential' },
|
||||
});
|
||||
|
||||
virtualType({
|
||||
labelKey: 'legacy.psps',
|
||||
name: 'pod-security-policies',
|
||||
group: 'Root',
|
||||
namespaced: false,
|
||||
weight: 0,
|
||||
icon: 'folder',
|
||||
route: { name: 'c-cluster-manager-pages-page', params: { cluster: 'local', page: 'pod-security-policies' } },
|
||||
exact: true
|
||||
});
|
||||
|
||||
basicType([
|
||||
CAPI.RANCHER_CLUSTER,
|
||||
'cloud-credentials',
|
||||
'drivers',
|
||||
'pod-security-policies'
|
||||
]);
|
||||
|
||||
configureType(CAPI.RANCHER_CLUSTER, { showListMasthead: false, namespaced: false });
|
||||
// configureType(NORMAN.CLOUD_CREDENTIAL, { showListMasthead: false, namespaced: false });
|
||||
weightType(CAPI.RANCHER_CLUSTER, 100, true);
|
||||
configureType(NORMAN.CLOUD_CREDENTIAL, {
|
||||
showState: false, showAge: false, canYaml: false
|
||||
});
|
||||
|
||||
virtualType({
|
||||
label: 'Drivers',
|
||||
|
|
@ -47,16 +80,6 @@ export function init(store) {
|
|||
exact: true
|
||||
});
|
||||
|
||||
virtualType({
|
||||
label: 'Cloud Credentials',
|
||||
name: 'rke-cloud-credentials',
|
||||
group: 'Root',
|
||||
namespaced: false,
|
||||
icon: 'globe',
|
||||
route: { name: 'c-cluster-manager-pages-page', params: { cluster: 'local', page: 'cloud-credentials' } },
|
||||
exact: true
|
||||
});
|
||||
|
||||
virtualType({
|
||||
label: 'Node Templates',
|
||||
name: 'rke-node-templates',
|
||||
|
|
@ -69,7 +92,6 @@ export function init(store) {
|
|||
|
||||
basicType([
|
||||
'rke-templates',
|
||||
'rke-cloud-credentials',
|
||||
'rke-node-templates'
|
||||
], 'RKE1 Configuration');
|
||||
|
||||
|
|
@ -77,40 +99,14 @@ export function init(store) {
|
|||
weightType(CAPI.MACHINE_SET, 2, true);
|
||||
weightType(CAPI.MACHINE, 1, true);
|
||||
|
||||
virtualType({
|
||||
label: 'Cloud Credentials',
|
||||
group: 'Root',
|
||||
namespaced: false,
|
||||
icon: 'globe',
|
||||
name: 'secret',
|
||||
weight: 99,
|
||||
route: { name: 'c-cluster-manager-secret' },
|
||||
});
|
||||
|
||||
basicType([
|
||||
CAPI.RANCHER_CLUSTER,
|
||||
'secret',
|
||||
'drivers',
|
||||
]);
|
||||
|
||||
virtualType({
|
||||
labelKey: 'legacy.psps',
|
||||
name: 'pod-security-policies',
|
||||
group: 'Root',
|
||||
namespaced: false,
|
||||
weight: 0,
|
||||
icon: 'folder',
|
||||
route: { name: 'c-cluster-manager-pages-page', params: { cluster: 'local', page: 'pod-security-policies' } },
|
||||
exact: true
|
||||
});
|
||||
|
||||
basicType([
|
||||
CAPI.MACHINE_DEPLOYMENT,
|
||||
CAPI.MACHINE_SET,
|
||||
CAPI.MACHINE,
|
||||
'pod-security-policies'
|
||||
], 'Advanced');
|
||||
|
||||
weightGroup('Advanced', -1, true);
|
||||
|
||||
const MACHINE_SUMMARY = {
|
||||
name: 'summary',
|
||||
labelKey: 'tableHeaders.machines',
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export const NORMAN = {
|
|||
ETCD_BACKUP: 'etcdbackup',
|
||||
CLUSTER_TOKEN: 'clusterregistrationtoken',
|
||||
CLUSTER_ROLE_TEMPLATE_BINDING: 'clusterRoleTemplateBinding',
|
||||
CLOUD_CREDENTIAL: 'cloudcredential',
|
||||
GROUP: 'group',
|
||||
// Note - This allows access to node resources, not schema's or custom components (both are accessed via 'type' which clashes with kube node)
|
||||
NODE: 'node',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,243 @@
|
|||
<script>
|
||||
import { TYPES } from '@/models/secret';
|
||||
import { MANAGEMENT, NORMAN } from '@/config/types';
|
||||
import CreateEditView from '@/mixins/create-edit-view';
|
||||
import NameNsDescription from '@/components/form/NameNsDescription';
|
||||
import CruResource from '@/components/CruResource';
|
||||
import { _CREATE } from '@/config/query-params';
|
||||
import Loading from '@/components/Loading';
|
||||
import Labels from '@/components/form/Labels';
|
||||
import { HIDE_SENSITIVE } from '@/store/prefs';
|
||||
import { CAPI } from '@/config/labels-annotations';
|
||||
import { clear, uniq } from '@/utils/array';
|
||||
import { importCloudCredential } from '@/utils/dynamic-importer';
|
||||
import SelectIconGrid from '@/components/SelectIconGrid';
|
||||
import { DEFAULT_WORKSPACE } from '@/models/provisioning.cattle.io.cluster';
|
||||
import { sortBy } from '@/utils/sort';
|
||||
import { ucFirst } from '@/utils/string';
|
||||
import { set } from '@/utils/object';
|
||||
import { mapFeature, RKE2 as RKE2_FEATURE } from '@/store/features';
|
||||
import { rke1Supports } from '~/store/plugins';
|
||||
|
||||
export default {
|
||||
name: 'CruCloudCredential',
|
||||
|
||||
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.KONTANIER_DRIVER });
|
||||
|
||||
if ( !this.value._name ) {
|
||||
set(this.value, '_name', '');
|
||||
}
|
||||
|
||||
if ( this.value.provider ) {
|
||||
this.selectType(this.value.provider);
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
nodeDrivers: null,
|
||||
kontainerDrivers: null
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
rke2Enabled: mapFeature(RKE2_FEATURE),
|
||||
|
||||
storeOverride() {
|
||||
return 'rancher';
|
||||
},
|
||||
|
||||
driverName() {
|
||||
return this.value?.provider;
|
||||
},
|
||||
|
||||
cloudComponent() {
|
||||
const driver = this.driverName;
|
||||
const haveProviders = this.$store.getters['plugins/credentialDrivers'];
|
||||
|
||||
if ( haveProviders.includes(driver) ) {
|
||||
return importCloudCredential(driver);
|
||||
}
|
||||
|
||||
return importCloudCredential('generic');
|
||||
},
|
||||
|
||||
// array of id, label, description, initials for type selection step
|
||||
secretSubTypes() {
|
||||
const out = [];
|
||||
|
||||
const drivers = [...this.nodeDrivers, ...this.kontainerDrivers]
|
||||
.filter(x => x.spec.active && x.id !== 'rancherkubernetesengine')
|
||||
.map(x => x.spec.displayName || x.id);
|
||||
|
||||
let types = uniq(drivers.map(x => this.$store.getters['plugins/credentialDriverFor'](x)));
|
||||
|
||||
if ( !this.rke2Enabled ) {
|
||||
types = types.filter(x => rke1Supports.includes(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;
|
||||
});
|
||||
|
||||
for ( const id of types ) {
|
||||
let bannerImage, bannerAbbrv;
|
||||
|
||||
try {
|
||||
bannerImage = require(`~/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
|
||||
});
|
||||
}
|
||||
|
||||
return sortBy(out, 'label');
|
||||
},
|
||||
|
||||
hideSensitiveData() {
|
||||
return this.$store.getters['prefs/get'](HIDE_SENSITIVE);
|
||||
},
|
||||
|
||||
doneRoute() {
|
||||
return 'c-cluster-manager-cloudCredential';
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
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.$set(this.value, '_type', type);
|
||||
this.$emit('set-subtype', this.typeDisplay(type, driver));
|
||||
},
|
||||
|
||||
typeDisplay(type, 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);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form>
|
||||
<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"
|
||||
:can-yaml="false"
|
||||
@finish="saveCredential"
|
||||
@select-type="selectType"
|
||||
@error="e=>errors = e"
|
||||
>
|
||||
<NameNsDescription v-model="value" name-key="_name" :mode="mode" :namespaced="false" />
|
||||
|
||||
<component
|
||||
:is="cloudComponent"
|
||||
ref="cloudComponent"
|
||||
:driver-name="driverName"
|
||||
:value="value"
|
||||
:mode="mode"
|
||||
:hide-sensitive-data="hideSensitiveData"
|
||||
/>
|
||||
</CruResource>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<style lang='scss'>
|
||||
</style>
|
||||
|
|
@ -50,7 +50,9 @@ export default {
|
|||
|
||||
methods: {
|
||||
update() {
|
||||
this.$emit('input', { ...this.config });
|
||||
const out = { ...this.config };
|
||||
|
||||
this.$emit('input', out);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -67,30 +69,31 @@ export default {
|
|||
:allow-aws="true"
|
||||
:namespace="namespace"
|
||||
generate-name="etcd-backup-s3-"
|
||||
@input="update"
|
||||
/>
|
||||
|
||||
<div class="row mt-20">
|
||||
<div class="col span-6">
|
||||
<LabeledInput v-model="config.bucket" label="Bucket" />
|
||||
<LabeledInput v-model="config.bucket" label="Bucket" @input="update" />
|
||||
</div>
|
||||
<div class="col span-6">
|
||||
<LabeledInput v-model="config.folder" label="Folder" />
|
||||
<LabeledInput v-model="config.folder" label="Folder" @input="update" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-20">
|
||||
<div class="col span-6">
|
||||
<LabeledInput v-model="config.region" label="Region" />
|
||||
<LabeledInput v-model="config.region" label="Region" @input="update" />
|
||||
</div>
|
||||
<div class="col span-6">
|
||||
<LabeledInput v-model="config.endpoint" label="Endpoint" />
|
||||
<LabeledInput v-model="config.endpoint" label="Endpoint" @input="update" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-20">
|
||||
<Checkbox v-model="config.skipSSLVerify" :mode="mode" label="Accept any certificate (insecure)" />
|
||||
<Checkbox v-model="config.skipSSLVerify" :mode="mode" label="Accept any certificate (insecure)" @input="update" />
|
||||
|
||||
<LabeledInput v-if="!config.skipSSLVerify" v-model="config.endpointCA" type="multiline" label="Endpoint CA Cert" />
|
||||
<LabeledInput v-if="!config.skipSSLVerify" v-model="config.endpointCA" type="multiline" label="Endpoint CA Cert" @input="update" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import Loading from '@/components/Loading';
|
||||
import LabeledSelect from '@/components/form/LabeledSelect';
|
||||
import { SECRET } from '@/config/types';
|
||||
import { NORMAN, SECRET } from '@/config/types';
|
||||
import CreateEditView from '@/mixins/create-edit-view';
|
||||
import CruResource from '@/components/CruResource';
|
||||
import NameNsDescription from '@/components/form/NameNsDescription';
|
||||
|
|
@ -40,11 +40,7 @@ export default {
|
|||
},
|
||||
|
||||
async fetch() {
|
||||
const secretSchema = this.$store.getters['management/schemaFor'](SECRET);
|
||||
|
||||
if (secretSchema?.collectionMethods.find(x => x.toLowerCase() === 'get')) {
|
||||
this.allSecrets = await this.$store.dispatch('management/findAll', { type: SECRET });
|
||||
}
|
||||
this.allCredentials = await this.$store.dispatch('rancher/findAll', { type: NORMAN.CLOUD_CREDENTIAL });
|
||||
|
||||
this.newCredential = await this.$store.dispatch('management/create', {
|
||||
type: SECRET,
|
||||
|
|
@ -56,17 +52,17 @@ export default {
|
|||
data: {},
|
||||
});
|
||||
|
||||
if ( this.filteredSecrets.length === 1 ) {
|
||||
if ( this.filteredCredentials.length === 1 ) {
|
||||
// Auto pick the first credential if there's only one
|
||||
this.credentialId = this.filteredSecrets[0].id;
|
||||
} else if ( !this.filteredSecrets.length ) {
|
||||
this.credentialId = this.filteredCredentials[0].id;
|
||||
} else if ( !this.filteredCredentials.length ) {
|
||||
this.credentialId = _NEW;
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
allSecrets: [],
|
||||
allCredentials: [],
|
||||
nodeComponent: null,
|
||||
credentialId: this.value || _NONE,
|
||||
newCredential: null,
|
||||
|
|
@ -96,19 +92,12 @@ export default {
|
|||
return driver;
|
||||
},
|
||||
|
||||
filteredSecrets() {
|
||||
// @TODO better thing to filter secrets by, limit to matching provider
|
||||
const out = this.allSecrets.filter((obj) => {
|
||||
return obj.metadata.namespace === DEFAULT_WORKSPACE &&
|
||||
obj.metadata.annotations?.[CAPI.CREDENTIAL_DRIVER] === this.driverName;
|
||||
});
|
||||
|
||||
return out;
|
||||
filteredCredentials() {
|
||||
return this.allCredentials.filter(x => x.provider === this.driverName);
|
||||
},
|
||||
|
||||
options() {
|
||||
// @TODO better thing to filter secrets by, limit to matching provider
|
||||
const out = this.filteredSecrets.map((obj) => {
|
||||
const out = this.filteredCredentials.map((obj) => {
|
||||
return {
|
||||
label: obj.nameDisplay,
|
||||
value: obj.id,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import merge from 'lodash/merge';
|
|||
import { mapGetters } from 'vuex';
|
||||
|
||||
import CreateEditView from '@/mixins/create-edit-view';
|
||||
import { CAPI, MANAGEMENT, SECRET } from '@/config/types';
|
||||
import { CAPI, MANAGEMENT, NORMAN } from '@/config/types';
|
||||
import { _CREATE, _EDIT } from '@/config/query-params';
|
||||
import { DEFAULT_WORKSPACE } from '@/models/provisioning.cattle.io.cluster';
|
||||
|
||||
|
|
@ -119,6 +119,14 @@ export default {
|
|||
set(this.value, 'spec', {});
|
||||
}
|
||||
|
||||
if ( !this.value.spec.machineSelectorConfig ) {
|
||||
set(this.value.spec, 'machineSelectorConfig', []);
|
||||
}
|
||||
|
||||
if ( !this.value.spec.machineSelectorConfig.find(x => !x.machineLabelSelector) ) {
|
||||
this.value.spec.machineSelectorConfig.unshift({ config: {} });
|
||||
}
|
||||
|
||||
if ( this.value.spec.cloudCredentialSecretName ) {
|
||||
this.credentialId = `${ this.value.metadata.namespace }/${ this.value.spec.cloudCredentialSecretName }`;
|
||||
}
|
||||
|
|
@ -585,12 +593,20 @@ export default {
|
|||
},
|
||||
|
||||
watch: {
|
||||
s3Backup(neu) {
|
||||
if ( neu ) {
|
||||
set(this.rkeConfig.etcd, 's3', {});
|
||||
} else {
|
||||
set(this.rkeConfig.etcd, 's3', null);
|
||||
}
|
||||
},
|
||||
|
||||
credentialId(val) {
|
||||
if ( val ) {
|
||||
this.credential = this.$store.getters['management/byId'](SECRET, this.credentialId);
|
||||
this.credential = this.$store.getters['rancher/byId'](NORMAN.CLOUD_CREDENTIAL, this.credentialId);
|
||||
|
||||
if ( this.credential ) {
|
||||
this.value.spec.cloudCredentialSecretName = this.credential.metadata.name;
|
||||
this.value.spec.cloudCredentialSecretName = this.credential.id;
|
||||
} else {
|
||||
this.value.spec.cloudCredentialSecretName = null;
|
||||
}
|
||||
|
|
@ -1145,7 +1161,7 @@ export default {
|
|||
/>
|
||||
|
||||
<S3Config
|
||||
v-if="s3Backup"
|
||||
v-if="rkeConfig.etcd.s3"
|
||||
v-model="rkeConfig.etcd.s3"
|
||||
:namespace="value.metadata.namespace"
|
||||
:register-before-hook="registerBeforeHook"
|
||||
|
|
@ -1302,7 +1318,7 @@ export default {
|
|||
<h3>Add additional Kubelet args:</h3>
|
||||
</template>
|
||||
<h3 v-else>
|
||||
For all nodes:
|
||||
For all machines:
|
||||
</h3>
|
||||
|
||||
<ArrayList
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
<script>
|
||||
import { CAPI } from '@/config/labels-annotations';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
|
||||
mode: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
const driver = this.value.metadata.annotations?.[CAPI.CREDENTIAL_DRIVER];
|
||||
|
||||
return { driver };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>Cloud -{{ driver }}-</div>
|
||||
</template>
|
||||
|
|
@ -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 ) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import Loading from '@/components/Loading';
|
||||
import CreateEditView from '@/mixins/create-edit-view';
|
||||
import { SECRET } from '@/config/types';
|
||||
import { NORMAN } from '@/config/types';
|
||||
import { stringify, exceptionToErrorsArray } from '@/utils/error';
|
||||
import Banner from '@/components/Banner';
|
||||
import merge from 'lodash/merge';
|
||||
|
|
@ -114,10 +114,7 @@ export default {
|
|||
this.errors = [];
|
||||
|
||||
try {
|
||||
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 });
|
||||
|
||||
const {
|
||||
clientId,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import Loading from '@/components/Loading';
|
|||
import CreateEditView from '@/mixins/create-edit-view';
|
||||
import LabeledSelect from '@/components/form/LabeledSelect';
|
||||
import Checkbox from '@/components/form/Checkbox';
|
||||
import { SECRET } from '@/config/types';
|
||||
import { NORMAN } from '@/config/types';
|
||||
import { stringify, exceptionToErrorsArray } from '@/utils/error';
|
||||
import Banner from '@/components/Banner';
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ export default {
|
|||
this.errors = [];
|
||||
|
||||
try {
|
||||
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 });
|
||||
this.regionOptions = await this.$store.dispatch('digitalocean/regionOptions', { credentialId: this.credentialId });
|
||||
|
||||
let defaultRegion = 'sfo3';
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ 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 { NORMAN } from '@/config/types';
|
||||
import Questions from '@/components/Questions';
|
||||
import { iffyFields, simplify } from '@/store/plugins';
|
||||
import { isEmpty } from '@/utils/object';
|
||||
|
|
@ -31,7 +31,7 @@ export default {
|
|||
this.errors = [];
|
||||
|
||||
try {
|
||||
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 });
|
||||
this.fields = this.$store.getters['plugins/fieldsForDriver'](this.provider);
|
||||
const name = `rke-machine-config.cattle.io.${ this.provider }config`;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import Banner from '@/components/Banner';
|
|||
import { get } from '@/utils/object';
|
||||
import { mapGetters } from 'vuex';
|
||||
import {
|
||||
SECRET, HCI, NAMESPACE, MANAGEMENT, CONFIG_MAP
|
||||
HCI, NAMESPACE, MANAGEMENT, CONFIG_MAP, NORMAN
|
||||
} from '@/config/types';
|
||||
import { base64Decode, base64Encode } from '@/utils/crypto';
|
||||
import { allHashSettled } from '@/utils/promise';
|
||||
|
|
@ -41,7 +41,7 @@ export default {
|
|||
this.errors = [];
|
||||
|
||||
try {
|
||||
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 });
|
||||
const clusterId = get(this.credential, `metadata.annotations."${ HCI_ANNOTATIONS.CLUSTER_ID }"`);
|
||||
|
||||
const url = `/k8s/clusters/${ clusterId }/v1`;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import Loading from '@/components/Loading';
|
|||
import CreateEditView from '@/mixins/create-edit-view';
|
||||
import LabeledSelect from '@/components/form/LabeledSelect';
|
||||
// import Checkbox from '@/components/form/Checkbox';
|
||||
import { SECRET } from '@/config/types';
|
||||
import { NORMAN } from '@/config/types';
|
||||
import { stringify } from '@/utils/error';
|
||||
import Banner from '@/components/Banner';
|
||||
import UnitInput from '@/components/form/UnitInput';
|
||||
|
|
@ -150,7 +150,7 @@ export default {
|
|||
async fetch() {
|
||||
this.errors = [];
|
||||
|
||||
this.credential = await this.$store.dispatch('management/find', { type: SECRET, id: this.cloudCredentialId });
|
||||
this.credential = await this.$store.dispatch('rancher/find', { type: NORMAN.CLOUD_CREDENTIAL, id: this.cloudCredentialId });
|
||||
},
|
||||
|
||||
data() {
|
||||
|
|
@ -262,7 +262,7 @@ export default {
|
|||
},
|
||||
|
||||
cloudCredentialId() {
|
||||
return this.credentialId.split('/')[1];
|
||||
return this.credentialId.split(/[:/]/)[1];
|
||||
},
|
||||
|
||||
host: {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export default {
|
|||
},
|
||||
|
||||
schema() {
|
||||
const inStore = this.$store.getters['currentStore'](this.value.type);
|
||||
const inStore = this.storeOverride || this.$store.getters['currentStore'](this.value.type);
|
||||
|
||||
return this.$store.getters[`${ inStore }/schemaFor`](this.value.type);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,170 @@
|
|||
import { CAPI } from '@/config/labels-annotations';
|
||||
import { fullFields, prefixFields, simplify, suffixFields } from '@/store/plugins';
|
||||
import { isEmpty, set } from '~/utils/object';
|
||||
import { SECRET } from '~/config/types';
|
||||
import { escapeHtml } from '~/utils/string';
|
||||
|
||||
export default {
|
||||
hasSensitiveData: () => true,
|
||||
canCustomEdit: () => true,
|
||||
|
||||
_detailLocation() {
|
||||
return {
|
||||
name: `c-cluster-manager-cloudCredential-id`,
|
||||
params: {
|
||||
product: this.$rootGetters['productId'],
|
||||
cluster: this.$rootGetters['clusterId'],
|
||||
id: this.id,
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
parentLocationOverride() {
|
||||
return {
|
||||
name: `c-cluster-manager-cloudCredential`,
|
||||
params: { cluster: this.$rootGetters['clusterId'] }
|
||||
};
|
||||
},
|
||||
|
||||
secret() {
|
||||
return this.$rootGetters['management/byId'](SECRET, this.id.replace(':', '/'));
|
||||
},
|
||||
|
||||
configKey() {
|
||||
return Object.keys(this).find( k => k.endsWith('credentialConfig'));
|
||||
},
|
||||
|
||||
provider() {
|
||||
const annotation = this.annotations?.[CAPI.CREDENTIAL_DRIVER];
|
||||
|
||||
if ( annotation ) {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
const configKey = this.configKey;
|
||||
|
||||
// Call [amazoneks,amazonec2] -> aws
|
||||
if ( configKey ) {
|
||||
const out = this.$rootGetters['plugins/credentialDriverFor'](configKey.replace(/credentialConfig$/, ''));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
setProvider() {
|
||||
return (neu) => {
|
||||
this.setAnnotation(CAPI.CREDENTIAL_DRIVER, neu);
|
||||
|
||||
Object.keys(this).forEach((k) => {
|
||||
k = k.toLowerCase();
|
||||
|
||||
if ( k.endsWith('config') && k !== `${ neu }config` ) {
|
||||
set(this, k, null);
|
||||
}
|
||||
});
|
||||
|
||||
if ( !this[`${ neu }credentialConfig`] ) {
|
||||
set(this, `${ neu }credentialConfig`, {});
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
decodedData() {
|
||||
const k = this.configKey;
|
||||
|
||||
if ( k ) {
|
||||
return this[k];
|
||||
}
|
||||
|
||||
return {};
|
||||
},
|
||||
|
||||
setData() {
|
||||
return (key, value) => { // or (mapOfNewData)
|
||||
const isMap = key && typeof key === 'object';
|
||||
|
||||
if ( !this[this.configKey] || isMap ) {
|
||||
set(this, this.configKey, {});
|
||||
}
|
||||
|
||||
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
|
||||
set(this, `"${ this.configKey }"."${ k }"`, neu[k]);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
providerDisplay() {
|
||||
const provider = (this.provider || '').toLowerCase();
|
||||
|
||||
return this.$rootGetters['i18n/withFallback'](`cluster.provider."${ provider }"`, null, provider);
|
||||
},
|
||||
|
||||
publicData() {
|
||||
let { publicKey, publicMode } = this.$rootGetters['plugins/credentialOptions'](this.provider);
|
||||
|
||||
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 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let val = this.decodedData[publicKey];
|
||||
|
||||
if ( !val ) {
|
||||
val = this.secret?.decodedData?.[`${ this.provider }credentialConfig-${ publicKey }`];
|
||||
}
|
||||
|
||||
if ( !val ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const maxLength = Math.min(8, Math.floor(val.length / 2));
|
||||
|
||||
if ( publicMode === 'prefix' ) {
|
||||
return `${ escapeHtml(val.substr(0, maxLength)) }…`;
|
||||
} else if ( publicMode === 'suffix' ) {
|
||||
return `…${ escapeHtml(val.substr(-1 * maxLength)) }`;
|
||||
} else {
|
||||
return escapeHtml(val);
|
||||
}
|
||||
},
|
||||
|
||||
doneRoute() {
|
||||
return 'c-cluster-manager-secret';
|
||||
},
|
||||
};
|
||||
|
|
@ -418,7 +418,8 @@ export default {
|
|||
method: 'post',
|
||||
}, { root: true });
|
||||
} else {
|
||||
const args = {};
|
||||
const now = this.spec?.rkeConfig?.etcdSnapshotCreate.generation || 1;
|
||||
const args = { generation: now + 1 };
|
||||
|
||||
if ( this.spec?.rkeConfig?.etcd?.s3 ) {
|
||||
args.s3 = this.spec.rkeConfig.etcd.s3;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import r from 'jsrsasign';
|
||||
import { CAPI, CERTMANAGER, KUBERNETES } from '@/config/labels-annotations';
|
||||
import { CERTMANAGER, KUBERNETES } from '@/config/labels-annotations';
|
||||
import { base64Decode, base64Encode } from '@/utils/crypto';
|
||||
import { removeObjects } from '@/utils/array';
|
||||
import { SERVICE_ACCOUNT } from '@/config/types';
|
||||
import { isEmpty, set } from '@/utils/object';
|
||||
import { escapeHtml } from '@/utils/string';
|
||||
import { fullFields, prefixFields, simplify, suffixFields } from '@/store/plugins';
|
||||
import { set } from '@/utils/object';
|
||||
import { NAME as MANAGER } from '@/config/product/manager';
|
||||
|
||||
export const TYPES = {
|
||||
|
|
@ -35,7 +33,7 @@ export default {
|
|||
},
|
||||
|
||||
isCloudCredential() {
|
||||
return this._type === TYPES.CLOUD_CREDENTIAL;
|
||||
return this._type === TYPES.CLOUD_CREDENTIAL || (this.metadata.namespace === 'cattle-global-data' && this.metadata.generateName === 'cc-');
|
||||
},
|
||||
|
||||
dockerJSON() {
|
||||
|
|
@ -336,6 +334,7 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
/*
|
||||
cloudCredentialProvider() {
|
||||
return this.metadata?.annotations?.[CAPI.CREDENTIAL_DRIVER];
|
||||
},
|
||||
|
|
@ -391,6 +390,7 @@ export default {
|
|||
return escapeHtml(val);
|
||||
}
|
||||
},
|
||||
*/
|
||||
|
||||
doneRoute() {
|
||||
if ( this.$rootGetters['currentProduct'].name === MANAGER ) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<script>
|
||||
import ResourceDetail from '@/components/ResourceDetail';
|
||||
|
||||
export default {
|
||||
name: 'CloudCredentialEdit',
|
||||
components: { ResourceDetail },
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ResourceDetail
|
||||
store-override="rancher"
|
||||
resource-override="cloudcredential"
|
||||
parent-route-override="c-cluster-manager-cloudCredential"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<script>
|
||||
import ResourceDetail from '@/components/ResourceDetail';
|
||||
|
||||
export default {
|
||||
name: 'CloudCredentialCreate',
|
||||
components: { ResourceDetail },
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ResourceDetail
|
||||
store-override="rancher"
|
||||
resource-override="cloudcredential"
|
||||
parent-route-override="c-cluster-manager-cloudCredential"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -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"
|
||||
/>
|
||||
|
||||
<ResourceTable :schema="schema" :rows="rows" :headers="headers" :namespaced="false">
|
||||
<ResourceTable :schema="schema" :rows="rows" :headers="headers" :namespaced="false" group-by="providerDisplay">
|
||||
<template #cell:apikey="{row}">
|
||||
<span v-html="row.cloudCredentialPublicData" />
|
||||
<span v-if="row.publicData" v-html="row.publicData" />
|
||||
<span v-else class="text-muted">—</span>
|
||||
</template>
|
||||
</ResourceTable>
|
||||
</div>
|
||||
|
|
@ -209,8 +209,8 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row mb-20">
|
||||
<div class="col span-12">
|
||||
<h4 v-t="'prefs.helm.label'" />
|
||||
<ButtonGroup v-model="showPreRelease" :options="helmOptions" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 || {});
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue