Skip to content

Commit f4d05aa

Browse files
authored
Merge pull request kubernetes#128470 from omerap12/autoscale-unit
kubectl: add test coverage for autoscale command
2 parents 3036d10 + 6cb17d8 commit f4d05aa

File tree

1 file changed

+298
-0
lines changed

1 file changed

+298
-0
lines changed
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package autoscale
18+
19+
import (
20+
"fmt"
21+
"testing"
22+
23+
"github.com/stretchr/testify/assert"
24+
25+
autoscalingv1 "k8s.io/api/autoscaling/v1"
26+
"k8s.io/apimachinery/pkg/api/meta"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/apimachinery/pkg/runtime/schema"
29+
"k8s.io/utils/ptr"
30+
)
31+
32+
type validateTestCase struct {
33+
name string
34+
options *AutoscaleOptions
35+
expectedError error
36+
}
37+
38+
func TestAutoscaleValidate(t *testing.T) {
39+
tests := []validateTestCase{
40+
{
41+
name: "valid options",
42+
options: &AutoscaleOptions{
43+
Max: 10,
44+
Min: 1,
45+
},
46+
expectedError: nil,
47+
},
48+
{
49+
name: "max less than 1",
50+
options: &AutoscaleOptions{
51+
Max: 0,
52+
Min: 1,
53+
},
54+
expectedError: fmt.Errorf("--max=MAXPODS is required and must be at least 1, max: 0"),
55+
},
56+
{
57+
name: "min greater than max",
58+
options: &AutoscaleOptions{
59+
Max: 1,
60+
Min: 2,
61+
},
62+
expectedError: fmt.Errorf("--max=MAXPODS must be larger or equal to --min=MINPODS, max: 1, min: 2"),
63+
},
64+
{
65+
name: "zero min replicas",
66+
options: &AutoscaleOptions{
67+
Max: 5,
68+
Min: 0,
69+
},
70+
expectedError: nil,
71+
},
72+
{
73+
name: "negative min replicas",
74+
options: &AutoscaleOptions{
75+
Max: 5,
76+
Min: -2,
77+
},
78+
expectedError: nil,
79+
},
80+
}
81+
for _, tc := range tests {
82+
t.Run(tc.name, func(t *testing.T) {
83+
errorGot := tc.options.Validate()
84+
assert.Equal(t, tc.expectedError, errorGot)
85+
})
86+
}
87+
}
88+
89+
type createHorizontalPodAutoscalerTestCase struct {
90+
name string
91+
options *AutoscaleOptions
92+
refName string
93+
mapping *meta.RESTMapping
94+
expectedHPA *autoscalingv1.HorizontalPodAutoscaler
95+
}
96+
97+
func TestCreateHorizontalPodAutoscaler(t *testing.T) {
98+
tests := []createHorizontalPodAutoscalerTestCase{
99+
{
100+
name: "create with all options",
101+
options: &AutoscaleOptions{
102+
Name: "custom-name",
103+
Max: 10,
104+
Min: 2,
105+
CPUPercent: 80,
106+
},
107+
refName: "deployment-1",
108+
mapping: &meta.RESTMapping{
109+
GroupVersionKind: schema.GroupVersionKind{
110+
Group: "apps",
111+
Version: "v1",
112+
Kind: "Deployment",
113+
},
114+
},
115+
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
116+
ObjectMeta: metav1.ObjectMeta{
117+
Name: "custom-name",
118+
},
119+
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
120+
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
121+
APIVersion: "apps/v1",
122+
Kind: "Deployment",
123+
Name: "deployment-1",
124+
},
125+
MinReplicas: ptr.To(int32(2)),
126+
MaxReplicas: int32(10),
127+
TargetCPUUtilizationPercentage: ptr.To(int32(80)),
128+
},
129+
},
130+
},
131+
{
132+
name: "create without min replicas",
133+
options: &AutoscaleOptions{
134+
Name: "custom-name-2",
135+
Max: 10,
136+
Min: -1,
137+
CPUPercent: 80,
138+
},
139+
refName: "deployment-2",
140+
mapping: &meta.RESTMapping{
141+
GroupVersionKind: schema.GroupVersionKind{
142+
Group: "apps",
143+
Version: "v1",
144+
Kind: "Deployment",
145+
},
146+
},
147+
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
148+
ObjectMeta: metav1.ObjectMeta{
149+
Name: "custom-name-2",
150+
},
151+
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
152+
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
153+
APIVersion: "apps/v1",
154+
Kind: "Deployment",
155+
Name: "deployment-2",
156+
},
157+
MinReplicas: nil,
158+
MaxReplicas: int32(10),
159+
TargetCPUUtilizationPercentage: ptr.To(int32(80)),
160+
},
161+
},
162+
},
163+
{
164+
name: "create without max replicas",
165+
options: &AutoscaleOptions{
166+
Name: "custom-name-3",
167+
Max: -1,
168+
Min: 2,
169+
CPUPercent: 80,
170+
},
171+
refName: "deployment-3",
172+
mapping: &meta.RESTMapping{
173+
GroupVersionKind: schema.GroupVersionKind{
174+
Group: "apps",
175+
Version: "v1",
176+
Kind: "Deployment",
177+
},
178+
},
179+
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
180+
ObjectMeta: metav1.ObjectMeta{
181+
Name: "custom-name-3",
182+
},
183+
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
184+
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
185+
APIVersion: "apps/v1",
186+
Kind: "Deployment",
187+
Name: "deployment-3",
188+
},
189+
MinReplicas: ptr.To(int32(2)),
190+
MaxReplicas: int32(-1),
191+
TargetCPUUtilizationPercentage: ptr.To(int32(80)),
192+
},
193+
},
194+
},
195+
{
196+
name: "create without cpu utilization",
197+
options: &AutoscaleOptions{
198+
Name: "custom-name-4",
199+
Max: 10,
200+
Min: 2,
201+
CPUPercent: -1,
202+
},
203+
refName: "deployment-4",
204+
mapping: &meta.RESTMapping{
205+
GroupVersionKind: schema.GroupVersionKind{
206+
Group: "apps",
207+
Version: "v1",
208+
Kind: "Deployment",
209+
},
210+
},
211+
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
212+
ObjectMeta: metav1.ObjectMeta{
213+
Name: "custom-name-4",
214+
},
215+
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
216+
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
217+
APIVersion: "apps/v1",
218+
Kind: "Deployment",
219+
Name: "deployment-4",
220+
},
221+
MinReplicas: ptr.To(int32(2)),
222+
MaxReplicas: int32(10),
223+
TargetCPUUtilizationPercentage: nil,
224+
},
225+
},
226+
},
227+
{
228+
name: "create with replicaset reference",
229+
options: &AutoscaleOptions{
230+
Name: "replicaset-hpa",
231+
Max: 5,
232+
Min: 1,
233+
CPUPercent: 70,
234+
},
235+
refName: "frontend",
236+
mapping: &meta.RESTMapping{
237+
GroupVersionKind: schema.GroupVersionKind{
238+
Group: "apps",
239+
Version: "v1",
240+
Kind: "ReplicaSet",
241+
},
242+
},
243+
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
244+
ObjectMeta: metav1.ObjectMeta{
245+
Name: "replicaset-hpa",
246+
},
247+
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
248+
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
249+
APIVersion: "apps/v1",
250+
Kind: "ReplicaSet",
251+
Name: "frontend",
252+
},
253+
MinReplicas: ptr.To(int32(1)),
254+
MaxReplicas: int32(5),
255+
TargetCPUUtilizationPercentage: ptr.To(int32(70)),
256+
},
257+
},
258+
},
259+
{
260+
name: "create with statefulset reference",
261+
options: &AutoscaleOptions{
262+
Name: "statefulset-hpa",
263+
Max: 8,
264+
Min: 2,
265+
CPUPercent: 60,
266+
},
267+
refName: "web",
268+
mapping: &meta.RESTMapping{
269+
GroupVersionKind: schema.GroupVersionKind{
270+
Group: "apps",
271+
Version: "v1",
272+
Kind: "StatefulSet",
273+
},
274+
},
275+
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
276+
ObjectMeta: metav1.ObjectMeta{
277+
Name: "statefulset-hpa",
278+
},
279+
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
280+
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
281+
APIVersion: "apps/v1",
282+
Kind: "StatefulSet",
283+
Name: "web",
284+
},
285+
MinReplicas: ptr.To(int32(2)),
286+
MaxReplicas: int32(8),
287+
TargetCPUUtilizationPercentage: ptr.To(int32(60)),
288+
},
289+
},
290+
},
291+
}
292+
for _, tc := range tests {
293+
t.Run(tc.name, func(t *testing.T) {
294+
hpa := tc.options.createHorizontalPodAutoscaler(tc.refName, tc.mapping)
295+
assert.Equal(t, tc.expectedHPA, hpa)
296+
})
297+
}
298+
}

0 commit comments

Comments
 (0)