Skip to content

Commit

Permalink
[2.9]GKE - allow initial node count to be 0 (#11862)
Browse files Browse the repository at this point in the history
* fix gke node pool count validation and event emitting from the gkenodepool component

* update gke node count validation and refactor to enable unit tests

* revert vue3 syntax change from cherry pick
  • Loading branch information
mantis-toboggan-md authored Sep 6, 2024
1 parent d305d0b commit 9d6d98b
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 17 deletions.
20 changes: 4 additions & 16 deletions pkg/gke/components/CruGKE.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import type { getGKEMachineTypesResponse, getGKEServiceAccountsResponse } from '
import type { GKEMachineTypeOption } from '../types/index.d.ts';
import debounce from 'lodash/debounce';
import {
clusterNameChars, clusterNameStartEnd, requiredInCluster, ipv4WithCidr, ipv4oripv6WithCidr
clusterNameChars, clusterNameStartEnd, requiredInCluster, ipv4WithCidr, ipv4oripv6WithCidr, GKEInitialCount
} from '../util/validators';
import { diffUpstreamSpec, syncUpstreamConfig } from '@shell/utils/kontainer';
Expand Down Expand Up @@ -322,6 +322,7 @@ export default defineComponent({
nodeIpv4CidrBlockFormat: ipv4WithCidr(this, 'gke.nodeIpv4CidrBlock.label', 'gkeConfig.ipAllocationPolicy.nodeIpv4CidrBlock'),
servicesIpv4CidrBlockFormat: ipv4WithCidr(this, 'gke.servicesIpv4CidrBlock.label', 'gkeConfig.ipAllocationPolicy.servicesIpv4CidrBlock'),
clusterIpv4CidrFormat: ipv4oripv6WithCidr(this, 'gke.clusterIpv4Cidr.label', 'gkeConfig.clusterIpv4Cidr'),
initialNodeCount: GKEInitialCount(this),
/**
* The nodepool validators below are performing double duty. When passed directly to an input, the val argument is provided and validated - this generates the error icon in the input component.
* otherwise they're run in the fv mixin and ALL nodepools are validated - this disables the cruresource create button
Expand All @@ -339,19 +340,6 @@ export default defineComponent({
return !!this.nodePools.find((pool: GKENodePool) => !valid(pool.config.diskSizeGb || 0) ) ? this.t('gke.errors.diskSizeGb') : null;
},
initialNodeCount: (val: number) => {
if (!this.isAuthenticated) {
return;
}
const valid = (input: number) => input >= 1;
if (val || val === 0) {
return !valid(val) ? this.t('gke.errors.initialNodeCount') : null;
}
return !!this.nodePools.find((pool: GKENodePool) => !valid(pool.initialNodeCount || 0) ) ? this.t('gke.errors.initialNodeCount') : null;
},
ssdCount: (val: number) => {
if (!this.isAuthenticated) {
return;
Expand Down Expand Up @@ -686,7 +674,7 @@ export default defineComponent({
@error="e=>errors=e"
@finish="save"
>
<template>
<div>
<AccountAccess
:mode="mode"
:credential.sync="config.googleCredentialSecret"
Expand Down Expand Up @@ -884,7 +872,7 @@ export default defineComponent({
/>
</Accordion>
</div>
</template>
</div>
<template
v-if="!hasCredential"
#form-footer
Expand Down
2 changes: 1 addition & 1 deletion pkg/gke/l10n/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ gke:
clusterNameStartEnd: Cluster name must startd and end with a letter or number.
diskSizeGb: Disk Size must be at least 10GB.
genericKey: Value
initialNodeCount: Initial Node Count must be at least 1.
initialNodeCount: Initial Node Count must be at least 0, and no greater than 1000.
ipv4Cidr: '{key} must be a valid ipv4 cidr range.'
ipv4oripv6: '{key} must be a valid ipv6 or ipv4 CIDR address'
minMaxNodeCount: Minimum Node Count cannot exceed Maximum Node Count.
Expand Down
42 changes: 42 additions & 0 deletions pkg/gke/util/__tests__/validators.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { GKEInitialCount } from '../validators';

const mockTranslation = (key: string) => key;

describe('validate GKE node group initial count', () => {
it.each([
[-1, 'gke.errors.initialNodeCount'],
[0, null],
[2, null],
[1000, null],
[1001, 'gke.errors.initialNodeCount']
])('should return an error if the initial node count is less than 0 or greater than 1000', (count, errMsg) => {
const ctx = {
config: { },
t: mockTranslation,
isAuthenticated: true
} as any;

const res = GKEInitialCount(ctx)(count);

expect(res).toBe(errMsg);
});

it.each([
[[{ initialNodeCount: -1 }], 'gke.errors.initialNodeCount'],
[[{ initialNodeCount: 0 }, { initialNodeCount: 1 }], null],
[[{ initialNodeCount: 1000 }, { initialNodeCount: 1 }], null],
[[{ initialNodeCount: 1001 }, { initialNodeCount: 1 }], 'gke.errors.initialNodeCount']

])('should validate each node group in the component context if not called with a specific count value', (nodePools, errMsg) => {
const ctx = {
config: { },
t: mockTranslation,
nodePools,
isAuthenticated: true
} as any;

const res = GKEInitialCount(ctx)();

expect(res).toBe(errMsg);
});
});
16 changes: 16 additions & 0 deletions pkg/gke/util/validators.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { get } from '@shell/utils/object';
import { GKENodePool } from '../types';
import ipaddr from 'ipaddr.js';

// no need to try to validate any fields if the user is still selecting a credential and the rest of the form isn't visible
Expand Down Expand Up @@ -69,3 +70,18 @@ export const ipv4oripv6WithCidr = (ctx: any, labelKey: string, clusterPath: stri
return isValid || !toValidate.length ? undefined : ctx.t('gke.errors.ipv4Cidr', { key: ctx.t(labelKey) || ctx.t('gke.errors.genericKey') });
};
};

export const GKEInitialCount = (ctx:any) => {
return (val?: number) => {
if (!ctx.isAuthenticated) {
return;
}
const valid = (input?: number) => (!!input || input === 0) && input >= 0 && input <= 1000;

if (val || val === 0) {
return !valid(val) ? ctx.t('gke.errors.initialNodeCount') : null;
}

return !!ctx.nodePools.find((pool: GKENodePool) => !valid(pool.initialNodeCount) ) ? ctx.t('gke.errors.initialNodeCount') : null;
};
};

0 comments on commit 9d6d98b

Please sign in to comment.