Skip to content

Commit 9b8558e

Browse files
authored
Merge pull request #8412 from sbueringer/pr-fix-scale-to-0
Fix scale to 0 for Cluster API NodePool
2 parents 612fdbb + c3257e9 commit 9b8558e

File tree

2 files changed

+52
-6
lines changed

2 files changed

+52
-6
lines changed

cluster-autoscaler/cloudprovider/clusterapi/clusterapi_unstructured.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"time"
2727

2828
"github.com/pkg/errors"
29-
autoscalingv1 "k8s.io/api/autoscaling/v1"
3029
apiv1 "k8s.io/api/core/v1"
3130
corev1 "k8s.io/api/core/v1"
3231
resourceapi "k8s.io/api/resource/v1beta1"
@@ -124,9 +123,9 @@ func (r unstructuredScalableResource) SetSize(nreplicas int) error {
124123
return err
125124
}
126125

127-
spec := autoscalingv1.Scale{
128-
Spec: autoscalingv1.ScaleSpec{
129-
Replicas: int32(nreplicas),
126+
spec := autoscalingv1Scale{
127+
Spec: autoscalingv1ScaleSpec{
128+
Replicas: ptr.To(int32(nreplicas)),
130129
},
131130
}
132131

@@ -144,6 +143,24 @@ func (r unstructuredScalableResource) SetSize(nreplicas int) error {
144143
return updateErr
145144
}
146145

146+
// scale is a version of the autoscalingv1.Scale struct that marshals correctly.
147+
// Specifically the Spec.Replicas field is *int32 instead of int32.
148+
// Accordingly Spec.Replicas = 0 is not omitted, which is important for autoscale to 0 to work.
149+
type autoscalingv1Scale struct {
150+
// spec defines the behavior of the scale. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status.
151+
// +optional
152+
Spec autoscalingv1ScaleSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
153+
}
154+
155+
type autoscalingv1ScaleSpec struct {
156+
// replicas is the desired number of instances for the scaled object.
157+
// +optional
158+
// +k8s:optional
159+
// +default=0
160+
// +k8s:minimum=0
161+
Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"`
162+
}
163+
147164
func (r unstructuredScalableResource) UnmarkMachineForDeletion(machine *unstructured.Unstructured) error {
148165
u, err := r.controller.managementClient.Resource(r.controller.machineResource).Namespace(machine.GetNamespace()).Get(context.TODO(), machine.GetName(), metav1.GetOptions{})
149166
if err != nil {

cluster-autoscaler/cloudprovider/clusterapi/clusterapi_unstructured_test.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const (
4141
func TestSetSize(t *testing.T) {
4242
initialReplicas := 1
4343
updatedReplicas := 5
44+
finalReplicas := 0
4445

4546
test := func(t *testing.T, testConfig *testConfig) {
4647
controller, stop := mustCreateTestController(t, testConfig)
@@ -61,6 +62,7 @@ func TestSetSize(t *testing.T) {
6162
t.Fatal(err)
6263
}
6364

65+
// First update to updatedReplicas
6466
err = sr.SetSize(updatedReplicas)
6567
if err != nil {
6668
t.Fatal(err)
@@ -86,6 +88,33 @@ func TestSetSize(t *testing.T) {
8688
if replicas != int64(updatedReplicas) {
8789
t.Errorf("expected %v, got: %v", updatedReplicas, replicas)
8890
}
91+
92+
// Second update to finalReplicas
93+
err = sr.SetSize(finalReplicas)
94+
if err != nil {
95+
t.Fatal(err)
96+
}
97+
98+
s, err = sr.controller.managementScaleClient.Scales(testResource.GetNamespace()).
99+
Get(context.TODO(), gvr.GroupResource(), testResource.GetName(), metav1.GetOptions{})
100+
if err != nil {
101+
t.Fatalf("error getting scale subresource: %v", err)
102+
}
103+
104+
if s.Spec.Replicas != int32(finalReplicas) {
105+
t.Errorf("expected %v, got: %v", finalReplicas, s.Spec.Replicas)
106+
}
107+
108+
replicas, found, err = unstructured.NestedInt64(sr.unstructured.Object, "spec", "replicas")
109+
if err != nil {
110+
t.Fatal(err)
111+
}
112+
if !found {
113+
t.Fatal("replicas = 0")
114+
}
115+
if replicas != int64(finalReplicas) {
116+
t.Errorf("expected %v, got: %v", finalReplicas, replicas)
117+
}
89118
}
90119

91120
t.Run("MachineSet", func(t *testing.T) {
@@ -94,7 +123,7 @@ func TestSetSize(t *testing.T) {
94123
RandomString(6),
95124
RandomString(6),
96125
initialReplicas, map[string]string{
97-
nodeGroupMinSizeAnnotationKey: "1",
126+
nodeGroupMinSizeAnnotationKey: "0",
98127
nodeGroupMaxSizeAnnotationKey: "10",
99128
},
100129
nil,
@@ -107,7 +136,7 @@ func TestSetSize(t *testing.T) {
107136
RandomString(6),
108137
RandomString(6),
109138
initialReplicas, map[string]string{
110-
nodeGroupMinSizeAnnotationKey: "1",
139+
nodeGroupMinSizeAnnotationKey: "0",
111140
nodeGroupMaxSizeAnnotationKey: "10",
112141
},
113142
nil,

0 commit comments

Comments
 (0)