diff --git a/pkg/aks/components/AksNodePool.vue b/pkg/aks/components/AksNodePool.vue index 108648fa3a2..73ece1d8ee3 100644 --- a/pkg/aks/components/AksNodePool.vue +++ b/pkg/aks/components/AksNodePool.vue @@ -178,7 +178,9 @@ export default defineComponent({ }, poolCountValidator() { - return (val: number) => this.validationRules?.count?.[0](val, this.pool.enableAutoScaling); + const canBeZero: boolean = this.pool.mode === 'User'; + + return (val: number) => this.validationRules?.count?.[0](val, canBeZero); } }, }); @@ -309,9 +311,10 @@ export default defineComponent({ type="number" :mode="mode" label-key="aks.nodePools.count.label" - :rules="[poolCountValidator]" - :min="pool.enableAutoScaling ? 0 : 1" + :rules="[poolCountValidator()]" + :min="pool.mode === 'User' ? 0 : 1" :max="1000" + data-testid="aks-pool-count-input" />
diff --git a/pkg/aks/components/CruAks.vue b/pkg/aks/components/CruAks.vue index a7010733c36..35b37bcf069 100644 --- a/pkg/aks/components/CruAks.vue +++ b/pkg/aks/components/CruAks.vue @@ -52,7 +52,8 @@ import { outboundTypeUserDefined, privateDnsZone, nodePoolNames, - nodePoolNamesUnique + nodePoolNamesUnique, + nodePoolCount } from '../util/validators'; export const defaultNodePool = { @@ -342,6 +343,7 @@ export default defineComponent({ privateDnsZone: privateDnsZone(this, 'aks.privateDnsZone.label', 'aksConfig.privateDnsZone'), poolNames: nodePoolNames(this), poolNamesUnique: nodePoolNamesUnique(this), + poolCount: nodePoolCount(this), vmSizeAvailable: () => { if (this.touchedVmSize) { @@ -433,40 +435,6 @@ export default defineComponent({ return this.canUseAvailabilityZones || !isUsingAvailabilityZones ? undefined : this.t('aks.errors.availabilityZones'); }, - poolCount: (count?: number, autoscale = false) => { - let min = 1; - let errMsg = this.t('aks.errors.poolCount'); - - if (autoscale) { - min = 0; - errMsg = this.t('aks.errors.poolAutoscaleCount'); - } - if (count || count === 0) { - return count >= min ? undefined : errMsg; - } else { - let allValid = true; - - this.nodePools.forEach((pool: AKSNodePool) => { - const { count = 0, enableAutoScaling } = pool; - - if (enableAutoScaling) { - min = 0; - } else { - min = 1; - } - - if (count < min) { - pool._validation['_validCount'] = false; - allValid = false; - } else { - pool._validation['_validCount'] = true; - } - }); - - return allValid ? undefined : this.t('aks.errors.poolCount'); - } - }, - poolMin: (min?:number) => { if (min || min === 0) { return min < 0 || min > 1000 ? this.t('aks.errors.poolMin') : undefined; diff --git a/pkg/aks/components/__tests__/AksNodePool.test.ts b/pkg/aks/components/__tests__/AksNodePool.test.ts index b2bb5a18d9b..6449b75705a 100644 --- a/pkg/aks/components/__tests__/AksNodePool.test.ts +++ b/pkg/aks/components/__tests__/AksNodePool.test.ts @@ -227,4 +227,39 @@ describe('aks node pool component', () => { expect(wrapper.props().pool.nodeLabels).toStrictEqual(newLabels); }); + + it('should validate pool count using the provided count validator function', async() => { + const countValidator = jest.fn(); + + mount(AksNodePool, { + propsData: { + pool: { ...defaultPool, count: -1 }, + validationRules: { count: [countValidator] } + }, + ...requiredSetup() + + }); + + expect(countValidator).toHaveBeenCalledWith(-1, false); + }); + + it.each([ + ['System', false], + ['User', true], + ])('should validate node pool count differently if the pool mode is User', async(mode, allowZeroCount) => { + const countValidator = jest.fn(); + + mount(AksNodePool, { + propsData: { + pool: { + ...defaultPool, count: -1, mode + }, + validationRules: { count: [countValidator] } + }, + ...requiredSetup() + + }); + + expect(countValidator).toHaveBeenCalledWith(-1, allowZeroCount); + }); }); diff --git a/pkg/aks/l10n/en-us.yaml b/pkg/aks/l10n/en-us.yaml index 1f7798242b8..4ee4dd73bef 100644 --- a/pkg/aks/l10n/en-us.yaml +++ b/pkg/aks/l10n/en-us.yaml @@ -163,8 +163,8 @@ aks: availabilityZones: Availability zones are not available in the selected region. privateDnsZone: Private DNS Zone Resource ID must be in the format /subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCEGROUP_NAME/providers/Microsoft.Network/privateDnsZones/PRIVATE_DNS_ZONE_NAME. The Private DNS Zone Resource Name must be in the format privatelink.REGION.azmk8s.io, SUBZONE.privatelink.REGION.azmk8s.io, private.REGION.azmk8s.io, or SUBZONE.private.REGION.azmk8s.io poolName: Node pool names must be 1-12 characters long, consist only of lowercase letters and numbers, and start with a letter. - poolCount: Node count must be at least one when autoscaling is disabled. - poolAutoscaleCount: Node count cannot be less than zero. + poolCount: Node count must be at least one in System pools. + poolUserCount: Node count cannot be less than zero. poolMinMax: The minimum number of nodes must be less than or equal to the maximum number of nodes, and the node count must be between or equal to the minimum and maximum. poolMin: The minimum number of nodes must be greater than 0 and at most 1000. poolMax: The maximum number of nodes must be greater than 0 and at most 1000. diff --git a/pkg/aks/util/__tests__/validators.test.ts b/pkg/aks/util/__tests__/validators.test.ts index fefcbb1bdb5..94ab855b6e8 100644 --- a/pkg/aks/util/__tests__/validators.test.ts +++ b/pkg/aks/util/__tests__/validators.test.ts @@ -139,3 +139,48 @@ describe('fx: nodePoolNames', () => { expect(validator(name)).toStrictEqual(expected); }); }); + +describe('fx: nodePoolCount', () => { + // AksNodePool unit tests check that the second arg is passed in as expected + it('validates that count is at least 1 when second arg is false', () => { + const validator = validators.nodePoolCount(mockCtx); + + expect(validator(1, false)).toBeUndefined(); + expect(validator(0, false)).toStrictEqual(MOCK_TRANSLATION); + }); + + it('validates that count is at least 0 when second arg is true', () => { + const validator = validators.nodePoolCount(mockCtx); + + expect(validator(1, true)).toBeUndefined(); + expect(validator(0, true)).toBeUndefined(); + expect(validator(-1, true)).toStrictEqual(MOCK_TRANSLATION); + }); + + it('validates each node pool in the provided context when not passed a count value', () => { + const ctx = { + ...mockCtx, + nodePools: [ + { + name: 'abc', _validation: {}, mode: 'System', count: 0 + }, + { + name: 'def', _validation: {}, mode: 'System', count: 1 + }, + { + name: 'hij', _validation: {}, mode: 'User', count: 0 + }, + { + name: 'klm', _validation: {}, mode: 'User', count: -1 + } + ] as unknown as AKSNodePool[] + }; + const validator = validators.nodePoolCount(ctx); + + validator(); + expect(ctx.nodePools[0]?._validation?._validCount).toStrictEqual(false); + expect(ctx.nodePools[1]?._validation?._validCount).toStrictEqual(true); + expect(ctx.nodePools[2]?._validation?._validCount).toStrictEqual(true); + expect(ctx.nodePools[3]?._validation?._validCount).toStrictEqual(false); + }); +}); diff --git a/pkg/aks/util/validators.ts b/pkg/aks/util/validators.ts index 4f0dfdc5df3..0c52340c27e 100644 --- a/pkg/aks/util/validators.ts +++ b/pkg/aks/util/validators.ts @@ -177,3 +177,39 @@ export const nodePoolNamesUnique = (ctx: any) => { } }; }; + +export const nodePoolCount = (ctx:any) => { + return (count?: number, canBeZero = false) => { + let min = 1; + let errMsg = ctx.t('aks.errors.poolCount'); + + if (canBeZero) { + min = 0; + errMsg = ctx.t('aks.errors.poolUserCount'); + } + if (count || count === 0) { + return count >= min ? undefined : errMsg; + } else { + let allValid = true; + + ctx.nodePools.forEach((pool: AKSNodePool) => { + const { count = 0, mode } = pool; + + if (mode === 'User') { + min = 0; + } else { + min = 1; + } + + if (count < min) { + pool._validation['_validCount'] = false; + allValid = false; + } else { + pool._validation['_validCount'] = true; + } + }); + + return allValid ? undefined : ctx.t('aks.errors.poolCount'); + } + }; +};