Skip to content

Commit 0135673

Browse files
Copilotfredleger
andcommitted
feat: allow autoscaling configuration for node pools
Co-authored-by: fredleger <2778741+fredleger@users.noreply.github.com>
1 parent 98e940b commit 0135673

8 files changed

Lines changed: 324 additions & 12 deletions

File tree

action.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,22 @@ inputs:
4646
description: "The ID of the OVH MKS nodepool"
4747
number-of-nodes:
4848
required: true
49-
description: "The number of nodes to scale to"
49+
description: "The desired number of nodes to scale to"
5050
default: "1"
51+
autoscale:
52+
required: false
53+
description: "Whether to enable autoscaling for the nodepool"
54+
default: "true"
55+
min-nodes:
56+
required: false
57+
description: |
58+
The minimum number of nodes for autoscaling.
59+
Defaults to the value of number-of-nodes if not provided.
60+
max-nodes:
61+
required: false
62+
description: |
63+
The maximum number of nodes for autoscaling.
64+
Defaults to the value of number-of-nodes if not provided.
5165
5266
outputs:
5367
response:

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,6 @@
8585
]
8686
},
8787
"tsDevTools": {
88-
"version": "20250623095500-add-prettier-oxc"
88+
"version": "20250623095600-remove-prettier-oxc"
8989
}
90-
}
90+
}

src/index-runner.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ describe("run", () => {
3636
clusterId: "cluster-id",
3737
nodepoolId: "nodepool-id",
3838
numberOfNodes: 3,
39+
autoscale: true,
40+
minNodes: null,
41+
maxNodes: null,
3942
}));
4043

4144
scaleNodepoolMock.mockResolvedValueOnce({} as NodepoolUpdateResponse);
@@ -46,7 +49,7 @@ describe("run", () => {
4649
// Assert
4750
expect(debugMock).toHaveBeenNthCalledWith(
4851
1,
49-
'inputs: {"endpoint":"ovh-eu","appKey":"app-key","appSecret":"app-secret","consumerKey":"consumer-key","clientId":null,"clientSecret":null,"projectId":"project-id","clusterId":"cluster-id","nodepoolId":"nodepool-id","numberOfNodes":3}'
52+
'inputs: {"endpoint":"ovh-eu","appKey":"app-key","appSecret":"app-secret","consumerKey":"consumer-key","clientId":null,"clientSecret":null,"projectId":"project-id","clusterId":"cluster-id","nodepoolId":"nodepool-id","numberOfNodes":3,"autoscale":true,"minNodes":null,"maxNodes":null}'
5053
);
5154

5255
expect(infoMock).toHaveBeenNthCalledWith(
@@ -59,6 +62,9 @@ describe("run", () => {
5962
clusterId: "cluster-id",
6063
nodepoolId: "nodepool-id",
6164
numberOfNodes: 3,
65+
autoscale: true,
66+
minNodes: null,
67+
maxNodes: null,
6268
});
6369

6470
expect(setFailedMock).not.toHaveBeenCalled();
@@ -82,6 +88,9 @@ describe("run", () => {
8288
clusterId: "cluster-id",
8389
nodepoolId: "nodepool-id",
8490
numberOfNodes: 3,
91+
autoscale: true,
92+
minNodes: null,
93+
maxNodes: null,
8594
}));
8695

8796
// Act
@@ -107,6 +116,9 @@ describe("run", () => {
107116
clusterId: "cluster-id",
108117
nodepoolId: "nodepool-id",
109118
numberOfNodes: 3,
119+
autoscale: true,
120+
minNodes: null,
121+
maxNodes: null,
110122
}));
111123

112124
// Act

src/index-runner.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ export async function run(): Promise<void> {
3232
clusterId: inputs.clusterId,
3333
nodepoolId: inputs.nodepoolId,
3434
numberOfNodes: inputs.numberOfNodes,
35+
autoscale: inputs.autoscale,
36+
minNodes: inputs.minNodes,
37+
maxNodes: inputs.maxNodes,
3538
});
3639
loggerService.info("Nodepool scaling completed successfully.");
3740
setOutput("result", JSON.stringify(result));

src/services/input.service.test.ts

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,5 +482,168 @@ describe("InputService", () => {
482482
expect(inputs.numberOfNodes).toBeNaN();
483483
});
484484
});
485+
486+
describe("autoscale", () => {
487+
it("should return true when autoscale input is not provided", () => {
488+
getInputMock.mockImplementation((inputName) => {
489+
switch (inputName) {
490+
case InputNames.ProjectId:
491+
return "project-123";
492+
case InputNames.ClusterId:
493+
return "cluster-123";
494+
case InputNames.NodepoolId:
495+
return "nodepool-456";
496+
case InputNames.NumberOfNodes:
497+
return "3";
498+
case InputNames.Autoscale:
499+
return "";
500+
default:
501+
return "";
502+
}
503+
});
504+
505+
const inputs = service.getInputs();
506+
507+
expect(inputs.autoscale).toBe(true);
508+
});
509+
510+
it("should return true when autoscale input is 'true'", () => {
511+
getInputMock.mockImplementation((inputName) => {
512+
switch (inputName) {
513+
case InputNames.ProjectId:
514+
return "project-123";
515+
case InputNames.ClusterId:
516+
return "cluster-123";
517+
case InputNames.NodepoolId:
518+
return "nodepool-456";
519+
case InputNames.NumberOfNodes:
520+
return "3";
521+
case InputNames.Autoscale:
522+
return "true";
523+
default:
524+
return "";
525+
}
526+
});
527+
528+
const inputs = service.getInputs();
529+
530+
expect(inputs.autoscale).toBe(true);
531+
});
532+
533+
it("should return false when autoscale input is 'false'", () => {
534+
getInputMock.mockImplementation((inputName) => {
535+
switch (inputName) {
536+
case InputNames.ProjectId:
537+
return "project-123";
538+
case InputNames.ClusterId:
539+
return "cluster-123";
540+
case InputNames.NodepoolId:
541+
return "nodepool-456";
542+
case InputNames.NumberOfNodes:
543+
return "3";
544+
case InputNames.Autoscale:
545+
return "false";
546+
default:
547+
return "";
548+
}
549+
});
550+
551+
const inputs = service.getInputs();
552+
553+
expect(inputs.autoscale).toBe(false);
554+
});
555+
});
556+
557+
describe("min-nodes", () => {
558+
it("should return null when min-nodes input is not provided", () => {
559+
getInputMock.mockImplementation((inputName) => {
560+
switch (inputName) {
561+
case InputNames.ProjectId:
562+
return "project-123";
563+
case InputNames.ClusterId:
564+
return "cluster-123";
565+
case InputNames.NodepoolId:
566+
return "nodepool-456";
567+
case InputNames.NumberOfNodes:
568+
return "3";
569+
default:
570+
return "";
571+
}
572+
});
573+
574+
const inputs = service.getInputs();
575+
576+
expect(inputs.minNodes).toBeNull();
577+
});
578+
579+
it("should return given min-nodes input as number", () => {
580+
getInputMock.mockImplementation((inputName) => {
581+
switch (inputName) {
582+
case InputNames.ProjectId:
583+
return "project-123";
584+
case InputNames.ClusterId:
585+
return "cluster-123";
586+
case InputNames.NodepoolId:
587+
return "nodepool-456";
588+
case InputNames.NumberOfNodes:
589+
return "3";
590+
case InputNames.MinNodes:
591+
return "1";
592+
default:
593+
return "";
594+
}
595+
});
596+
597+
const inputs = service.getInputs();
598+
599+
expect(inputs.minNodes).toEqual(1);
600+
});
601+
});
602+
603+
describe("max-nodes", () => {
604+
it("should return null when max-nodes input is not provided", () => {
605+
getInputMock.mockImplementation((inputName) => {
606+
switch (inputName) {
607+
case InputNames.ProjectId:
608+
return "project-123";
609+
case InputNames.ClusterId:
610+
return "cluster-123";
611+
case InputNames.NodepoolId:
612+
return "nodepool-456";
613+
case InputNames.NumberOfNodes:
614+
return "3";
615+
default:
616+
return "";
617+
}
618+
});
619+
620+
const inputs = service.getInputs();
621+
622+
expect(inputs.maxNodes).toBeNull();
623+
});
624+
625+
it("should return given max-nodes input as number", () => {
626+
getInputMock.mockImplementation((inputName) => {
627+
switch (inputName) {
628+
case InputNames.ProjectId:
629+
return "project-123";
630+
case InputNames.ClusterId:
631+
return "cluster-123";
632+
case InputNames.NodepoolId:
633+
return "nodepool-456";
634+
case InputNames.NumberOfNodes:
635+
return "3";
636+
case InputNames.MaxNodes:
637+
return "10";
638+
default:
639+
return "";
640+
}
641+
});
642+
643+
const inputs = service.getInputs();
644+
645+
expect(inputs.maxNodes).toEqual(10);
646+
});
647+
});
485648
});
486649
});

src/services/input.service.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export type Inputs = {
1616
clusterId: string;
1717
nodepoolId: string;
1818
numberOfNodes: number;
19+
autoscale: boolean;
20+
minNodes: number | null;
21+
maxNodes: number | null;
1922
};
2023

2124
export enum InputNames {
@@ -35,6 +38,9 @@ export enum InputNames {
3538
ClusterId = "cluster-id",
3639
NodepoolId = "nodepool-id",
3740
NumberOfNodes = "number-of-nodes",
41+
Autoscale = "autoscale",
42+
MinNodes = "min-nodes",
43+
MaxNodes = "max-nodes",
3844
}
3945

4046
export class InputService {
@@ -53,6 +59,9 @@ export class InputService {
5359
clusterId: this.getClusterId(),
5460
nodepoolId: this.getNodepoolId(),
5561
numberOfNodes: this.getNumberOfNodes(),
62+
autoscale: this.getAutoscale(),
63+
minNodes: this.getMinNodes(),
64+
maxNodes: this.getMaxNodes(),
5665
};
5766
}
5867

@@ -95,4 +104,21 @@ export class InputService {
95104
private getNumberOfNodes(): number {
96105
return parseInt(getInput(InputNames.NumberOfNodes, { required: true }), 10);
97106
}
107+
108+
private getAutoscale(): boolean {
109+
const value = getInput(InputNames.Autoscale, { required: false });
110+
return value === "" || value.toLowerCase() !== "false";
111+
}
112+
113+
private getMinNodes(): number | null {
114+
const value = getInput(InputNames.MinNodes, { required: false });
115+
if (!value) return null;
116+
return parseInt(value, 10);
117+
}
118+
119+
private getMaxNodes(): number | null {
120+
const value = getInput(InputNames.MaxNodes, { required: false });
121+
if (!value) return null;
122+
return parseInt(value, 10);
123+
}
98124
}

0 commit comments

Comments
 (0)