k8s.io/kubernetes@v1.29.3/pkg/apis/autoscaling/validation/validation_test.go (about) 1 /* 2 Copyright 2016 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 validation 18 19 import ( 20 "strings" 21 "testing" 22 23 "k8s.io/apimachinery/pkg/api/resource" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 utilfeature "k8s.io/apiserver/pkg/util/feature" 26 featuregatetesting "k8s.io/component-base/featuregate/testing" 27 "k8s.io/kubernetes/pkg/apis/autoscaling" 28 api "k8s.io/kubernetes/pkg/apis/core" 29 "k8s.io/kubernetes/pkg/features" 30 utilpointer "k8s.io/utils/pointer" 31 ) 32 33 func TestValidateScale(t *testing.T) { 34 successCases := []autoscaling.Scale{{ 35 ObjectMeta: metav1.ObjectMeta{ 36 Name: "frontend", 37 Namespace: metav1.NamespaceDefault, 38 }, 39 Spec: autoscaling.ScaleSpec{ 40 Replicas: 1, 41 }, 42 }, { 43 ObjectMeta: metav1.ObjectMeta{ 44 Name: "frontend", 45 Namespace: metav1.NamespaceDefault, 46 }, 47 Spec: autoscaling.ScaleSpec{ 48 Replicas: 10, 49 }, 50 }, { 51 ObjectMeta: metav1.ObjectMeta{ 52 Name: "frontend", 53 Namespace: metav1.NamespaceDefault, 54 }, 55 Spec: autoscaling.ScaleSpec{ 56 Replicas: 0, 57 }, 58 }} 59 60 for _, successCase := range successCases { 61 if errs := ValidateScale(&successCase); len(errs) != 0 { 62 t.Errorf("expected success: %v", errs) 63 } 64 } 65 66 errorCases := []struct { 67 scale autoscaling.Scale 68 msg string 69 }{{ 70 scale: autoscaling.Scale{ 71 ObjectMeta: metav1.ObjectMeta{ 72 Name: "frontend", 73 Namespace: metav1.NamespaceDefault, 74 }, 75 Spec: autoscaling.ScaleSpec{ 76 Replicas: -1, 77 }, 78 }, 79 msg: "must be greater than or equal to 0", 80 }} 81 82 for _, c := range errorCases { 83 if errs := ValidateScale(&c.scale); len(errs) == 0 { 84 t.Errorf("expected failure for %s", c.msg) 85 } else if !strings.Contains(errs[0].Error(), c.msg) { 86 t.Errorf("unexpected error: %v, expected: %s", errs[0], c.msg) 87 } 88 } 89 } 90 91 func TestValidateBehavior(t *testing.T) { 92 maxPolicy := autoscaling.MaxPolicySelect 93 minPolicy := autoscaling.MinPolicySelect 94 disabledPolicy := autoscaling.DisabledPolicySelect 95 incorrectPolicy := autoscaling.ScalingPolicySelect("incorrect") 96 simplePoliciesList := []autoscaling.HPAScalingPolicy{{ 97 Type: autoscaling.PercentScalingPolicy, 98 Value: 10, 99 PeriodSeconds: 1, 100 }, { 101 Type: autoscaling.PodsScalingPolicy, 102 Value: 1, 103 PeriodSeconds: 1800, 104 }} 105 successCases := []autoscaling.HorizontalPodAutoscalerBehavior{{ 106 ScaleUp: nil, 107 ScaleDown: nil, 108 }, { 109 ScaleUp: &autoscaling.HPAScalingRules{ 110 StabilizationWindowSeconds: utilpointer.Int32(3600), 111 SelectPolicy: &minPolicy, 112 Policies: simplePoliciesList, 113 }, 114 ScaleDown: &autoscaling.HPAScalingRules{ 115 StabilizationWindowSeconds: utilpointer.Int32(0), 116 SelectPolicy: &disabledPolicy, 117 Policies: simplePoliciesList, 118 }, 119 }, { 120 ScaleUp: &autoscaling.HPAScalingRules{ 121 StabilizationWindowSeconds: utilpointer.Int32(120), 122 SelectPolicy: &maxPolicy, 123 Policies: []autoscaling.HPAScalingPolicy{{ 124 Type: autoscaling.PodsScalingPolicy, 125 Value: 1, 126 PeriodSeconds: 2, 127 }, { 128 Type: autoscaling.PercentScalingPolicy, 129 Value: 3, 130 PeriodSeconds: 4, 131 }, { 132 Type: autoscaling.PodsScalingPolicy, 133 Value: 5, 134 PeriodSeconds: 6, 135 }, { 136 Type: autoscaling.PercentScalingPolicy, 137 Value: 7, 138 PeriodSeconds: 8, 139 }}, 140 }, 141 ScaleDown: &autoscaling.HPAScalingRules{ 142 StabilizationWindowSeconds: utilpointer.Int32(120), 143 SelectPolicy: &maxPolicy, 144 Policies: []autoscaling.HPAScalingPolicy{{ 145 Type: autoscaling.PodsScalingPolicy, 146 Value: 1, 147 PeriodSeconds: 2, 148 }, { 149 Type: autoscaling.PercentScalingPolicy, 150 Value: 3, 151 PeriodSeconds: 4, 152 }, { 153 Type: autoscaling.PodsScalingPolicy, 154 Value: 5, 155 PeriodSeconds: 6, 156 }, { 157 Type: autoscaling.PercentScalingPolicy, 158 Value: 7, 159 PeriodSeconds: 8, 160 }}, 161 }, 162 }} 163 for _, behavior := range successCases { 164 hpa := prepareHPAWithBehavior(behavior) 165 if errs := ValidateHorizontalPodAutoscaler(&hpa); len(errs) != 0 { 166 t.Errorf("expected success: %v", errs) 167 } 168 } 169 errorCases := []struct { 170 behavior autoscaling.HorizontalPodAutoscalerBehavior 171 msg string 172 }{{ 173 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 174 ScaleUp: &autoscaling.HPAScalingRules{ 175 SelectPolicy: &minPolicy, 176 }, 177 }, 178 msg: "spec.behavior.scaleUp.policies: Required value: must specify at least one Policy", 179 }, { 180 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 181 ScaleUp: &autoscaling.HPAScalingRules{ 182 StabilizationWindowSeconds: utilpointer.Int32(3601), 183 SelectPolicy: &minPolicy, 184 Policies: simplePoliciesList, 185 }, 186 }, 187 msg: "spec.behavior.scaleUp.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600", 188 }, { 189 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 190 ScaleUp: &autoscaling.HPAScalingRules{ 191 Policies: []autoscaling.HPAScalingPolicy{{ 192 Type: autoscaling.PodsScalingPolicy, 193 Value: 7, 194 PeriodSeconds: 1801, 195 }}, 196 }, 197 }, 198 msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800", 199 }, { 200 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 201 ScaleUp: &autoscaling.HPAScalingRules{ 202 SelectPolicy: &incorrectPolicy, 203 Policies: []autoscaling.HPAScalingPolicy{{ 204 Type: autoscaling.PodsScalingPolicy, 205 Value: 7, 206 PeriodSeconds: 8, 207 }}, 208 }, 209 }, 210 msg: `spec.behavior.scaleUp.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`, 211 }, { 212 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 213 ScaleUp: &autoscaling.HPAScalingRules{ 214 Policies: []autoscaling.HPAScalingPolicy{{ 215 Type: autoscaling.HPAScalingPolicyType("hm"), 216 Value: 7, 217 PeriodSeconds: 8, 218 }}, 219 }, 220 }, 221 msg: `spec.behavior.scaleUp.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`, 222 }, { 223 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 224 ScaleUp: &autoscaling.HPAScalingRules{ 225 Policies: []autoscaling.HPAScalingPolicy{{ 226 Type: autoscaling.PodsScalingPolicy, 227 Value: 8, 228 }}, 229 }, 230 }, 231 msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 0: must be greater than zero", 232 }, { 233 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 234 ScaleUp: &autoscaling.HPAScalingRules{ 235 Policies: []autoscaling.HPAScalingPolicy{{ 236 Type: autoscaling.PodsScalingPolicy, 237 PeriodSeconds: 8, 238 }}, 239 }, 240 }, 241 msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: 0: must be greater than zero", 242 }, { 243 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 244 ScaleUp: &autoscaling.HPAScalingRules{ 245 Policies: []autoscaling.HPAScalingPolicy{{ 246 Type: autoscaling.PodsScalingPolicy, 247 PeriodSeconds: -1, 248 Value: 1, 249 }}, 250 }, 251 }, 252 msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: -1: must be greater than zero", 253 }, { 254 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 255 ScaleUp: &autoscaling.HPAScalingRules{ 256 Policies: []autoscaling.HPAScalingPolicy{{ 257 Type: autoscaling.PodsScalingPolicy, 258 PeriodSeconds: 1, 259 Value: -1, 260 }}, 261 }, 262 }, 263 msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: -1: must be greater than zero", 264 }, { 265 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 266 ScaleDown: &autoscaling.HPAScalingRules{ 267 SelectPolicy: &minPolicy, 268 }, 269 }, 270 msg: "spec.behavior.scaleDown.policies: Required value: must specify at least one Policy", 271 }, { 272 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 273 ScaleDown: &autoscaling.HPAScalingRules{ 274 StabilizationWindowSeconds: utilpointer.Int32(3601), 275 SelectPolicy: &minPolicy, 276 Policies: simplePoliciesList, 277 }, 278 }, 279 msg: "spec.behavior.scaleDown.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600", 280 }, { 281 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 282 ScaleDown: &autoscaling.HPAScalingRules{ 283 Policies: []autoscaling.HPAScalingPolicy{{ 284 Type: autoscaling.PercentScalingPolicy, 285 Value: 7, 286 PeriodSeconds: 1801, 287 }}, 288 }, 289 }, 290 msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800", 291 }, { 292 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 293 ScaleDown: &autoscaling.HPAScalingRules{ 294 SelectPolicy: &incorrectPolicy, 295 Policies: []autoscaling.HPAScalingPolicy{{ 296 Type: autoscaling.PodsScalingPolicy, 297 Value: 7, 298 PeriodSeconds: 8, 299 }}, 300 }, 301 }, 302 msg: `spec.behavior.scaleDown.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`, 303 }, { 304 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 305 ScaleDown: &autoscaling.HPAScalingRules{ 306 Policies: []autoscaling.HPAScalingPolicy{{ 307 Type: autoscaling.HPAScalingPolicyType("hm"), 308 Value: 7, 309 PeriodSeconds: 8, 310 }}, 311 }, 312 }, 313 msg: `spec.behavior.scaleDown.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`, 314 }, { 315 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 316 ScaleDown: &autoscaling.HPAScalingRules{ 317 Policies: []autoscaling.HPAScalingPolicy{{ 318 Type: autoscaling.PodsScalingPolicy, 319 Value: 8, 320 }}, 321 }, 322 }, 323 msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 0: must be greater than zero", 324 }, { 325 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 326 ScaleDown: &autoscaling.HPAScalingRules{ 327 Policies: []autoscaling.HPAScalingPolicy{{ 328 Type: autoscaling.PodsScalingPolicy, 329 PeriodSeconds: 8, 330 }}, 331 }, 332 }, 333 msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: 0: must be greater than zero", 334 }, { 335 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 336 ScaleDown: &autoscaling.HPAScalingRules{ 337 Policies: []autoscaling.HPAScalingPolicy{{ 338 Type: autoscaling.PodsScalingPolicy, 339 PeriodSeconds: -1, 340 Value: 1, 341 }}, 342 }, 343 }, 344 msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: -1: must be greater than zero", 345 }, { 346 behavior: autoscaling.HorizontalPodAutoscalerBehavior{ 347 ScaleDown: &autoscaling.HPAScalingRules{ 348 Policies: []autoscaling.HPAScalingPolicy{{ 349 Type: autoscaling.PodsScalingPolicy, 350 PeriodSeconds: 1, 351 Value: -1, 352 }}, 353 }, 354 }, 355 msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: -1: must be greater than zero", 356 }} 357 for _, c := range errorCases { 358 hpa := prepareHPAWithBehavior(c.behavior) 359 if errs := ValidateHorizontalPodAutoscaler(&hpa); len(errs) == 0 { 360 t.Errorf("expected failure for %s", c.msg) 361 } else if !strings.Contains(errs[0].Error(), c.msg) { 362 t.Errorf("unexpected error: %v, expected: %s", errs[0], c.msg) 363 } 364 } 365 } 366 367 func prepareHPAWithBehavior(b autoscaling.HorizontalPodAutoscalerBehavior) autoscaling.HorizontalPodAutoscaler { 368 return autoscaling.HorizontalPodAutoscaler{ 369 ObjectMeta: metav1.ObjectMeta{ 370 Name: "myautoscaler", 371 Namespace: metav1.NamespaceDefault, 372 }, 373 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 374 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 375 Kind: "ReplicationController", 376 Name: "myrc", 377 }, 378 MinReplicas: utilpointer.Int32(1), 379 MaxReplicas: 5, 380 Metrics: []autoscaling.MetricSpec{{ 381 Type: autoscaling.ResourceMetricSourceType, 382 Resource: &autoscaling.ResourceMetricSource{ 383 Name: api.ResourceCPU, 384 Target: autoscaling.MetricTarget{ 385 Type: autoscaling.UtilizationMetricType, 386 AverageUtilization: utilpointer.Int32(70), 387 }, 388 }, 389 }}, 390 Behavior: &b, 391 }, 392 } 393 } 394 395 func TestValidateHorizontalPodAutoscaler(t *testing.T) { 396 metricLabelSelector, err := metav1.ParseToLabelSelector("label=value") 397 if err != nil { 398 t.Errorf("unable to parse label selector: %v", err) 399 } 400 401 successCases := []autoscaling.HorizontalPodAutoscaler{{ 402 ObjectMeta: metav1.ObjectMeta{ 403 Name: "myautoscaler", 404 Namespace: metav1.NamespaceDefault, 405 }, 406 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 407 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 408 Kind: "ReplicationController", 409 Name: "myrc", 410 }, 411 MinReplicas: utilpointer.Int32(1), 412 MaxReplicas: 5, 413 Metrics: []autoscaling.MetricSpec{{ 414 Type: autoscaling.ResourceMetricSourceType, 415 Resource: &autoscaling.ResourceMetricSource{ 416 Name: api.ResourceCPU, 417 Target: autoscaling.MetricTarget{ 418 Type: autoscaling.UtilizationMetricType, 419 AverageUtilization: utilpointer.Int32(70), 420 }, 421 }, 422 }}, 423 }, 424 }, { 425 ObjectMeta: metav1.ObjectMeta{ 426 Name: "myautoscaler", 427 Namespace: metav1.NamespaceDefault, 428 }, 429 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 430 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 431 Kind: "ReplicationController", 432 Name: "myrc", 433 }, 434 MinReplicas: utilpointer.Int32(1), 435 MaxReplicas: 5, 436 }, 437 }, { 438 ObjectMeta: metav1.ObjectMeta{ 439 Name: "myautoscaler", 440 Namespace: metav1.NamespaceDefault, 441 }, 442 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 443 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 444 Kind: "ReplicationController", 445 Name: "myrc", 446 }, 447 MinReplicas: utilpointer.Int32(1), 448 MaxReplicas: 5, 449 Metrics: []autoscaling.MetricSpec{{ 450 Type: autoscaling.ResourceMetricSourceType, 451 Resource: &autoscaling.ResourceMetricSource{ 452 Name: api.ResourceCPU, 453 Target: autoscaling.MetricTarget{ 454 Type: autoscaling.AverageValueMetricType, 455 AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), 456 }, 457 }, 458 }}, 459 }, 460 }, { 461 ObjectMeta: metav1.ObjectMeta{ 462 Name: "myautoscaler", 463 Namespace: metav1.NamespaceDefault, 464 }, 465 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 466 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 467 Kind: "ReplicationController", 468 Name: "myrc", 469 }, 470 MinReplicas: utilpointer.Int32(1), 471 MaxReplicas: 5, 472 Metrics: []autoscaling.MetricSpec{{ 473 Type: autoscaling.PodsMetricSourceType, 474 Pods: &autoscaling.PodsMetricSource{ 475 Metric: autoscaling.MetricIdentifier{ 476 Name: "somemetric", 477 }, 478 Target: autoscaling.MetricTarget{ 479 Type: autoscaling.AverageValueMetricType, 480 AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), 481 }, 482 }, 483 }}, 484 }, 485 }, { 486 ObjectMeta: metav1.ObjectMeta{ 487 Name: "myautoscaler", 488 Namespace: metav1.NamespaceDefault, 489 }, 490 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 491 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 492 Kind: "ReplicationController", 493 Name: "myrc", 494 }, 495 MinReplicas: utilpointer.Int32(1), 496 MaxReplicas: 5, 497 Metrics: []autoscaling.MetricSpec{{ 498 Type: autoscaling.ContainerResourceMetricSourceType, 499 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 500 Name: api.ResourceCPU, 501 Container: "test-container", 502 Target: autoscaling.MetricTarget{ 503 Type: autoscaling.UtilizationMetricType, 504 AverageUtilization: utilpointer.Int32(70), 505 }, 506 }, 507 }}, 508 }, 509 }, { 510 ObjectMeta: metav1.ObjectMeta{ 511 Name: "myautoscaler", 512 Namespace: metav1.NamespaceDefault, 513 }, 514 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 515 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 516 Kind: "ReplicationController", 517 Name: "myrc", 518 }, 519 MinReplicas: utilpointer.Int32(1), 520 MaxReplicas: 5, 521 Metrics: []autoscaling.MetricSpec{{ 522 Type: autoscaling.ContainerResourceMetricSourceType, 523 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 524 Name: api.ResourceCPU, 525 Container: "test-container", 526 Target: autoscaling.MetricTarget{ 527 Type: autoscaling.AverageValueMetricType, 528 AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), 529 }, 530 }, 531 }}, 532 }, 533 }, { 534 ObjectMeta: metav1.ObjectMeta{ 535 Name: "myautoscaler", 536 Namespace: metav1.NamespaceDefault, 537 }, 538 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 539 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 540 Kind: "ReplicationController", 541 Name: "myrc", 542 }, 543 MinReplicas: utilpointer.Int32(1), 544 MaxReplicas: 5, 545 Metrics: []autoscaling.MetricSpec{{ 546 Type: autoscaling.ObjectMetricSourceType, 547 Object: &autoscaling.ObjectMetricSource{ 548 DescribedObject: autoscaling.CrossVersionObjectReference{ 549 Kind: "ReplicationController", 550 Name: "myrc", 551 }, 552 Metric: autoscaling.MetricIdentifier{ 553 Name: "somemetric", 554 }, 555 Target: autoscaling.MetricTarget{ 556 Type: autoscaling.ValueMetricType, 557 Value: resource.NewMilliQuantity(300, resource.DecimalSI), 558 }, 559 }, 560 }}, 561 }, 562 }, { 563 ObjectMeta: metav1.ObjectMeta{ 564 Name: "myautoscaler", 565 Namespace: metav1.NamespaceDefault, 566 }, 567 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 568 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 569 Kind: "ReplicationController", 570 Name: "myrc", 571 }, 572 MinReplicas: utilpointer.Int32(1), 573 MaxReplicas: 5, 574 Metrics: []autoscaling.MetricSpec{{ 575 Type: autoscaling.ExternalMetricSourceType, 576 External: &autoscaling.ExternalMetricSource{ 577 Metric: autoscaling.MetricIdentifier{ 578 Name: "somemetric", 579 Selector: metricLabelSelector, 580 }, 581 Target: autoscaling.MetricTarget{ 582 Type: autoscaling.ValueMetricType, 583 Value: resource.NewMilliQuantity(300, resource.DecimalSI), 584 }, 585 }, 586 }}, 587 }, 588 }, { 589 ObjectMeta: metav1.ObjectMeta{ 590 Name: "myautoscaler", 591 Namespace: metav1.NamespaceDefault, 592 }, 593 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 594 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 595 Kind: "ReplicationController", 596 Name: "myrc", 597 }, 598 MinReplicas: utilpointer.Int32(1), 599 MaxReplicas: 5, 600 Metrics: []autoscaling.MetricSpec{{ 601 Type: autoscaling.ExternalMetricSourceType, 602 External: &autoscaling.ExternalMetricSource{ 603 Metric: autoscaling.MetricIdentifier{ 604 Name: "somemetric", 605 Selector: metricLabelSelector, 606 }, 607 Target: autoscaling.MetricTarget{ 608 Type: autoscaling.AverageValueMetricType, 609 AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), 610 }, 611 }, 612 }}, 613 }, 614 }} 615 for _, successCase := range successCases { 616 if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 { 617 t.Errorf("expected success: %v", errs) 618 } 619 } 620 621 errorCases := []struct { 622 horizontalPodAutoscaler autoscaling.HorizontalPodAutoscaler 623 msg string 624 }{{ 625 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 626 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 627 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 628 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc"}, 629 MinReplicas: utilpointer.Int32(1), 630 MaxReplicas: 5, 631 Metrics: []autoscaling.MetricSpec{{ 632 Type: autoscaling.ResourceMetricSourceType, 633 Resource: &autoscaling.ResourceMetricSource{ 634 Name: api.ResourceCPU, 635 Target: autoscaling.MetricTarget{ 636 Type: autoscaling.UtilizationMetricType, 637 AverageUtilization: utilpointer.Int32(70), 638 }, 639 }, 640 }}, 641 }, 642 }, 643 msg: "scaleTargetRef.kind: Required", 644 }, { 645 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 646 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 647 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 648 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc"}, 649 MinReplicas: utilpointer.Int32(1), 650 MaxReplicas: 5, 651 Metrics: []autoscaling.MetricSpec{{ 652 Type: autoscaling.ContainerResourceMetricSourceType, 653 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 654 Name: api.ResourceCPU, 655 Container: "test-application", 656 Target: autoscaling.MetricTarget{ 657 Type: autoscaling.UtilizationMetricType, 658 AverageUtilization: utilpointer.Int32(70), 659 }, 660 }, 661 }}, 662 }, 663 }, 664 msg: "scaleTargetRef.kind: Required", 665 }, { 666 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 667 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 668 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 669 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "..", Name: "myrc"}, 670 MinReplicas: utilpointer.Int32(1), 671 MaxReplicas: 5, 672 Metrics: []autoscaling.MetricSpec{{ 673 Type: autoscaling.ResourceMetricSourceType, 674 Resource: &autoscaling.ResourceMetricSource{ 675 Name: api.ResourceCPU, 676 Target: autoscaling.MetricTarget{ 677 Type: autoscaling.UtilizationMetricType, 678 AverageUtilization: utilpointer.Int32(70), 679 }, 680 }, 681 }}, 682 }, 683 }, 684 msg: "scaleTargetRef.kind: Invalid", 685 }, { 686 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 687 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 688 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 689 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "..", Name: "myrc"}, 690 MinReplicas: utilpointer.Int32(1), 691 MaxReplicas: 5, 692 Metrics: []autoscaling.MetricSpec{{ 693 Type: autoscaling.ContainerResourceMetricSourceType, 694 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 695 Name: api.ResourceCPU, 696 Container: "test-application", 697 Target: autoscaling.MetricTarget{ 698 Type: autoscaling.UtilizationMetricType, 699 AverageUtilization: utilpointer.Int32(70), 700 }, 701 }, 702 }}, 703 }, 704 }, 705 msg: "scaleTargetRef.kind: Invalid", 706 }, { 707 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 708 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 709 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 710 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController"}, 711 MinReplicas: utilpointer.Int32(1), 712 MaxReplicas: 5, 713 Metrics: []autoscaling.MetricSpec{{ 714 Type: autoscaling.ResourceMetricSourceType, 715 Resource: &autoscaling.ResourceMetricSource{ 716 Name: api.ResourceCPU, 717 Target: autoscaling.MetricTarget{ 718 Type: autoscaling.UtilizationMetricType, 719 AverageUtilization: utilpointer.Int32(70), 720 }, 721 }, 722 }}, 723 }, 724 }, 725 msg: "scaleTargetRef.name: Required", 726 }, { 727 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 728 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 729 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 730 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController"}, 731 MinReplicas: utilpointer.Int32(1), 732 MaxReplicas: 5, 733 Metrics: []autoscaling.MetricSpec{{ 734 Type: autoscaling.ContainerResourceMetricSourceType, 735 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 736 Name: api.ResourceCPU, 737 Container: "test-application", 738 Target: autoscaling.MetricTarget{ 739 Type: autoscaling.UtilizationMetricType, 740 AverageUtilization: utilpointer.Int32(70), 741 }, 742 }, 743 }}, 744 }, 745 }, 746 msg: "scaleTargetRef.name: Required", 747 }, { 748 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 749 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 750 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 751 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController", Name: ".."}, 752 MinReplicas: utilpointer.Int32(1), 753 MaxReplicas: 5, 754 Metrics: []autoscaling.MetricSpec{{ 755 Type: autoscaling.ResourceMetricSourceType, 756 Resource: &autoscaling.ResourceMetricSource{ 757 Name: api.ResourceCPU, 758 Target: autoscaling.MetricTarget{ 759 Type: autoscaling.UtilizationMetricType, 760 AverageUtilization: utilpointer.Int32(70), 761 }, 762 }, 763 }}, 764 }, 765 }, 766 msg: "scaleTargetRef.name: Invalid", 767 }, { 768 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 769 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 770 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 771 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController", Name: ".."}, 772 MinReplicas: utilpointer.Int32(1), 773 MaxReplicas: 5, 774 Metrics: []autoscaling.MetricSpec{{ 775 Type: autoscaling.ContainerResourceMetricSourceType, 776 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 777 Name: api.ResourceCPU, 778 Container: "test-application", 779 Target: autoscaling.MetricTarget{ 780 Type: autoscaling.UtilizationMetricType, 781 AverageUtilization: utilpointer.Int32(70), 782 }, 783 }, 784 }}, 785 }, 786 }, 787 msg: "scaleTargetRef.name: Invalid", 788 }, { 789 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 790 ObjectMeta: metav1.ObjectMeta{ 791 Name: "myautoscaler", 792 Namespace: metav1.NamespaceDefault, 793 }, 794 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 795 ScaleTargetRef: autoscaling.CrossVersionObjectReference{}, 796 MinReplicas: utilpointer.Int32(-1), 797 MaxReplicas: 5, 798 }, 799 }, 800 msg: "must be greater than or equal to 1", 801 }, { 802 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 803 ObjectMeta: metav1.ObjectMeta{ 804 Name: "myautoscaler", 805 Namespace: metav1.NamespaceDefault, 806 }, 807 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 808 ScaleTargetRef: autoscaling.CrossVersionObjectReference{}, 809 MinReplicas: utilpointer.Int32(7), 810 MaxReplicas: 5, 811 }, 812 }, 813 msg: "must be greater than or equal to `minReplicas`", 814 }, { 815 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 816 ObjectMeta: metav1.ObjectMeta{ 817 Name: "myautoscaler", 818 Namespace: metav1.NamespaceDefault, 819 }, 820 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 821 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 822 MinReplicas: utilpointer.Int32(1), 823 MaxReplicas: 5, 824 Metrics: []autoscaling.MetricSpec{{ 825 Type: autoscaling.ResourceMetricSourceType, 826 Resource: &autoscaling.ResourceMetricSource{ 827 Name: api.ResourceCPU, 828 Target: autoscaling.MetricTarget{ 829 Type: autoscaling.UtilizationMetricType, 830 AverageUtilization: utilpointer.Int32(70), 831 AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), 832 }, 833 }, 834 }}, 835 }, 836 }, 837 msg: "may not set both a target raw value and a target utilization", 838 }, { 839 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 840 ObjectMeta: metav1.ObjectMeta{ 841 Name: "myautoscaler", 842 Namespace: metav1.NamespaceDefault, 843 }, 844 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 845 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 846 MinReplicas: utilpointer.Int32(1), 847 MaxReplicas: 5, 848 Metrics: []autoscaling.MetricSpec{{ 849 Type: autoscaling.ContainerResourceMetricSourceType, 850 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 851 Name: api.ResourceCPU, 852 Container: "test-application", 853 Target: autoscaling.MetricTarget{ 854 Type: autoscaling.UtilizationMetricType, 855 AverageUtilization: utilpointer.Int32(70), 856 AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), 857 }, 858 }, 859 }}, 860 }, 861 }, 862 msg: "may not set both a target raw value and a target utilization", 863 }, { 864 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 865 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 866 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 867 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 868 MinReplicas: utilpointer.Int32(1), 869 MaxReplicas: 5, 870 Metrics: []autoscaling.MetricSpec{{ 871 Type: autoscaling.ResourceMetricSourceType, 872 Resource: &autoscaling.ResourceMetricSource{ 873 Target: autoscaling.MetricTarget{ 874 Type: autoscaling.UtilizationMetricType, 875 AverageUtilization: utilpointer.Int32(70), 876 }, 877 }, 878 }}, 879 }, 880 }, 881 msg: "must specify a resource name", 882 }, { 883 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 884 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 885 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 886 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 887 MinReplicas: utilpointer.Int32(1), 888 MaxReplicas: 5, 889 Metrics: []autoscaling.MetricSpec{{ 890 Type: autoscaling.ContainerResourceMetricSourceType, 891 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 892 Container: "test-application", 893 Target: autoscaling.MetricTarget{ 894 Type: autoscaling.UtilizationMetricType, 895 AverageUtilization: utilpointer.Int32(70), 896 }, 897 }, 898 }}, 899 }, 900 }, 901 msg: "must specify a resource name", 902 }, { 903 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 904 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 905 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 906 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 907 MinReplicas: utilpointer.Int32(1), 908 MaxReplicas: 5, 909 Metrics: []autoscaling.MetricSpec{{ 910 Type: autoscaling.ContainerResourceMetricSourceType, 911 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 912 Name: "InvalidResource", 913 Container: "test-application", 914 Target: autoscaling.MetricTarget{ 915 Type: autoscaling.UtilizationMetricType, 916 AverageUtilization: utilpointer.Int32(70), 917 }, 918 }, 919 }}, 920 }, 921 }, 922 msg: "Invalid value: InvalidResource: must be a standard resource type or fully qualified", 923 }, { 924 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 925 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 926 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 927 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 928 MinReplicas: utilpointer.Int32(1), 929 MaxReplicas: 5, 930 Metrics: []autoscaling.MetricSpec{{ 931 Type: autoscaling.ResourceMetricSourceType, 932 Resource: &autoscaling.ResourceMetricSource{ 933 Name: api.ResourceCPU, 934 Target: autoscaling.MetricTarget{ 935 Type: autoscaling.UtilizationMetricType, 936 AverageUtilization: utilpointer.Int32(-10), 937 }, 938 }, 939 }}, 940 }, 941 }, 942 msg: "must be greater than 0", 943 }, { 944 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 945 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 946 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 947 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 948 MinReplicas: utilpointer.Int32(1), 949 MaxReplicas: 5, 950 Metrics: []autoscaling.MetricSpec{{ 951 Type: autoscaling.ContainerResourceMetricSourceType, 952 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 953 Name: api.ResourceCPU, 954 Container: "test-application", 955 Target: autoscaling.MetricTarget{ 956 Type: autoscaling.UtilizationMetricType, 957 AverageUtilization: utilpointer.Int32(-10), 958 }, 959 }, 960 }}, 961 }, 962 }, 963 msg: "must be greater than 0", 964 }, { 965 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 966 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 967 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 968 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 969 MinReplicas: utilpointer.Int32(1), 970 MaxReplicas: 5, 971 Metrics: []autoscaling.MetricSpec{{ 972 Type: autoscaling.ContainerResourceMetricSourceType, 973 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 974 Name: api.ResourceCPU, 975 Target: autoscaling.MetricTarget{ 976 Type: autoscaling.UtilizationMetricType, 977 AverageUtilization: utilpointer.Int32(-10), 978 }, 979 }, 980 }}, 981 }, 982 }, 983 msg: "must specify a container", 984 }, { 985 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 986 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 987 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 988 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 989 MinReplicas: utilpointer.Int32(1), 990 MaxReplicas: 5, 991 Metrics: []autoscaling.MetricSpec{{ 992 Type: autoscaling.ContainerResourceMetricSourceType, 993 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 994 Name: api.ResourceCPU, 995 Container: "---***", 996 Target: autoscaling.MetricTarget{ 997 Type: autoscaling.UtilizationMetricType, 998 AverageUtilization: utilpointer.Int32(-10), 999 }, 1000 }, 1001 }}, 1002 }, 1003 }, 1004 msg: "Invalid value: \"---***\"", 1005 }, { 1006 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1007 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1008 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1009 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1010 MinReplicas: utilpointer.Int32(1), 1011 MaxReplicas: 5, 1012 Metrics: []autoscaling.MetricSpec{{ 1013 Type: autoscaling.ResourceMetricSourceType, 1014 Resource: &autoscaling.ResourceMetricSource{ 1015 Name: api.ResourceCPU, 1016 Target: autoscaling.MetricTarget{ 1017 Type: autoscaling.ValueMetricType, 1018 }, 1019 }, 1020 }}, 1021 }, 1022 }, 1023 msg: "must set either a target raw value or a target utilization", 1024 }, { 1025 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1026 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1027 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1028 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1029 MinReplicas: utilpointer.Int32(1), 1030 MaxReplicas: 5, 1031 Metrics: []autoscaling.MetricSpec{{ 1032 Type: autoscaling.ContainerResourceMetricSourceType, 1033 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 1034 Name: api.ResourceCPU, 1035 Container: "test-application", 1036 Target: autoscaling.MetricTarget{ 1037 Type: autoscaling.ValueMetricType, 1038 }, 1039 }, 1040 }}, 1041 }, 1042 }, 1043 msg: "must set either a target raw value or a target utilization", 1044 }, { 1045 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1046 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1047 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1048 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1049 MinReplicas: utilpointer.Int32(1), 1050 MaxReplicas: 5, 1051 Metrics: []autoscaling.MetricSpec{{ 1052 Type: autoscaling.PodsMetricSourceType, 1053 Pods: &autoscaling.PodsMetricSource{ 1054 Metric: autoscaling.MetricIdentifier{}, 1055 Target: autoscaling.MetricTarget{ 1056 Type: autoscaling.ValueMetricType, 1057 AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), 1058 }, 1059 }, 1060 }}, 1061 }, 1062 }, 1063 msg: "must specify a metric name", 1064 }, { 1065 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1066 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1067 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1068 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1069 MinReplicas: utilpointer.Int32(1), 1070 MaxReplicas: 5, 1071 Metrics: []autoscaling.MetricSpec{{ 1072 Type: autoscaling.PodsMetricSourceType, 1073 Pods: &autoscaling.PodsMetricSource{ 1074 Metric: autoscaling.MetricIdentifier{ 1075 Name: "somemetric", 1076 }, 1077 Target: autoscaling.MetricTarget{ 1078 Type: autoscaling.ValueMetricType, 1079 }, 1080 }, 1081 }}, 1082 }, 1083 }, 1084 msg: "must specify a positive target averageValue", 1085 }, { 1086 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1087 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1088 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1089 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1090 MinReplicas: utilpointer.Int32(1), 1091 MaxReplicas: 5, 1092 Metrics: []autoscaling.MetricSpec{{ 1093 Type: autoscaling.ObjectMetricSourceType, 1094 Object: &autoscaling.ObjectMetricSource{ 1095 DescribedObject: autoscaling.CrossVersionObjectReference{ 1096 Kind: "ReplicationController", 1097 Name: "myrc", 1098 }, 1099 Metric: autoscaling.MetricIdentifier{ 1100 Name: "somemetric", 1101 }, 1102 Target: autoscaling.MetricTarget{ 1103 Type: autoscaling.ValueMetricType, 1104 }, 1105 }, 1106 }}, 1107 }, 1108 }, 1109 msg: "must set either a target value or averageValue", 1110 }, { 1111 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1112 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1113 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1114 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1115 MinReplicas: utilpointer.Int32(1), 1116 MaxReplicas: 5, 1117 Metrics: []autoscaling.MetricSpec{{ 1118 Type: autoscaling.ObjectMetricSourceType, 1119 Object: &autoscaling.ObjectMetricSource{ 1120 DescribedObject: autoscaling.CrossVersionObjectReference{ 1121 Name: "myrc", 1122 }, 1123 Metric: autoscaling.MetricIdentifier{ 1124 Name: "somemetric", 1125 }, 1126 Target: autoscaling.MetricTarget{ 1127 Type: autoscaling.ValueMetricType, 1128 Value: resource.NewMilliQuantity(100, resource.DecimalSI), 1129 }, 1130 }, 1131 }}, 1132 }, 1133 }, 1134 msg: "object.describedObject.kind: Required", 1135 }, { 1136 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1137 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1138 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1139 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1140 MinReplicas: utilpointer.Int32(1), 1141 MaxReplicas: 5, 1142 Metrics: []autoscaling.MetricSpec{{ 1143 Type: autoscaling.ObjectMetricSourceType, 1144 Object: &autoscaling.ObjectMetricSource{ 1145 DescribedObject: autoscaling.CrossVersionObjectReference{ 1146 Kind: "ReplicationController", 1147 Name: "myrc", 1148 }, 1149 Target: autoscaling.MetricTarget{ 1150 Type: autoscaling.ValueMetricType, 1151 Value: resource.NewMilliQuantity(100, resource.DecimalSI), 1152 }, 1153 }, 1154 }}, 1155 }, 1156 }, 1157 msg: "must specify a metric name", 1158 }, { 1159 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1160 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1161 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1162 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1163 MinReplicas: utilpointer.Int32(1), 1164 MaxReplicas: 5, 1165 Metrics: []autoscaling.MetricSpec{{ 1166 Type: autoscaling.ExternalMetricSourceType, 1167 External: &autoscaling.ExternalMetricSource{ 1168 Metric: autoscaling.MetricIdentifier{ 1169 Selector: metricLabelSelector, 1170 }, 1171 Target: autoscaling.MetricTarget{ 1172 Type: autoscaling.ValueMetricType, 1173 Value: resource.NewMilliQuantity(300, resource.DecimalSI), 1174 }, 1175 }, 1176 }}, 1177 }, 1178 }, 1179 msg: "must specify a metric name", 1180 }, { 1181 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1182 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1183 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1184 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1185 MinReplicas: utilpointer.Int32(1), 1186 MaxReplicas: 5, 1187 Metrics: []autoscaling.MetricSpec{{ 1188 Type: autoscaling.ExternalMetricSourceType, 1189 External: &autoscaling.ExternalMetricSource{ 1190 Metric: autoscaling.MetricIdentifier{ 1191 Name: "foo/../", 1192 Selector: metricLabelSelector, 1193 }, 1194 Target: autoscaling.MetricTarget{ 1195 Type: autoscaling.ValueMetricType, 1196 Value: resource.NewMilliQuantity(300, resource.DecimalSI), 1197 }, 1198 }, 1199 }}, 1200 }, 1201 }, 1202 msg: "'/'", 1203 }, 1204 1205 { 1206 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1207 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1208 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1209 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1210 MinReplicas: utilpointer.Int32(1), 1211 MaxReplicas: 5, 1212 Metrics: []autoscaling.MetricSpec{{ 1213 Type: autoscaling.ExternalMetricSourceType, 1214 External: &autoscaling.ExternalMetricSource{ 1215 Metric: autoscaling.MetricIdentifier{ 1216 Name: "somemetric", 1217 Selector: metricLabelSelector, 1218 }, 1219 Target: autoscaling.MetricTarget{ 1220 Type: autoscaling.ValueMetricType, 1221 }, 1222 }, 1223 }}, 1224 }, 1225 }, 1226 msg: "must set either a target value for metric or a per-pod target", 1227 }, { 1228 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1229 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1230 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1231 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1232 MinReplicas: utilpointer.Int32(1), 1233 MaxReplicas: 5, 1234 Metrics: []autoscaling.MetricSpec{{ 1235 Type: autoscaling.ExternalMetricSourceType, 1236 External: &autoscaling.ExternalMetricSource{ 1237 Metric: autoscaling.MetricIdentifier{ 1238 Name: "somemetric", 1239 Selector: metricLabelSelector, 1240 }, 1241 Target: autoscaling.MetricTarget{ 1242 Type: autoscaling.ValueMetricType, 1243 Value: resource.NewMilliQuantity(-300, resource.DecimalSI), 1244 }, 1245 }, 1246 }}, 1247 }, 1248 }, 1249 msg: "must be positive", 1250 }, { 1251 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1252 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1253 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1254 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1255 MinReplicas: utilpointer.Int32(1), 1256 MaxReplicas: 5, 1257 Metrics: []autoscaling.MetricSpec{{ 1258 Type: autoscaling.ExternalMetricSourceType, 1259 External: &autoscaling.ExternalMetricSource{ 1260 Metric: autoscaling.MetricIdentifier{ 1261 Name: "somemetric", 1262 Selector: metricLabelSelector, 1263 }, 1264 Target: autoscaling.MetricTarget{ 1265 Type: autoscaling.ValueMetricType, 1266 AverageValue: resource.NewMilliQuantity(-300, resource.DecimalSI), 1267 }, 1268 }, 1269 }}, 1270 }, 1271 }, 1272 msg: "must be positive", 1273 }, { 1274 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1275 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1276 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1277 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1278 MinReplicas: utilpointer.Int32(1), 1279 MaxReplicas: 5, 1280 Metrics: []autoscaling.MetricSpec{{ 1281 Type: autoscaling.ExternalMetricSourceType, 1282 External: &autoscaling.ExternalMetricSource{ 1283 Metric: autoscaling.MetricIdentifier{ 1284 Name: "somemetric", 1285 Selector: metricLabelSelector, 1286 }, 1287 Target: autoscaling.MetricTarget{ 1288 Type: autoscaling.ValueMetricType, 1289 Value: resource.NewMilliQuantity(300, resource.DecimalSI), 1290 AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), 1291 }, 1292 }, 1293 }}, 1294 }, 1295 }, 1296 msg: "may not set both a target value for metric and a per-pod target", 1297 }, { 1298 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1299 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1300 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1301 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1302 MinReplicas: utilpointer.Int32(1), 1303 MaxReplicas: 5, 1304 Metrics: []autoscaling.MetricSpec{{ 1305 Type: autoscaling.ExternalMetricSourceType, 1306 External: &autoscaling.ExternalMetricSource{ 1307 Metric: autoscaling.MetricIdentifier{ 1308 Name: "somemetric", 1309 Selector: metricLabelSelector, 1310 }, 1311 Target: autoscaling.MetricTarget{ 1312 Type: "boogity", 1313 Value: resource.NewMilliQuantity(300, resource.DecimalSI), 1314 }, 1315 }, 1316 }}, 1317 }, 1318 }, 1319 msg: "must be either Utilization, Value, or AverageValue", 1320 }, { 1321 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1322 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1323 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1324 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1325 MinReplicas: utilpointer.Int32(1), 1326 MaxReplicas: 5, 1327 Metrics: []autoscaling.MetricSpec{{ 1328 Type: autoscaling.ExternalMetricSourceType, 1329 External: &autoscaling.ExternalMetricSource{ 1330 Metric: autoscaling.MetricIdentifier{ 1331 Name: "somemetric", 1332 Selector: metricLabelSelector, 1333 }, 1334 Target: autoscaling.MetricTarget{ 1335 Value: resource.NewMilliQuantity(300, resource.DecimalSI), 1336 }, 1337 }, 1338 }}, 1339 }, 1340 }, 1341 msg: "must specify a metric target type", 1342 }, { 1343 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1344 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1345 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1346 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1347 MinReplicas: utilpointer.Int32(1), 1348 MaxReplicas: 5, 1349 Metrics: []autoscaling.MetricSpec{{ 1350 Type: autoscaling.ExternalMetricSourceType, 1351 External: &autoscaling.ExternalMetricSource{ 1352 Metric: autoscaling.MetricIdentifier{ 1353 Name: "somemetric", 1354 Selector: metricLabelSelector, 1355 }, 1356 }, 1357 }}, 1358 }, 1359 }, 1360 msg: "must specify a metric target", 1361 }, { 1362 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1363 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1364 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1365 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1366 MinReplicas: utilpointer.Int32(1), 1367 MaxReplicas: 5, 1368 Metrics: []autoscaling.MetricSpec{ 1369 {}, 1370 }, 1371 }, 1372 }, 1373 msg: "must specify a metric source type", 1374 }, { 1375 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1376 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1377 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1378 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1379 MinReplicas: utilpointer.Int32(1), 1380 MaxReplicas: 5, 1381 Metrics: []autoscaling.MetricSpec{{ 1382 Type: autoscaling.MetricSourceType("InvalidType"), 1383 }}, 1384 }, 1385 }, 1386 msg: "type: Unsupported value", 1387 }, { 1388 horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ 1389 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1390 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1391 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1392 MinReplicas: utilpointer.Int32(1), 1393 MaxReplicas: 5, 1394 Metrics: []autoscaling.MetricSpec{{ 1395 Type: autoscaling.ResourceMetricSourceType, 1396 Resource: &autoscaling.ResourceMetricSource{ 1397 Name: api.ResourceCPU, 1398 Target: autoscaling.MetricTarget{ 1399 Type: autoscaling.AverageValueMetricType, 1400 AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), 1401 }, 1402 }, 1403 Pods: &autoscaling.PodsMetricSource{ 1404 Metric: autoscaling.MetricIdentifier{ 1405 Name: "somemetric", 1406 }, 1407 Target: autoscaling.MetricTarget{ 1408 Type: autoscaling.AverageValueMetricType, 1409 AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), 1410 }, 1411 }, 1412 }}, 1413 }, 1414 }, 1415 msg: "must populate the given metric source only", 1416 }, 1417 } 1418 1419 for _, c := range errorCases { 1420 errs := ValidateHorizontalPodAutoscaler(&c.horizontalPodAutoscaler) 1421 if len(errs) == 0 { 1422 t.Errorf("expected failure for %q", c.msg) 1423 } else if !strings.Contains(errs[0].Error(), c.msg) { 1424 t.Errorf("unexpected error: %q, expected: %q", errs[0], c.msg) 1425 } 1426 } 1427 1428 sourceTypes := map[autoscaling.MetricSourceType]autoscaling.MetricSpec{ 1429 autoscaling.ResourceMetricSourceType: { 1430 Resource: &autoscaling.ResourceMetricSource{ 1431 Name: api.ResourceCPU, 1432 Target: autoscaling.MetricTarget{ 1433 Type: autoscaling.AverageValueMetricType, 1434 AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), 1435 }, 1436 }, 1437 }, 1438 autoscaling.ContainerResourceMetricSourceType: { 1439 ContainerResource: &autoscaling.ContainerResourceMetricSource{ 1440 Name: api.ResourceCPU, 1441 Container: "test-application", 1442 Target: autoscaling.MetricTarget{ 1443 Type: autoscaling.AverageValueMetricType, 1444 AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), 1445 }, 1446 }, 1447 }, 1448 autoscaling.PodsMetricSourceType: { 1449 Pods: &autoscaling.PodsMetricSource{ 1450 Metric: autoscaling.MetricIdentifier{ 1451 Name: "somemetric", 1452 }, 1453 Target: autoscaling.MetricTarget{ 1454 Type: autoscaling.AverageValueMetricType, 1455 AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), 1456 }, 1457 }, 1458 }, 1459 autoscaling.ObjectMetricSourceType: { 1460 Object: &autoscaling.ObjectMetricSource{ 1461 DescribedObject: autoscaling.CrossVersionObjectReference{ 1462 Kind: "ReplicationController", 1463 Name: "myrc", 1464 }, 1465 Metric: autoscaling.MetricIdentifier{ 1466 Name: "somemetric", 1467 }, 1468 Target: autoscaling.MetricTarget{ 1469 Type: autoscaling.ValueMetricType, 1470 Value: resource.NewMilliQuantity(100, resource.DecimalSI), 1471 }, 1472 }, 1473 }, 1474 } 1475 1476 for correctType, spec := range sourceTypes { 1477 for incorrectType := range sourceTypes { 1478 if correctType == incorrectType { 1479 continue 1480 } 1481 1482 spec.Type = incorrectType 1483 1484 errs := ValidateHorizontalPodAutoscaler(&autoscaling.HorizontalPodAutoscaler{ 1485 ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 1486 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1487 ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, 1488 MinReplicas: utilpointer.Int32(1), 1489 MaxReplicas: 5, Metrics: []autoscaling.MetricSpec{spec}, 1490 }, 1491 }) 1492 1493 expectedMsg := "must populate information for the given metric source" 1494 1495 if len(errs) == 0 { 1496 t.Errorf("expected failure with type of %v and spec for %v", incorrectType, correctType) 1497 } else if !strings.Contains(errs[0].Error(), expectedMsg) { 1498 t.Errorf("unexpected error: %q, expected %q", errs[0], expectedMsg) 1499 } 1500 } 1501 } 1502 } 1503 1504 func prepareMinReplicasCases(t *testing.T, minReplicas int32) []autoscaling.HorizontalPodAutoscaler { 1505 metricLabelSelector, err := metav1.ParseToLabelSelector("label=value") 1506 if err != nil { 1507 t.Errorf("unable to parse label selector: %v", err) 1508 } 1509 minReplicasCases := []autoscaling.HorizontalPodAutoscaler{{ 1510 ObjectMeta: metav1.ObjectMeta{ 1511 Name: "myautoscaler", 1512 Namespace: metav1.NamespaceDefault, 1513 ResourceVersion: "theversion", 1514 }, 1515 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1516 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 1517 Kind: "ReplicationController", 1518 Name: "myrc", 1519 }, 1520 MinReplicas: utilpointer.Int32(minReplicas), 1521 MaxReplicas: 5, 1522 Metrics: []autoscaling.MetricSpec{{ 1523 Type: autoscaling.ObjectMetricSourceType, 1524 Object: &autoscaling.ObjectMetricSource{ 1525 DescribedObject: autoscaling.CrossVersionObjectReference{ 1526 Kind: "ReplicationController", 1527 Name: "myrc", 1528 }, 1529 Metric: autoscaling.MetricIdentifier{ 1530 Name: "somemetric", 1531 }, 1532 Target: autoscaling.MetricTarget{ 1533 Type: autoscaling.ValueMetricType, 1534 Value: resource.NewMilliQuantity(300, resource.DecimalSI), 1535 }, 1536 }, 1537 }}, 1538 }, 1539 }, { 1540 ObjectMeta: metav1.ObjectMeta{ 1541 Name: "myautoscaler", 1542 Namespace: metav1.NamespaceDefault, 1543 ResourceVersion: "theversion", 1544 }, 1545 Spec: autoscaling.HorizontalPodAutoscalerSpec{ 1546 ScaleTargetRef: autoscaling.CrossVersionObjectReference{ 1547 Kind: "ReplicationController", 1548 Name: "myrc", 1549 }, 1550 MinReplicas: utilpointer.Int32(minReplicas), 1551 MaxReplicas: 5, 1552 Metrics: []autoscaling.MetricSpec{{ 1553 Type: autoscaling.ExternalMetricSourceType, 1554 External: &autoscaling.ExternalMetricSource{ 1555 Metric: autoscaling.MetricIdentifier{ 1556 Name: "somemetric", 1557 Selector: metricLabelSelector, 1558 }, 1559 Target: autoscaling.MetricTarget{ 1560 Type: autoscaling.AverageValueMetricType, 1561 AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), 1562 }, 1563 }, 1564 }}, 1565 }, 1566 }} 1567 return minReplicasCases 1568 } 1569 1570 func TestValidateHorizontalPodAutoscalerScaleToZeroEnabled(t *testing.T) { 1571 // Enable HPAScaleToZero feature gate. 1572 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, true)() 1573 1574 zeroMinReplicasCases := prepareMinReplicasCases(t, 0) 1575 for _, successCase := range zeroMinReplicasCases { 1576 if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 { 1577 t.Errorf("expected success: %v", errs) 1578 } 1579 } 1580 } 1581 1582 func TestValidateHorizontalPodAutoscalerScaleToZeroDisabled(t *testing.T) { 1583 // Disable HPAScaleToZero feature gate. 1584 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, false)() 1585 1586 zeroMinReplicasCases := prepareMinReplicasCases(t, 0) 1587 errorMsg := "must be greater than or equal to 1" 1588 1589 for _, errorCase := range zeroMinReplicasCases { 1590 errs := ValidateHorizontalPodAutoscaler(&errorCase) 1591 if len(errs) == 0 { 1592 t.Errorf("expected failure for %q", errorMsg) 1593 } else if !strings.Contains(errs[0].Error(), errorMsg) { 1594 t.Errorf("unexpected error: %q, expected: %q", errs[0], errorMsg) 1595 } 1596 } 1597 1598 nonZeroMinReplicasCases := prepareMinReplicasCases(t, 1) 1599 1600 for _, successCase := range nonZeroMinReplicasCases { 1601 successCase.Spec.MinReplicas = utilpointer.Int32(1) 1602 if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 { 1603 t.Errorf("expected success: %v", errs) 1604 } 1605 } 1606 } 1607 1608 func TestValidateHorizontalPodAutoscalerUpdateScaleToZeroEnabled(t *testing.T) { 1609 // Enable HPAScaleToZero feature gate. 1610 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, true)() 1611 1612 zeroMinReplicasCases := prepareMinReplicasCases(t, 0) 1613 nonZeroMinReplicasCases := prepareMinReplicasCases(t, 1) 1614 1615 for i, zeroCase := range zeroMinReplicasCases { 1616 nonZeroCase := nonZeroMinReplicasCases[i] 1617 1618 if errs := ValidateHorizontalPodAutoscalerUpdate(&nonZeroCase, &zeroCase); len(errs) != 0 { 1619 t.Errorf("expected success: %v", errs) 1620 } 1621 1622 if errs := ValidateHorizontalPodAutoscalerUpdate(&zeroCase, &nonZeroCase); len(errs) != 0 { 1623 t.Errorf("expected success: %v", errs) 1624 } 1625 } 1626 } 1627 1628 func TestValidateHorizontalPodAutoscalerScaleToZeroUpdateDisabled(t *testing.T) { 1629 // Disable HPAScaleToZero feature gate. 1630 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, false)() 1631 1632 zeroMinReplicasCases := prepareMinReplicasCases(t, 0) 1633 nonZeroMinReplicasCases := prepareMinReplicasCases(t, 1) 1634 errorMsg := "must be greater than or equal to 1" 1635 1636 for i, zeroCase := range zeroMinReplicasCases { 1637 nonZeroCase := nonZeroMinReplicasCases[i] 1638 errs := ValidateHorizontalPodAutoscalerUpdate(&zeroCase, &nonZeroCase) 1639 1640 if len(errs) == 0 { 1641 t.Errorf("expected failure for %q", errorMsg) 1642 } else if !strings.Contains(errs[0].Error(), errorMsg) { 1643 t.Errorf("unexpected error: %q, expected: %q", errs[0], errorMsg) 1644 } 1645 1646 if errs := ValidateHorizontalPodAutoscalerUpdate(&zeroCase, &zeroCase); len(errs) != 0 { 1647 t.Errorf("expected success: %v", errs) 1648 } 1649 1650 if errs := ValidateHorizontalPodAutoscalerUpdate(&nonZeroCase, &zeroCase); len(errs) != 0 { 1651 t.Errorf("expected success: %v", errs) 1652 } 1653 } 1654 }