From ef5afd4007e53d7c3ef61807d3f4d4b25fe10418 Mon Sep 17 00:00:00 2001 From: Cody Jackson Date: Mon, 22 Feb 2021 09:06:57 -0700 Subject: [PATCH] Adding the agent env vars to rke drivers. rancher/dashboard#31023 --- app/authenticated/cluster/edit/route.js | 25 ++ .../cluster-driver/driver-rke/component.js | 10 + .../cluster-driver/driver-rke/template.hbs | 15 ++ .../form-agent-env-var/component.js | 254 ++++++++++++++++++ .../form-agent-env-var/template.hbs | 212 +++++++++++++++ .../addon/components/new-select/component.js | 8 + .../addon/components/new-select/template.hbs | 2 +- .../form-agent-env-var/component.js | 1 + translations/en-us.yaml | 28 ++ 9 files changed, 554 insertions(+), 1 deletion(-) create mode 100644 lib/shared/addon/components/form-agent-env-var/component.js create mode 100644 lib/shared/addon/components/form-agent-env-var/template.hbs create mode 100644 lib/shared/app/components/form-agent-env-var/component.js diff --git a/app/authenticated/cluster/edit/route.js b/app/authenticated/cluster/edit/route.js index 144875391..27291d618 100644 --- a/app/authenticated/cluster/edit/route.js +++ b/app/authenticated/cluster/edit/route.js @@ -9,6 +9,7 @@ import { scheduleOnce } from '@ember/runloop'; export default Route.extend({ access: service(), globalStore: service(), + clusterStore: service(), releaseVersions: service(), clusterTemplateService: service('clusterTemplates'), roleTemplateService: service('roleTemplate'), @@ -21,11 +22,13 @@ export default Route.extend({ originalCluster: cluster, cluster: cluster.clone(), cloudCredentials: globalStore.findAll('cloudcredential'), + configMaps: this.configMaps(), kontainerDrivers: globalStore.findAll('kontainerDriver'), nodeTemplates: globalStore.findAll('nodeTemplate'), nodeDrivers: globalStore.findAll('nodeDriver'), psps: globalStore.findAll('podSecurityPolicyTemplate'), roleTemplates: get(this, 'roleTemplateService').get('allFilteredRoleTemplates'), + secrets: this.secrets(), users: globalStore.findAll('user'), clusterRoleTemplateBinding: globalStore.findAll('clusterRoleTemplateBinding'), me: get(this, 'access.principal'), @@ -157,6 +160,28 @@ export default Route.extend({ } }, + async configMaps() { + const projects = await this.projects(); + const configMapPromises = projects.map((project) => project.hasLink('configMaps') ? project.followLink('configMaps') : Promise.resolve([])); + const nestedConfigMaps = await Promise.all(configMapPromises) || []; + + return nestedConfigMaps.flatMap((maps) => maps.content); + }, + + async secrets() { + const projects = await this.projects(); + const secretPromises = projects.map((project) => project.hasLink('secrets') ? project.followLink('secrets') : Promise.resolve([])); + const nestedSecrets = await Promise.all(secretPromises) || []; + + return nestedSecrets.flatMap((maps) => maps.content); + }, + + async projects() { + const cluster = this.modelFor('authenticated.cluster'); + + return (await cluster.followLink('projects')).content; + }, + queryParams: { provider: { refreshModel: true }, clusterTemplateRevision: { refreshModel: true } diff --git a/lib/shared/addon/components/cluster-driver/driver-rke/component.js b/lib/shared/addon/components/cluster-driver/driver-rke/component.js index 0f844bcec..e19ce8ac1 100644 --- a/lib/shared/addon/components/cluster-driver/driver-rke/component.js +++ b/lib/shared/addon/components/cluster-driver/driver-rke/component.js @@ -142,6 +142,8 @@ export default InputTextFile.extend(ManageLabels, ClusterDriver, { scheduledClusterScan: { enabled: false }, }) + this.initAgentEnvVars(); + this.initNodeCounts(); if (!this.useClusterTemplate && this.model.cluster.clusterTemplateRevisionId) { @@ -877,6 +879,10 @@ export default InputTextFile.extend(ManageLabels, ClusterDriver, { return options; }), + showCustomConfigMapsAndSecrets: computed('model.provider', function() { + return get(this, 'model.provider') === 'custom'; + }), + initScheduledClusterScan() { // We need to wait for benchmarks to be available before we can actually create the profiles if (this.cisHelpers.cisScanBenchmarks.length === 0) { @@ -1430,6 +1436,10 @@ export default InputTextFile.extend(ManageLabels, ClusterDriver, { }) }, + initAgentEnvVars() { + setProperties(this.cluster, { agentEnvVars: get(this, 'cluster.agentEnvVars') || [] }); + }, + initPrivateRegistries() { const config = get(this, 'config'); diff --git a/lib/shared/addon/components/cluster-driver/driver-rke/template.hbs b/lib/shared/addon/components/cluster-driver/driver-rke/template.hbs index 6b023f3b7..993854233 100644 --- a/lib/shared/addon/components/cluster-driver/driver-rke/template.hbs +++ b/lib/shared/addon/components/cluster-driver/driver-rke/template.hbs @@ -1253,6 +1253,21 @@ {{drain-node selection=upgradeStrategy.nodeDrainInput clusterTemplateCreate=clusterTemplateCreate editable=notView applyClusterTemplate=applyClusterTemplate clusterTemplateRevision=model.clusterTemplateRevision.clusterConfig questions=model.clusterTemplateRevision.questions addOverride=(action "addOverride")}} {{/if}} + {{#if (not clusterTemplateCreate)}} +
+ + +
+ {{/if}} {{/accordion-list-item}} {{#accordion-list-item diff --git a/lib/shared/addon/components/form-agent-env-var/component.js b/lib/shared/addon/components/form-agent-env-var/component.js new file mode 100644 index 000000000..60725e143 --- /dev/null +++ b/lib/shared/addon/components/form-agent-env-var/component.js @@ -0,0 +1,254 @@ +import Component from '@ember/component'; +import { inject as service } from '@ember/service'; +import layout from './template'; +import { computed, get, set } from '@ember/object'; + +export default Component.extend({ + globalStore: service(), + intl: service(), + layout, + + model: null, + editable: true, + + statusClass: null, + status: null, + editing: true, + showCustomConfigMaps: false, + showCustomSecrets: false, + typeChoices: [], + configMaps: [], + secrets: [], + value: [], + resourceKeyChoices: [], + init() { + this._super(...arguments); + + const configMapsEnabled = this.showCustomConfigMaps || this.configMaps?.length > 0; + const secretsEnabled = this.showCustomSecrets || this.secrets?.length > 0; + + const typeChoices = [ + { + label: this.intl.t('formAgentEnvVar.typeChoices.keyValue'), + value: 'keyValue' + }, + { + label: this.intl.t('formAgentEnvVar.typeChoices.resource'), + value: 'resource' + }, + { + label: configMapsEnabled ? this.intl.t('formAgentEnvVar.typeChoices.configMap') : this.intl.t('formAgentEnvVar.typeChoices.configMapNone'), + disabled: !configMapsEnabled, + value: 'configMap' + }, + { + label: secretsEnabled ? this.intl.t('formAgentEnvVar.typeChoices.secret') : this.intl.t('formAgentEnvVar.typeChoices.secretNone'), + disabled: !secretsEnabled, + value: 'secret' + }, + { + label: this.intl.t('formAgentEnvVar.typeChoices.podField'), + value: 'podfield' + } + ]; + + this.set('typeChoices', typeChoices); + + this.set('resourcekeyChoices', [ + { + label: 'limits.cpu', + value: 'limits.cpu' + }, + { + label: 'limits.ephemeral-storage', + value: 'limits.ephemeral-storage' + }, + { + label: 'limits.memory', + value: 'limits.memory' + }, + { + label: 'requests.cpu', + value: 'requests.cpu' + }, + { + label: 'requests.ephemeral-storage', + value: 'requests.ephemeral-storage' + }, + { + label: 'requests.memory', + value: 'requests.memory' + }, + ]); + + this.set('value', this.value || []); + + + this.value.forEach((envVar) => this.inferTypeAndSetHelpers(envVar)); + }, + + actions: { + add() { + this.value.pushObject(this.createEnvVar('keyValue')); + }, + updateType(index, event) { + const type = event.target.value; + + this.value.replace(index, 1, [this.createEnvVar(type)]); + }, + updateSecret(index, event) { + const secretName = event.target.value; + + set(this.value[index], 'secretKeyChoices', this.keyChoices('secrets', secretName)); + set(this.value[index], 'valueFrom.secretKeyRef.name', secretName); + set(this.value[index], 'valueFrom.secretKeyRef.key', this.value[index].secretKeyChoices[0].value); + }, + updateConfigMap(index, event) { + const configMapName = event.target.value; + + set(this.value[index], 'configMapKeyChoices', this.keyChoices('configMaps', configMapName)); + set(this.value[index], 'valueFrom.configMapKeyRef.name', configMapName); + set(this.value[index], 'valueFrom.configMapKeyRef.key', this.value[index].configMapKeyChoices[0].value); + }, + remove(index) { + this.value.removeAt(index, 1); + }, + }, + + configMapChoices: computed('configMaps', function() { + const configMaps = get(this, 'configMaps') || []; + const choices = configMaps.map((configMap) => ({ + label: configMap.displayName, + value: configMap.name + })); + + return choices + .uniqBy('label') + .sortBy('label'); + }), + + secretChoices: computed('secrets', function() { + const secrets = get(this, 'secrets') || []; + const choices = secrets.map((secret) => ({ + label: secret.displayName, + value: secret.name + })); + + return choices + .uniqBy('label') + .sortBy('label'); + }), + + inferTypeAndSetHelpers(envVar) { + if (this.isValueFromSet(envVar, 'resourceFieldRef', ['resource', 'containerName', 'divisor'])) { + return this.setEnvVarHelpers(envVar, 'resource'); + } + + if (this.isValueFromSet(envVar, 'configMapKeyRef', ['name', 'key'])) { + return this.setEnvVarHelpers(envVar, 'configMap'); + } + + if (this.isValueFromSet(envVar, 'secretKeyRef', ['name', 'key'])) { + return this.setEnvVarHelpers(envVar, 'secret'); + } + + if (this.isValueFromSet(envVar, 'fieldRef', ['fieldPath'])) { + return this.setEnvVarHelpers(envVar, 'podfield'); + } + + return this.setEnvVarHelpers(envVar, 'keyValue'); + }, + + isValueFromSet(envVar, refKey, fields) { + return fields.some((field) => get(envVar, `valueFrom.${ refKey }.${ field }`)); + }, + + keyChoices(resource, selectedName) { + const resources = get(this, resource) || []; + const data = resources.find((r) => r.name === selectedName)?.data || {}; + + return Object.keys(data).map((key) => ({ + label: key, + value: key + })) + }, + + showContainer(type) { + return ['resource'].includes(type); + }, + + showKey(type) { + return ['resource', 'configMap', 'secret', 'podfield'].includes(type); + }, + + showValue(type) { + return ['keyValue'].includes(type); + }, + + showConfigMap(type) { + return ['configMap'].includes(type); + }, + + showSecret(type) { + return ['secret'].includes(type); + }, + + showPodField(type) { + return ['podfield'].includes(type); + }, + createEnvVar(type) { + const envVar = {}; + + this.setEnvVarHelpers(envVar, type); + this.setDefaults(envVar, type); + + return envVar; + }, + + setEnvVarHelpers(envVar, type) { + Object.assign(envVar, { + type, + showContainer: this.showContainer(type), + showKey: this.showKey(type), + showValue: this.showValue(type), + showConfigMap: this.showConfigMap(type), + showSecret: this.showSecret(type), + showPodField: this.showPodField(type), + secretKeyChoices: envVar.valueFrom?.secretKeyRef ? this.keyChoices('secrets', envVar.valueFrom.secretKeyRef.name) : null, + configMapKeyChoices: envVar.valueFrom?.configMapKeyRef ? this.keyChoices('configMaps', envVar.valueFrom.configMapKeyRef.name) : null, + }); + }, + + setDefaults(envVar, type) { + if (type !== 'keyValue') { + set(envVar, 'valueFrom', envVar.valueFrom || {}); + } + + if (type === 'configMap') { + set(envVar, 'valueFrom.configMapKeyRef', envVar.valueFrom.configMapKeyRef || {}); + const configMapName = this.configMapChoices[0]?.value; + + set(envVar, 'configMapKeyChoices', this.keyChoices('configMaps', configMapName)); + set(envVar, 'valueFrom.configMapKeyRef', envVar.valueFrom.configMapKeyRef || { + name: configMapName, + key: envVar.configMapKeyChoices[0]?.value + }); + } + if (type === 'resource') { + set(envVar, 'valueFrom.resourceFieldRef', envVar.valueFrom.resourceFieldRef || {}); + } + if (type === 'podfield') { + set(envVar, 'valueFrom.fieldRef', envVar.valueFrom.fieldRef || {}); + } + + if (type === 'secret') { + const secretName = this.secretChoices[0]?.value; + + set(envVar, 'secretKeyChoices', this.keyChoices('secrets', secretName)); + set(envVar, 'valueFrom.secretKeyRef', envVar.valueFrom.secretKeyRef || { + name: secretName, + key: envVar.secretKeyChoices[0]?.value + }); + } + } +}); diff --git a/lib/shared/addon/components/form-agent-env-var/template.hbs b/lib/shared/addon/components/form-agent-env-var/template.hbs new file mode 100644 index 000000000..1e309bea0 --- /dev/null +++ b/lib/shared/addon/components/form-agent-env-var/template.hbs @@ -0,0 +1,212 @@ +
+ + + {{#each value as |envVar index|}} + + + + {{#if envVar.showValue}} + + {{/if}} + {{#if envVar.showContainer}} + + + {{/if}} + {{#if envVar.showConfigMap}} + + + {{/if}} + {{#if envVar.showSecret}} + + + {{/if}} + {{#if envVar.showPodField}} + + {{/if}} + + + {{/each}} + +
+ + {{#input-or-display + editable=editable + value=envVar.type + }} + {{new-select + class="form-control" + content=typeChoices + value=envVar.type + onChange=(action 'updateType' index) + }} + {{/input-or-display}} + + + {{#input-or-display + editable=editable + value=envVar.name + }} + {{input + class="form-control" + type="text" + value=envVar.name + placeholder=(t 'formAgentEnvVar.placeholders.foo') + }} + {{/input-or-display}} + + + {{#input-or-display + editable=editable + value=envVar.value + }} + {{input + class="form-control" + type="text" + value=envVar.value + placeholder=(t 'formAgentEnvVar.placeholders.bar') + }} + {{/input-or-display}} + + + {{#input-or-display + editable=editable + value=envVar.valueFrom.resourceFieldRef.containerName + }} + {{input + class="form-control" + type="text" + value=envVar.valueFrom.resourceFieldRef.containerName + placeholder=(t 'formAgentEnvVar.placeholders.container') + }} + {{/input-or-display}} + + + {{#input-or-display + editable=editable + value=envVar.valueFrom.resourceFieldRef.resource + }} + {{new-select + class="form-control" + content=resourcekeyChoices + value=envVar.valueFrom.resourceFieldRef.resource + }} + {{/input-or-display}} + + + {{#input-or-display + editable=editable + value=envVar.valueFrom.configMapKeyRef.name + }} + {{#if showCustomConfigMaps}} + {{input + class="form-control" + type="text" + value=envVar.valueFrom.configMapKeyRef.name + }} + {{else}} + {{new-select + class="form-control" + content=configMapChoices + value=envVar.valueFrom.configMapKeyRef.name + onChange=(action 'updateConfigMap' index) + }} + {{/if}} + {{/input-or-display}} + + + {{#input-or-display + editable=editable + value=envVar.valueFrom.configMapKeyRef.key + }} + {{#if showCustomConfigMaps}} + {{input + class="form-control" + type="text" + value=envVar.valueFrom.configMapKeyRef.key + }} + {{else}} + {{new-select + class="form-control" + content=envVar.configMapKeyChoices + value=envVar.valueFrom.configMapKeyRef.key + disabled=(not envVar.configMapKeyChoices.length) + }} + {{/if}} + {{/input-or-display}} + + + {{#input-or-display + editable=editable + value=envVar.valueFrom.secretKeyRef.name + }} + {{#if showCustomSecrets}} + {{input + class="form-control" + type="text" + value=envVar.valueFrom.secretKeyRef.name + }} + {{else}} + {{new-select + class="form-control" + content=secretChoices + value=envVar.valueFrom.secretKeyRef.name + onChange=(action 'updateSecret' index) + }} + {{/if}} + {{/input-or-display}} + + + {{#input-or-display + editable=editable + value=envVar.valueFrom.secretKeyRef.key + }} + {{#if showCustomSecrets}} + {{input + class="form-control" + type="text" + value=envVar.valueFrom.secretKeyRef.key + }} + {{else}} + {{new-select + class="form-control" + content=envVar.secretKeyChoices + value=envVar.valueFrom.secretKeyRef.key + disabled=(not envVar.secretKeyChoices.length) + }} + {{/if}} + {{/input-or-display}} + + + {{#input-or-display + editable=editable + value=envVar.valueFrom.fieldRef.fieldPath + }} + {{input + class="form-control" + type="text" + value=envVar.valueFrom.fieldRef.fieldPath + placeholder=(t 'formAgentEnvVar.placeholders.key') + }} + {{/input-or-display}} + + + {{#if editable}} +
+ +
+ {{/if}} +
+ {{#if editable}} + + {{/if}} +
\ No newline at end of file diff --git a/lib/shared/addon/components/new-select/component.js b/lib/shared/addon/components/new-select/component.js index 56b0bc16a..abbf12362 100644 --- a/lib/shared/addon/components/new-select/component.js +++ b/lib/shared/addon/components/new-select/component.js @@ -14,6 +14,7 @@ export default Component.extend({ optionLabelPath: 'label', optionGroupPath: 'group', optionDisabledPath: 'disabled', + onChange: null, value: null, useContentForDefaultValue: false, @@ -49,6 +50,13 @@ export default Component.extend({ this.off('change', this, this._change); }, + actions: { + onChange() { + if (this.onChange) { + this.onChange(...arguments); + } + } + }, setDefaultValueObserver: observer('asyncContent.value', function() { const content = get(this, 'asyncContent.value'); diff --git a/lib/shared/addon/components/new-select/template.hbs b/lib/shared/addon/components/new-select/template.hbs index 637585e0e..f361ebb29 100644 --- a/lib/shared/addon/components/new-select/template.hbs +++ b/lib/shared/addon/components/new-select/template.hbs @@ -1,4 +1,4 @@ - {{#if prompt}}