Skip to content

Commit

Permalink
GKE - allow initial node count to be 0 (rancher#11860)
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
  • Loading branch information
mantis-toboggan-md authored Sep 6, 2024
1 parent 231995f commit e6319fc
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 32 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
v-model:credential="config.googleCredentialSecret"
v-model:project="config.projectID"
Expand Down Expand Up @@ -884,7 +872,7 @@ export default defineComponent({
/>
</Accordion>
</div>
</template>
</div>
<template
v-if="!hasCredential"
#form-footer
Expand Down
30 changes: 15 additions & 15 deletions pkg/gke/components/GKENodePool.vue
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ export default defineComponent({
:disabled="!isNew"
:rules="rules.poolName"
required
@input="$emit('update:name', $event)"
@update:value="$emit('update:name', $event)"
/>
</div>
<div class="col span-4">
Expand All @@ -310,7 +310,7 @@ export default defineComponent({
:value="initialNodeCount"
label-key="gke.initialNodeCount.label"
:rules="rules.initialNodeCount"
@input="$emit('update:initialNodeCount', $event)"
@update:value="$emit('update:initialNodeCount', $event)"
/>
</div>
<div class="col span-4">
Expand All @@ -319,7 +319,7 @@ export default defineComponent({
:mode="mode"
:value="maxPodsConstraint"
label-key="gke.maxPodsConstraint.label"
@input="$emit('update:maxPodsConstraint', $event)"
@update:value="$emit('update:maxPodsConstraint', $event)"
/>
</div>
</div>
Expand All @@ -329,19 +329,19 @@ export default defineComponent({
:mode="mode"
:value="autoscaling"
label-key="gke.autoscaling.label"
@input="$emit('update:autoscaling', $event)"
@update:value="$emit('update:autoscaling', $event)"
/>
<Checkbox
:mode="mode"
:value="autoRepair"
label-key="gke.autoRepair.label"
@input="$emit('update:autoRepair', $event)"
@update:value="$emit('update:autoRepair', $event)"
/>
<Checkbox
:mode="mode"
:value="autoUpgrade"
label-key="gke.autoUpgrade.label"
@input="$emit('update:autoUpgrade', $event)"
@update:value="$emit('update:autoUpgrade', $event)"
/>
</div>
<template v-if="autoscaling">
Expand All @@ -351,7 +351,7 @@ export default defineComponent({
type="number"
:value="minNodeCount"
label-key="gke.minNodeCount.label"
@input="$emit('update:minNodeCount', $event)"
@update:value="$emit('update:minNodeCount', $event)"
/>
</div>
<div class="col span-4">
Expand All @@ -360,7 +360,7 @@ export default defineComponent({
type="number"
:value="maxNodeCount"
label-key="gke.maxNodeCount.label"
@input="$emit('update:maxNodeCount', $event)"
@update:value="$emit('update:maxNodeCount', $event)"
/>
</div>
</template>
Expand Down Expand Up @@ -443,7 +443,7 @@ export default defineComponent({
suffix="GB"
:disabled="!isNew"
:rules="rules.diskSizeGb"
@input="$emit('update:diskSizeGb', $event)"
@update:value="$emit('update:diskSizeGb', $event)"
/>
</div>
<div class="col span-4">
Expand All @@ -453,7 +453,7 @@ export default defineComponent({
label-key="gke.localSsdCount.label"
:disabled="!isNew"
:rules="rules.ssdCount"
@input="$emit('update:localSsdCount', $event)"
@update:value="$emit('update:localSsdCount', $event)"
/>
</div>
</div>
Expand All @@ -464,7 +464,7 @@ export default defineComponent({
:mode="mode"
:value="preemptible"
:disabled="!isNew"
@input="$emit('update:preemptible', $event)"
@update:value="$emit('update:preemptible', $event)"
/>
</div>
</div>
Expand All @@ -476,7 +476,7 @@ export default defineComponent({
:disabled="!isNew"
:effect-values="{NO_SCHEDULE:'NoSchedule', PREFER_NO_SCHEDULE: 'PreferNoSchedule', NO_EXECUTE: 'NoExecute'}"
data-testid="gke-taints-comp"
@input="$emit('update:taints', $event)"
@update:value="$emit('update:taints', $event)"
/>
</div>
</div>
Expand All @@ -491,7 +491,7 @@ export default defineComponent({
:title-protip="t('gke.nodeLabels.tooltip')"
:add-label="t('gke.nodeLabels.add')"
:disabled="!isNew"
@input="$emit('update:labels', $event)"
@update:value="$emit('update:labels', $event)"
/>
</div>
</div>
Expand All @@ -502,7 +502,7 @@ export default defineComponent({
:value="tags"
:title="t('gke.tags.label')"
:add-label="t('gke.tags.add')"
@input="$emit('update:tags', $event)"
@update:value="$emit('update:tags', $event)"
/>
</div>
</div>
Expand All @@ -511,7 +511,7 @@ export default defineComponent({
:mode="mode"
:value="oauthScopes"
:disabled="!isNew"
@input="$emit('update:oauthScopes', $event)"
@update:value="$emit('update:oauthScopes', $event)"
/>
</div>
</template>
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 e6319fc

Please sign in to comment.