Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EKS: edit imported clusters, and clusters with exclusively self-managed nodes #11294

Merged
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
19 changes: 16 additions & 3 deletions pkg/eks/components/CruEKS.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { defineComponent } from 'vue';
import { removeObject } from '@shell/utils/array';
import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
import { NORMAN } from '@shell/config/types';
import { diffUpstreamSpec } from '@shell/utils/kontainer';
import { diffUpstreamSpec, syncUpstreamConfig } from '@shell/utils/kontainer';
import CreateEditView from '@shell/mixins/create-edit-view';
import FormValidation from '@shell/mixins/form-validation';

Expand Down Expand Up @@ -119,6 +119,10 @@ export default defineComponent({
const liveNormanCluster = await this.value.findNormanCluster();

this.normanCluster = await store.dispatch(`rancher/clone`, { resource: liveNormanCluster });
// ensure any fields editable through this UI that have been altered in aws are shown here - see syncUpstreamConfig jsdoc for details
if (!this.isNewOrUnprovisioned) {
syncUpstreamConfig('eks', this.normanCluster);
}
// track original version on edit to ensure we don't offer k8s downgrades
this.originalVersion = this.normanCluster?.eksConfig?.kubernetesVersion || '';
} else {
Expand All @@ -136,7 +140,7 @@ export default defineComponent({
}
this.config = this.normanCluster.eksConfig as EKSConfig;

if (!this.config.nodeGroups || !this.config.nodeGroups.length) {
if ((!this.config.nodeGroups || !this.config.nodeGroups.length) && this.mode === _CREATE) {
this.$set(this.config, 'nodeGroups', [{ ...DEFAULT_NODE_GROUP_CONFIG, nodegroupName: 'group1' }]);
}
if (this.config.nodeGroups) {
Expand Down Expand Up @@ -303,6 +307,11 @@ export default defineComponent({
return this.value?.id || null;
},

// used to display VPC/subnet information in the networking tab for imported clusters and clusters with the 'create a vpc and subnets automatically' option selected
statusSubnets(): string[] {
return this.normanCluster?.eksStatus?.subnets || [];
},

canManageMembers(): boolean {
return canViewClusterMembershipEditor(this.$store);
},
Expand Down Expand Up @@ -378,7 +387,7 @@ export default defineComponent({
});

return out;
}
},
},

methods: {
Expand Down Expand Up @@ -409,6 +418,9 @@ export default defineComponent({
},

async actuallySave(): Promise<void> {
if (!this.isNewOrUnprovisioned && !this.nodeGroups.length && !!this.normanCluster?.eksConfig?.nodeGroups) {
this.$set(this.normanCluster.eksConfig, 'nodeGroups', null);
}
await this.normanCluster.save();

return await this.normanCluster.waitForCondition('InitialRolesPopulated');
Expand Down Expand Up @@ -658,6 +670,7 @@ export default defineComponent({
:mode="mode"
:region="config.region"
:amazon-credential-secret="config.amazonCredentialSecret"
:status-subnets="statusSubnets"
:rules="{subnets:fvGetAndReportPathRules('subnets')}"
/>
</Accordion>
Expand Down
6 changes: 3 additions & 3 deletions pkg/eks/components/Logging.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ export default defineComponent({

methods: {
typeEnabled(type: string) {
return this.loggingTypes.includes(type);
return (this.loggingTypes || []).includes(type);
},

toggleType(type:string) {
const out = [...this.loggingTypes];
const out = [...(this.loggingTypes || [])];

if (this.loggingTypes.includes(type)) {
if (out.includes(type)) {
removeObject(out, type);
} else {
out.push(type);
Expand Down
32 changes: 25 additions & 7 deletions pkg/eks/components/Networking.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
import { defineComponent } from 'vue';
import { PropType, defineComponent } from 'vue';
import { Store, mapGetters } from 'vuex';
import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
Expand All @@ -27,31 +27,42 @@ export default defineComponent({
type: String,
default: _EDIT
},

region: {
type: String,
default: ''
},

amazonCredentialSecret: {
type: String,
default: ''
},

subnets: {
type: Array,
type: Array as PropType<string[]>,
default: () => []
},

publicAccess: {
type: Boolean,
default: false
},

privateAccess: {
type: Boolean,
default: false
},

publicAccessSources: {
type: Array,
default: () => []
},

statusSubnets: {
type: Array as PropType<string[]>,
default: () => []
},

rules: {
type: Object,
default: () => {}
Expand Down Expand Up @@ -89,7 +100,7 @@ export default defineComponent({
loadingVpcs: false,
vpcInfo: {} as {Vpcs: AWS.VPC[]},
subnetInfo: {} as {Subnets: AWS.Subnet[]},
chooseSubnet: this.subnets && !!this.subnets.length
chooseSubnet: !!this.subnets && !!this.subnets.length
};
},

Expand Down Expand Up @@ -143,8 +154,11 @@ export default defineComponent({
},

displaySubnets: {
get(): {key:string, label:string, _isSubnet?:boolean, kind?:string}[] {
return this.vpcOptions.filter((option) => this.subnets.includes(option.key));
get(): {key:string, label:string, _isSubnet?:boolean, kind?:string}[] | string[] {
const subnets: string[] = this.chooseSubnet ? this.subnets : this.statusSubnets;

// vpcOptions will be empty in 'view config' mode, where aws API requests are not made
return this.vpcOptions.length ? this.vpcOptions.filter((option) => subnets.includes(option.key)) : subnets;
},
set(neu: {key:string, label:string, _isSubnet?:boolean, kind?:string}[]) {
this.$emit('update:subnets', neu.map((s) => s.key));
Expand Down Expand Up @@ -223,7 +237,10 @@ export default defineComponent({
</div>
</div>
<div class="row mb-10">
<div class="col span-6">
<div
v-if="isNew"
class="col span-6"
>
<RadioGroup
v-model="chooseSubnet"
name="subnet-mode"
Expand All @@ -234,11 +251,12 @@ export default defineComponent({
/>
</div>
<div
v-if="chooseSubnet"
v-if="chooseSubnet || !isNew"
class="col span-6"
>
<LabeledSelect
v-model="displaySubnets"
:disabled="!isNew"
:mode="mode"
label-key="eks.vpcSubnet.label"
:options="vpcOptions"
Expand Down
22 changes: 21 additions & 1 deletion pkg/eks/components/__tests__/Networking.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

/* eslint-disable jest/no-mocks-import */
import { _CREATE, _EDIT } from '@shell/config/query-params';
import { shallowMount } from '@vue/test-utils';
import Networking from '../Networking.vue';

Expand Down Expand Up @@ -56,7 +57,7 @@ describe('eKS Networking', () => {
const setup = requiredSetup();

const wrapper = shallowMount(Networking, {
propsData: { },
propsData: { mode: _CREATE },
...setup
});

Expand All @@ -70,4 +71,23 @@ describe('eKS Networking', () => {
await wrapper.vm.$nextTick();
expect(subnetDropdown.exists()).toBe(false);
});

it('should show a list of subnets in use if a cluster has already provisioned and the \'create automatically\' vpc option was selected', async() => {
const setup = requiredSetup();

const wrapper = shallowMount(Networking, {
propsData: {
mode: _EDIT, subnets: [], statusSubnets: ['bc', 'def']
},
...setup
});

await wrapper.vm.$nextTick();

const subnetDropdown = wrapper.find('[data-testid="eks-subnets-dropdown"]');

expect(subnetDropdown.exists()).toBe(true);

expect(subnetDropdown.props().value).toStrictEqual(['bc', 'def']);
});
});
6 changes: 5 additions & 1 deletion pkg/gke/components/CruGKE.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import debounce from 'lodash/debounce';
import {
clusterNameChars, clusterNameStartEnd, requiredInCluster, ipv4WithCidr, ipv4oripv6WithCidr
} from '../util/validators';
import { diffUpstreamSpec } from '@shell/utils/kontainer';
import { diffUpstreamSpec, syncUpstreamConfig } from '@shell/utils/kontainer';

const defaultMachineType = 'n1-standard-2';

Expand Down Expand Up @@ -175,6 +175,10 @@ export default defineComponent({
} else {
this.normanCluster = await store.dispatch('rancher/create', { type: NORMAN.CLUSTER, ...defaultCluster }, { root: true });
}
// ensure any fields editable through this UI that have been altered in aws are shown here - see syncUpstreamConfig jsdoc for details
if (!this.isNewOrUnprovisioned) {
syncUpstreamConfig('gke', this.normanCluster);
}
if (!this.normanCluster.gkeConfig) {
this.$set(this.normanCluster, 'gkeConfig', { ...defaultGkeConfig });
}
Expand Down
90 changes: 89 additions & 1 deletion shell/utils/__tests__/kontainer.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { diffUpstreamSpec } from '@shell/utils/kontainer';
import { diffUpstreamSpec, syncUpstreamConfig } from '@shell/utils/kontainer';

describe('fx: diffUpstreamSpec', () => {
it.each([
Expand Down Expand Up @@ -90,3 +90,91 @@ describe('fx: diffUpstreamSpec', () => {
expect(diffUpstreamSpec(upstream, local)).toStrictEqual(diff);
});
});

describe('fx: syncUpstreamSpec', () => {
it('should set any fields defined in upstream spec and not local spec', () => {
const upstream = {
string: 'def',
'other-string': '123',
alreadySet: 'abc',
alreadySetArray: [2, 3, 4],
alreadySetBooleanFalse: false,
alreadySetBooleanTrue: true
};
const local = {
alreadySet: 'def',
alreadySetArray: [1, 2, 3],
alreadySetBooleanFalse: false,
alreadySetBooleanTrue: true
};

const expected = {
string: 'def',
'other-string': '123',
alreadySet: 'def',
alreadySetArray: [1, 2, 3],
alreadySetBooleanFalse: false,
alreadySetBooleanTrue: true
};

const testCluster = { eksConfig: local, eksStatus: { upstreamSpec: upstream } };

syncUpstreamConfig( 'eks', testCluster);

expect(testCluster.eksConfig).toStrictEqual(expected);
});

it('should not set empty objects or arrays from upstream spec', () => {
const upstream = {
emptyArray: [],
emptyObject: {},
nonEmptyArray: [1, 2, 3],
nonEmptyObject: { foo: 'bar' },
alreadySet: 'abc',
alreadySetArray: [2, 3, 4],
};
const local = {
alreadySet: 'def',
alreadySetArray: [1, 2, 3],
};

const expected = {
nonEmptyArray: [1, 2, 3],
nonEmptyObject: { foo: 'bar' },
alreadySet: 'def',
alreadySetArray: [1, 2, 3],
};

const testCluster = { eksConfig: local, eksStatus: { upstreamSpec: upstream } };

syncUpstreamConfig( 'eks', testCluster);

expect(testCluster.eksConfig).toStrictEqual(expected);
});

it('should not overwrite boolean values explicitly set false', () => {
const upstream = {
falseBoolean: false,
trueBoolean: true,
alreadySetBooleanFalse: true,
alreadySetBooleanTrue: false
};
const local = {
alreadySetBooleanFalse: false,
alreadySetBooleanTrue: true
};

const expected = {
falseBoolean: false,
trueBoolean: true,
alreadySetBooleanFalse: false,
alreadySetBooleanTrue: true
};

const testCluster = { eksConfig: local, eksStatus: { upstreamSpec: upstream } };

syncUpstreamConfig( 'eks', testCluster);

expect(testCluster.eksConfig).toStrictEqual(expected);
});
});
6 changes: 5 additions & 1 deletion shell/utils/kontainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ export function syncUpstreamConfig(configPrefix: string, normanCluster: {[key: s

if (!isEmpty(upstreamConfig)) {
Object.keys(upstreamConfig).forEach((key) => {
if (isEmpty(rancherConfig[key]) && !isEmpty(upstreamConfig[key])) {
if (typeof upstreamConfig[key] === 'object') {
if (isEmpty(rancherConfig[key]) && !isEmpty(upstreamConfig[key])) {
set(rancherConfig, key, upstreamConfig[key]);
}
} else if ((rancherConfig[key] === null || rancherConfig[key] === undefined) && upstreamConfig[key] !== null && upstreamConfig[key] !== undefined) {
set(rancherConfig, key, upstreamConfig[key]);
}
});
Expand Down
Loading