mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
Merge pull request #106316 from josephburnett/controller-v2
Watch HPA v2 instead of v1.
This commit is contained in:
commit
6d1d8c73ee
@ -78,10 +78,10 @@ func startHPAControllerWithMetricsClient(ctx context.Context, controllerContext
|
||||
go podautoscaler.NewHorizontalController(
|
||||
hpaClient.CoreV1(),
|
||||
scaleClient,
|
||||
hpaClient.AutoscalingV1(),
|
||||
hpaClient.AutoscalingV2(),
|
||||
controllerContext.RESTMapper,
|
||||
metricsClient,
|
||||
controllerContext.InformerFactory.Autoscaling().V1().HorizontalPodAutoscalers(),
|
||||
controllerContext.InformerFactory.Autoscaling().V2().HorizontalPodAutoscalers(),
|
||||
controllerContext.InformerFactory.Core().V1().Pods(),
|
||||
controllerContext.ComponentConfig.HPAController.HorizontalPodAutoscalerSyncPeriod.Duration,
|
||||
controllerContext.ComponentConfig.HPAController.HorizontalPodAutoscalerDownscaleStabilizationWindow.Duration,
|
||||
|
@ -35,19 +35,18 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
autoscalinginformers "k8s.io/client-go/informers/autoscaling/v1"
|
||||
autoscalinginformers "k8s.io/client-go/informers/autoscaling/v2"
|
||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
autoscalingclient "k8s.io/client-go/kubernetes/typed/autoscaling/v1"
|
||||
autoscalingclient "k8s.io/client-go/kubernetes/typed/autoscaling/v2"
|
||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
autoscalinglisters "k8s.io/client-go/listers/autoscaling/v1"
|
||||
autoscalinglisters "k8s.io/client-go/listers/autoscaling/v2"
|
||||
corelisters "k8s.io/client-go/listers/core/v1"
|
||||
scaleclient "k8s.io/client-go/scale"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
metricsclient "k8s.io/kubernetes/pkg/controller/podautoscaler/metrics"
|
||||
)
|
||||
@ -571,16 +570,9 @@ func (a *HorizontalController) recordInitialRecommendation(currentReplicas int32
|
||||
}
|
||||
}
|
||||
|
||||
func (a *HorizontalController) reconcileAutoscaler(ctx context.Context, hpav1Shared *autoscalingv1.HorizontalPodAutoscaler, key string) error {
|
||||
func (a *HorizontalController) reconcileAutoscaler(ctx context.Context, hpaShared *autoscalingv2.HorizontalPodAutoscaler, key string) error {
|
||||
// make a copy so that we never mutate the shared informer cache (conversion can mutate the object)
|
||||
hpav1 := hpav1Shared.DeepCopy()
|
||||
// then, convert to autoscaling/v2, which makes our lives easier when calculating metrics
|
||||
hpaRaw, err := unsafeConvertToVersionVia(hpav1, autoscalingv2.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
a.eventRecorder.Event(hpav1, v1.EventTypeWarning, "FailedConvertHPA", err.Error())
|
||||
return fmt.Errorf("failed to convert the given HPA to %s: %v", autoscalingv2.SchemeGroupVersion.String(), err)
|
||||
}
|
||||
hpa := hpaRaw.(*autoscalingv2.HorizontalPodAutoscaler)
|
||||
hpa := hpaShared.DeepCopy()
|
||||
hpaStatusOriginal := hpa.Status.DeepCopy()
|
||||
|
||||
reference := fmt.Sprintf("%s/%s/%s", hpa.Spec.ScaleTargetRef.Kind, hpa.Namespace, hpa.Spec.ScaleTargetRef.Name)
|
||||
@ -1156,15 +1148,7 @@ func (a *HorizontalController) updateStatusIfNeeded(ctx context.Context, oldStat
|
||||
|
||||
// updateStatus actually does the update request for the status of the given HPA
|
||||
func (a *HorizontalController) updateStatus(ctx context.Context, hpa *autoscalingv2.HorizontalPodAutoscaler) error {
|
||||
// convert back to autoscalingv1
|
||||
hpaRaw, err := unsafeConvertToVersionVia(hpa, autoscalingv1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
a.eventRecorder.Event(hpa, v1.EventTypeWarning, "FailedConvertHPA", err.Error())
|
||||
return fmt.Errorf("failed to convert the given HPA to %s: %v", autoscalingv2.SchemeGroupVersion.String(), err)
|
||||
}
|
||||
hpav1 := hpaRaw.(*autoscalingv1.HorizontalPodAutoscaler)
|
||||
|
||||
_, err = a.hpaNamespacer.HorizontalPodAutoscalers(hpav1.Namespace).UpdateStatus(ctx, hpav1, metav1.UpdateOptions{})
|
||||
_, err := a.hpaNamespacer.HorizontalPodAutoscalers(hpa.Namespace).UpdateStatus(ctx, hpa, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
a.eventRecorder.Event(hpa, v1.EventTypeWarning, "FailedUpdateStatus", err.Error())
|
||||
return fmt.Errorf("failed to update status for %s: %v", hpa.Name, err)
|
||||
@ -1173,24 +1157,6 @@ func (a *HorizontalController) updateStatus(ctx context.Context, hpa *autoscalin
|
||||
return nil
|
||||
}
|
||||
|
||||
// unsafeConvertToVersionVia is like Scheme.UnsafeConvertToVersion, but it does so via an internal version first.
|
||||
// We use it since working with v2alpha1 is convenient here, but we want to use the v1 client (and
|
||||
// can't just use the internal version). Note that conversion mutates the object, so you need to deepcopy
|
||||
// *before* you call this if the input object came out of a shared cache.
|
||||
func unsafeConvertToVersionVia(obj runtime.Object, externalVersion schema.GroupVersion) (runtime.Object, error) {
|
||||
objInt, err := legacyscheme.Scheme.UnsafeConvertToVersion(obj, schema.GroupVersion{Group: externalVersion.Group, Version: runtime.APIVersionInternal})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert the given object to the internal version: %v", err)
|
||||
}
|
||||
|
||||
objExt, err := legacyscheme.Scheme.UnsafeConvertToVersion(objInt, externalVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert the given object back to the external version: %v", err)
|
||||
}
|
||||
|
||||
return objExt, err
|
||||
}
|
||||
|
||||
// setCondition sets the specific condition type on the given HPA to the specified value with the given reason
|
||||
// and message. The message and args are treated like a format string. The condition will be added if it is
|
||||
// not present.
|
||||
|
@ -18,7 +18,6 @@ package podautoscaler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
@ -40,7 +39,6 @@ import (
|
||||
scalefake "k8s.io/client-go/scale/fake"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
autoscalingapiv2 "k8s.io/kubernetes/pkg/apis/autoscaling/v2"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
"k8s.io/kubernetes/pkg/controller/podautoscaler/metrics"
|
||||
@ -74,7 +72,7 @@ var statusOk = []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
}
|
||||
|
||||
// statusOkWithOverrides returns the "ok" status with the given conditions as overridden
|
||||
func statusOkWithOverrides(overrides ...autoscalingv2.HorizontalPodAutoscalerCondition) []autoscalingv1.HorizontalPodAutoscalerCondition {
|
||||
func statusOkWithOverrides(overrides ...autoscalingv2.HorizontalPodAutoscalerCondition) []autoscalingv2.HorizontalPodAutoscalerCondition {
|
||||
resv2 := make([]autoscalingv2.HorizontalPodAutoscalerCondition, len(statusOk))
|
||||
copy(resv2, statusOk)
|
||||
for _, override := range overrides {
|
||||
@ -82,10 +80,10 @@ func statusOkWithOverrides(overrides ...autoscalingv2.HorizontalPodAutoscalerCon
|
||||
}
|
||||
|
||||
// copy to a v1 slice
|
||||
resv1 := make([]autoscalingv1.HorizontalPodAutoscalerCondition, len(resv2))
|
||||
resv1 := make([]autoscalingv2.HorizontalPodAutoscalerCondition, len(resv2))
|
||||
for i, cond := range resv2 {
|
||||
resv1[i] = autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
Type: autoscalingv1.HorizontalPodAutoscalerConditionType(cond.Type),
|
||||
resv1[i] = autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
Type: autoscalingv2.HorizontalPodAutoscalerConditionType(cond.Type),
|
||||
Status: cond.Status,
|
||||
Reason: cond.Reason,
|
||||
}
|
||||
@ -129,7 +127,7 @@ type testCase struct {
|
||||
useMetricsAPI bool
|
||||
metricsTarget []autoscalingv2.MetricSpec
|
||||
expectedDesiredReplicas int32
|
||||
expectedConditions []autoscalingv1.HorizontalPodAutoscalerCondition
|
||||
expectedConditions []autoscalingv2.HorizontalPodAutoscalerCondition
|
||||
// Channel with names of HPA objects which we have reconciled.
|
||||
processed chan string
|
||||
|
||||
@ -243,6 +241,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa
|
||||
Resource: &autoscalingv2.ResourceMetricSource{
|
||||
Name: v1.ResourceCPU,
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.UtilizationMetricType,
|
||||
AverageUtilization: &tc.CPUTarget,
|
||||
},
|
||||
},
|
||||
@ -265,13 +264,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa
|
||||
}
|
||||
}
|
||||
|
||||
// and... convert to autoscaling v1 to return the right type
|
||||
objv1, err := unsafeConvertToVersionVia(obj, autoscalingv1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
return true, nil, err
|
||||
}
|
||||
|
||||
return true, objv1, nil
|
||||
return true, obj, nil
|
||||
})
|
||||
|
||||
fakeClient.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
@ -364,23 +357,20 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa
|
||||
})
|
||||
|
||||
fakeClient.AddReactor("update", "horizontalpodautoscalers", func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
handled, obj, err := func() (handled bool, ret *autoscalingv1.HorizontalPodAutoscaler, err error) {
|
||||
handled, obj, err := func() (handled bool, ret *autoscalingv2.HorizontalPodAutoscaler, err error) {
|
||||
tc.Lock()
|
||||
defer tc.Unlock()
|
||||
|
||||
obj := action.(core.UpdateAction).GetObject().(*autoscalingv1.HorizontalPodAutoscaler)
|
||||
obj := action.(core.UpdateAction).GetObject().(*autoscalingv2.HorizontalPodAutoscaler)
|
||||
assert.Equal(t, namespace, obj.Namespace, "the HPA namespace should be as expected")
|
||||
assert.Equal(t, hpaName, obj.Name, "the HPA name should be as expected")
|
||||
assert.Equal(t, tc.expectedDesiredReplicas, obj.Status.DesiredReplicas, "the desired replica count reported in the object status should be as expected")
|
||||
if tc.verifyCPUCurrent {
|
||||
if assert.NotNil(t, obj.Status.CurrentCPUUtilizationPercentage, "the reported CPU utilization percentage should be non-nil") {
|
||||
assert.Equal(t, tc.CPUCurrent, *obj.Status.CurrentCPUUtilizationPercentage, "the report CPU utilization percentage should be as expected")
|
||||
if utilization := findCpuUtilization(obj.Status.CurrentMetrics); assert.NotNil(t, utilization, "the reported CPU utilization percentage should be non-nil") {
|
||||
assert.Equal(t, tc.CPUCurrent, *utilization, "the report CPU utilization percentage should be as expected")
|
||||
}
|
||||
}
|
||||
var actualConditions []autoscalingv1.HorizontalPodAutoscalerCondition
|
||||
if err := json.Unmarshal([]byte(obj.ObjectMeta.Annotations[autoscaling.HorizontalPodAutoscalerConditionsAnnotation]), &actualConditions); err != nil {
|
||||
return true, nil, err
|
||||
}
|
||||
actualConditions := obj.Status.Conditions
|
||||
// TODO: it's ok not to sort these because statusOk
|
||||
// contains all the conditions, so we'll never be appending.
|
||||
// Default to statusOk when missing any specific conditions
|
||||
@ -654,6 +644,25 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa
|
||||
return fakeClient, fakeMetricsClient, fakeCMClient, fakeEMClient, fakeScaleClient
|
||||
}
|
||||
|
||||
func findCpuUtilization(metricStatus []autoscalingv2.MetricStatus) (utilization *int32) {
|
||||
for _, s := range metricStatus {
|
||||
if s.Type != autoscalingv2.ResourceMetricSourceType {
|
||||
continue
|
||||
}
|
||||
if s.Resource == nil {
|
||||
continue
|
||||
}
|
||||
if s.Resource.Name != v1.ResourceCPU {
|
||||
continue
|
||||
}
|
||||
if s.Resource.Current.AverageUtilization == nil {
|
||||
continue
|
||||
}
|
||||
return s.Resource.Current.AverageUtilization
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tc *testCase) verifyResults(t *testing.T) {
|
||||
tc.Lock()
|
||||
defer tc.Unlock()
|
||||
@ -717,10 +726,10 @@ func (tc *testCase) setupController(t *testing.T) (*HorizontalController, inform
|
||||
hpaController := NewHorizontalController(
|
||||
eventClient.CoreV1(),
|
||||
testScaleClient,
|
||||
testClient.AutoscalingV1(),
|
||||
testClient.AutoscalingV2(),
|
||||
testrestmapper.TestOnlyStaticRESTMapper(legacyscheme.Scheme),
|
||||
metricsClient,
|
||||
informerFactory.Autoscaling().V1().HorizontalPodAutoscalers(),
|
||||
informerFactory.Autoscaling().V2().HorizontalPodAutoscalers(),
|
||||
informerFactory.Core().V1().Pods(),
|
||||
100*time.Millisecond, // we need non-zero resync period to avoid race conditions
|
||||
defaultDownscalestabilizationWindow,
|
||||
@ -1000,6 +1009,7 @@ func TestScaleUpCM(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: &averageValue,
|
||||
},
|
||||
},
|
||||
@ -1028,6 +1038,7 @@ func TestScaleUpCMUnreadyAndHotCpuNoLessScale(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: &averageValue,
|
||||
},
|
||||
},
|
||||
@ -1058,6 +1069,7 @@ func TestScaleUpCMUnreadyandCpuHot(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: &averageValue,
|
||||
},
|
||||
},
|
||||
@ -1097,6 +1109,7 @@ func TestScaleUpHotCpuNoScaleWouldScaleDown(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: &averageValue,
|
||||
},
|
||||
},
|
||||
@ -1140,6 +1153,7 @@ func TestScaleUpCMObject(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: &targetValue,
|
||||
},
|
||||
},
|
||||
@ -1172,6 +1186,7 @@ func TestScaleUpFromZeroCMObject(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: &targetValue,
|
||||
},
|
||||
},
|
||||
@ -1204,6 +1219,7 @@ func TestScaleUpFromZeroIgnoresToleranceCMObject(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: &targetValue,
|
||||
},
|
||||
},
|
||||
@ -1236,6 +1252,7 @@ func TestScaleUpPerPodCMObject(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: &targetAverageValue,
|
||||
},
|
||||
},
|
||||
@ -1262,6 +1279,7 @@ func TestScaleUpCMExternal(t *testing.T) {
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(6666, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
@ -1288,6 +1306,7 @@ func TestScaleUpPerPodCMExternal(t *testing.T) {
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(2222, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
@ -1415,9 +1434,9 @@ func TestScaleUpBothMetricsEmpty(t *testing.T) { // Switch to missing
|
||||
},
|
||||
reportedLevels: []uint64{},
|
||||
reportedCPURequests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv1.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv1.ScalingActive, Status: v1.ConditionFalse, Reason: "InvalidMetricSourceType"},
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv2.ScalingActive, Status: v1.ConditionFalse, Reason: "InvalidMetricSourceType"},
|
||||
},
|
||||
}
|
||||
tc.runTest(t)
|
||||
@ -1466,6 +1485,7 @@ func TestScaleDownCM(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: &averageValue,
|
||||
},
|
||||
},
|
||||
@ -1500,6 +1520,7 @@ func TestScaleDownCMObject(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: &targetValue,
|
||||
},
|
||||
},
|
||||
@ -1534,6 +1555,7 @@ func TestScaleDownToZeroCMObject(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: &targetValue,
|
||||
},
|
||||
},
|
||||
@ -1568,6 +1590,7 @@ func TestScaleDownPerPodCMObject(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: &targetAverageValue,
|
||||
},
|
||||
},
|
||||
@ -1596,6 +1619,7 @@ func TestScaleDownCMExternal(t *testing.T) {
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(14400, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
@ -1623,6 +1647,7 @@ func TestScaleDownToZeroCMExternal(t *testing.T) {
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(14400, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
@ -1650,6 +1675,7 @@ func TestScaleDownPerPodCMExternal(t *testing.T) {
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(3000, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
@ -1776,6 +1802,7 @@ func TestToleranceCM(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: &averageValue,
|
||||
},
|
||||
},
|
||||
@ -1813,6 +1840,7 @@ func TestToleranceCMObject(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: &targetValue,
|
||||
},
|
||||
},
|
||||
@ -1845,6 +1873,7 @@ func TestToleranceCMExternal(t *testing.T) {
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(8666, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
@ -1881,6 +1910,7 @@ func TestTolerancePerPodCMObject(t *testing.T) {
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(2200, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
@ -1912,6 +1942,7 @@ func TestTolerancePerPodCMExternal(t *testing.T) {
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(2200, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
@ -2001,9 +2032,9 @@ func TestZeroReplicas(t *testing.T) {
|
||||
reportedLevels: []uint64{},
|
||||
reportedCPURequests: []resource.Quantity{},
|
||||
useMetricsAPI: true,
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv1.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv1.ScalingActive, Status: v1.ConditionFalse, Reason: "ScalingDisabled"},
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv2.ScalingActive, Status: v1.ConditionFalse, Reason: "ScalingDisabled"},
|
||||
},
|
||||
}
|
||||
tc.runTest(t)
|
||||
@ -2020,8 +2051,8 @@ func TestTooFewReplicas(t *testing.T) {
|
||||
reportedLevels: []uint64{},
|
||||
reportedCPURequests: []resource.Quantity{},
|
||||
useMetricsAPI: true,
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv1.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededRescale"},
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededRescale"},
|
||||
},
|
||||
}
|
||||
tc.runTest(t)
|
||||
@ -2038,8 +2069,8 @@ func TestTooManyReplicas(t *testing.T) {
|
||||
reportedLevels: []uint64{},
|
||||
reportedCPURequests: []resource.Quantity{},
|
||||
useMetricsAPI: true,
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv1.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededRescale"},
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededRescale"},
|
||||
},
|
||||
}
|
||||
tc.runTest(t)
|
||||
@ -2112,9 +2143,9 @@ func TestEmptyMetrics(t *testing.T) {
|
||||
reportedLevels: []uint64{},
|
||||
reportedCPURequests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
|
||||
useMetricsAPI: true,
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv1.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv1.ScalingActive, Status: v1.ConditionFalse, Reason: "FailedGetResourceMetric"},
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv2.ScalingActive, Status: v1.ConditionFalse, Reason: "FailedGetResourceMetric"},
|
||||
},
|
||||
}
|
||||
tc.runTest(t)
|
||||
@ -2131,9 +2162,9 @@ func TestEmptyCPURequest(t *testing.T) {
|
||||
reportedLevels: []uint64{200},
|
||||
reportedCPURequests: []resource.Quantity{},
|
||||
useMetricsAPI: true,
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv1.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv1.ScalingActive, Status: v1.ConditionFalse, Reason: "FailedGetResourceMetric"},
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv2.ScalingActive, Status: v1.ConditionFalse, Reason: "FailedGetResourceMetric"},
|
||||
},
|
||||
}
|
||||
tc.runTest(t)
|
||||
@ -2274,14 +2305,14 @@ func TestConditionInvalidSelectorMissing(t *testing.T) {
|
||||
reportedLevels: []uint64{100, 200, 300},
|
||||
reportedCPURequests: []resource.Quantity{resource.MustParse("0.1"), resource.MustParse("0.1"), resource.MustParse("0.1")},
|
||||
useMetricsAPI: true,
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{
|
||||
Type: autoscalingv1.AbleToScale,
|
||||
Type: autoscalingv2.AbleToScale,
|
||||
Status: v1.ConditionTrue,
|
||||
Reason: "SucceededGetScale",
|
||||
},
|
||||
{
|
||||
Type: autoscalingv1.ScalingActive,
|
||||
Type: autoscalingv2.ScalingActive,
|
||||
Status: v1.ConditionFalse,
|
||||
Reason: "InvalidSelector",
|
||||
},
|
||||
@ -2320,14 +2351,14 @@ func TestConditionInvalidSelectorUnparsable(t *testing.T) {
|
||||
reportedLevels: []uint64{100, 200, 300},
|
||||
reportedCPURequests: []resource.Quantity{resource.MustParse("0.1"), resource.MustParse("0.1"), resource.MustParse("0.1")},
|
||||
useMetricsAPI: true,
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{
|
||||
Type: autoscalingv1.AbleToScale,
|
||||
Type: autoscalingv2.AbleToScale,
|
||||
Status: v1.ConditionTrue,
|
||||
Reason: "SucceededGetScale",
|
||||
},
|
||||
{
|
||||
Type: autoscalingv1.ScalingActive,
|
||||
Type: autoscalingv2.ScalingActive,
|
||||
Status: v1.ConditionFalse,
|
||||
Reason: "InvalidSelector",
|
||||
},
|
||||
@ -2369,6 +2400,7 @@ func TestConditionFailedGetMetrics(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: &averageValue,
|
||||
},
|
||||
},
|
||||
@ -2387,6 +2419,7 @@ func TestConditionFailedGetMetrics(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: &targetValue,
|
||||
},
|
||||
},
|
||||
@ -2401,6 +2434,7 @@ func TestConditionFailedGetMetrics(t *testing.T) {
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(300, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
@ -2435,9 +2469,9 @@ func TestConditionFailedGetMetrics(t *testing.T) {
|
||||
return true, &emapi.ExternalMetricValueList{}, fmt.Errorf("something went wrong")
|
||||
})
|
||||
|
||||
tc.expectedConditions = []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv1.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv1.ScalingActive, Status: v1.ConditionFalse, Reason: reason},
|
||||
tc.expectedConditions = []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv2.ScalingActive, Status: v1.ConditionFalse, Reason: reason},
|
||||
}
|
||||
if specs != nil {
|
||||
tc.CPUTarget = 0
|
||||
@ -2463,14 +2497,14 @@ func TestConditionInvalidSourceType(t *testing.T) {
|
||||
},
|
||||
},
|
||||
reportedLevels: []uint64{20000},
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{
|
||||
Type: autoscalingv1.AbleToScale,
|
||||
Type: autoscalingv2.AbleToScale,
|
||||
Status: v1.ConditionTrue,
|
||||
Reason: "SucceededGetScale",
|
||||
},
|
||||
{
|
||||
Type: autoscalingv1.ScalingActive,
|
||||
Type: autoscalingv2.ScalingActive,
|
||||
Status: v1.ConditionFalse,
|
||||
Reason: "InvalidMetricSourceType",
|
||||
},
|
||||
@ -2490,9 +2524,9 @@ func TestConditionFailedGetScale(t *testing.T) {
|
||||
reportedLevels: []uint64{100, 200, 300},
|
||||
reportedCPURequests: []resource.Quantity{resource.MustParse("0.1"), resource.MustParse("0.1"), resource.MustParse("0.1")},
|
||||
useMetricsAPI: true,
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{
|
||||
Type: autoscalingv1.AbleToScale,
|
||||
Type: autoscalingv2.AbleToScale,
|
||||
Status: v1.ConditionFalse,
|
||||
Reason: "FailedGetScale",
|
||||
},
|
||||
@ -2555,6 +2589,7 @@ func TestNoBackoffUpscaleCM(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: &averageValue,
|
||||
},
|
||||
},
|
||||
@ -2599,6 +2634,7 @@ func TestNoBackoffUpscaleCMNoBackoffCpu(t *testing.T) {
|
||||
Name: "qps",
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.AverageValueMetricType,
|
||||
AverageValue: &averageValue,
|
||||
},
|
||||
},
|
||||
@ -2770,8 +2806,8 @@ func TestScaleUpRCImmediately(t *testing.T) {
|
||||
reportedCPURequests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
|
||||
useMetricsAPI: true,
|
||||
lastScaleTime: &time,
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv1.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededRescale"},
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededRescale"},
|
||||
},
|
||||
}
|
||||
tc.runTest(t)
|
||||
@ -2790,8 +2826,8 @@ func TestScaleDownRCImmediately(t *testing.T) {
|
||||
reportedCPURequests: []resource.Quantity{resource.MustParse("0.9"), resource.MustParse("1.0"), resource.MustParse("1.1")},
|
||||
useMetricsAPI: true,
|
||||
lastScaleTime: &time,
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv1.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededRescale"},
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededRescale"},
|
||||
},
|
||||
}
|
||||
tc.runTest(t)
|
||||
@ -2831,6 +2867,8 @@ func TestAvoidUnnecessaryUpdates(t *testing.T) {
|
||||
tc.processed <- "test-hpa"
|
||||
}()
|
||||
|
||||
var eighty int32 = 80
|
||||
|
||||
quantity := resource.MustParse("400m")
|
||||
obj := &autoscalingv2.HorizontalPodAutoscalerList{
|
||||
Items: []autoscalingv2.HorizontalPodAutoscaler{
|
||||
@ -2846,7 +2884,21 @@ func TestAvoidUnnecessaryUpdates(t *testing.T) {
|
||||
Name: "test-rc",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
|
||||
Metrics: []autoscalingv2.MetricSpec{{
|
||||
Type: autoscalingv2.ResourceMetricSourceType,
|
||||
Resource: &autoscalingv2.ResourceMetricSource{
|
||||
Name: v1.ResourceCPU,
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.UtilizationMetricType,
|
||||
// TODO: Change this to &tc.CPUTarget and the expected ScaleLimited
|
||||
// condition to False. This test incorrectly leaves the v1
|
||||
// HPA field TargetCPUUtilizization field blank and the
|
||||
// controller defaults to a target of 80. So the test relies
|
||||
// on downscale stabilization to prevent a scale change.
|
||||
AverageUtilization: &eighty,
|
||||
},
|
||||
},
|
||||
}},
|
||||
MinReplicas: &tc.minReplicas,
|
||||
MaxReplicas: tc.maxReplicas,
|
||||
},
|
||||
@ -2893,13 +2945,8 @@ func TestAvoidUnnecessaryUpdates(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
// and... convert to autoscaling v1 to return the right type
|
||||
objv1, err := unsafeConvertToVersionVia(obj, autoscalingv1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
return true, nil, err
|
||||
}
|
||||
|
||||
return true, objv1, nil
|
||||
return true, obj, nil
|
||||
})
|
||||
testClient.PrependReactor("update", "horizontalpodautoscalers", func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
assert.Fail(t, "should not have attempted to update the HPA when nothing changed")
|
||||
@ -4024,6 +4071,7 @@ func TestScaleUpOneMetricEmpty(t *testing.T) {
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
@ -4057,9 +4105,9 @@ func TestNoScaleDownOneMetricInvalid(t *testing.T) {
|
||||
reportedCPURequests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
|
||||
useMetricsAPI: true,
|
||||
recommendations: []timestampedRecommendation{},
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv1.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv1.ScalingActive, Status: v1.ConditionFalse, Reason: "InvalidMetricSourceType"},
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv2.ScalingActive, Status: v1.ConditionFalse, Reason: "InvalidMetricSourceType"},
|
||||
},
|
||||
}
|
||||
|
||||
@ -4083,6 +4131,7 @@ func TestNoScaleDownOneMetricEmpty(t *testing.T) {
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(1000, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
@ -4092,9 +4141,9 @@ func TestNoScaleDownOneMetricEmpty(t *testing.T) {
|
||||
reportedCPURequests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
|
||||
useMetricsAPI: true,
|
||||
recommendations: []timestampedRecommendation{},
|
||||
expectedConditions: []autoscalingv1.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv1.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv1.ScalingActive, Status: v1.ConditionFalse, Reason: "FailedGetExternalMetric"},
|
||||
expectedConditions: []autoscalingv2.HorizontalPodAutoscalerCondition{
|
||||
{Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededGetScale"},
|
||||
{Type: autoscalingv2.ScalingActive, Status: v1.ConditionFalse, Reason: "FailedGetExternalMetric"},
|
||||
},
|
||||
}
|
||||
_, _, _, testEMClient, _ := tc.prepareTestClient(t)
|
||||
|
Loading…
Reference in New Issue
Block a user