diff --git a/vertical-pod-autoscaler/charts/vertical-pod-autoscaler/crds/vpa-v1-crd-gen.yaml b/vertical-pod-autoscaler/charts/vertical-pod-autoscaler/crds/vpa-v1-crd-gen.yaml index c1472ea211bc..0f6190a9b9ea 100644 --- a/vertical-pod-autoscaler/charts/vertical-pod-autoscaler/crds/vpa-v1-crd-gen.yaml +++ b/vertical-pod-autoscaler/charts/vertical-pod-autoscaler/crds/vpa-v1-crd-gen.yaml @@ -408,7 +408,7 @@ spec: factor: description: |- factor specifies the factor to apply to the resource request. - This field is to be used only when Type is "Factor". + This field is required when Type is "Factor". format: int32 type: integer quantity: @@ -418,7 +418,7 @@ spec: description: |- quantity specifies the absolute resource quantity to be used as the resource request and limit during the boost phase. - This field is to be used only when Type is "Quantity". + This field is required when Type is "Quantity". pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: @@ -453,7 +453,7 @@ spec: factor: description: |- factor specifies the factor to apply to the resource request. - This field is to be used only when Type is "Factor". + This field is required when Type is "Factor". format: int32 type: integer quantity: @@ -463,7 +463,7 @@ spec: description: |- quantity specifies the absolute resource quantity to be used as the resource request and limit during the boost phase. - This field is to be used only when Type is "Quantity". + This field is required when Type is "Quantity". pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: diff --git a/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml b/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml index c1472ea211bc..0f6190a9b9ea 100644 --- a/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml +++ b/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml @@ -408,7 +408,7 @@ spec: factor: description: |- factor specifies the factor to apply to the resource request. - This field is to be used only when Type is "Factor". + This field is required when Type is "Factor". format: int32 type: integer quantity: @@ -418,7 +418,7 @@ spec: description: |- quantity specifies the absolute resource quantity to be used as the resource request and limit during the boost phase. - This field is to be used only when Type is "Quantity". + This field is required when Type is "Quantity". pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: @@ -453,7 +453,7 @@ spec: factor: description: |- factor specifies the factor to apply to the resource request. - This field is to be used only when Type is "Factor". + This field is required when Type is "Factor". format: int32 type: integer quantity: @@ -463,7 +463,7 @@ spec: description: |- quantity specifies the absolute resource quantity to be used as the resource request and limit during the boost phase. - This field is to be used only when Type is "Quantity". + This field is required when Type is "Quantity". pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: diff --git a/vertical-pod-autoscaler/enhancements/7862-cpu-startup-boost/README.md b/vertical-pod-autoscaler/enhancements/7862-cpu-startup-boost/README.md index b74bf93d98dc..ad4427f3e1a4 100644 --- a/vertical-pod-autoscaler/enhancements/7862-cpu-startup-boost/README.md +++ b/vertical-pod-autoscaler/enhancements/7862-cpu-startup-boost/README.md @@ -159,7 +159,7 @@ type CPUStartupBoost struct { > [!IMPORTANT] > The boosted CPU value will be capped by -> [`--container-recommendation-max-allowed-cpu`](https://github.com/kubernetes/autoscaler/blob/4d294562e505431d518a81e8833accc0ec99c9b8/vertical-pod-autoscaler/pkg/recommender/main.go#L122) +> [`--max-allowed-cpu-boost`](https://github.com/kubernetes/autoscaler/blob/4b40a55bebd2ce184b289cd028969182d15f412c/vertical-pod-autoscaler/pkg/admission-controller/main.go#L86C1-L86C2) > flag value, if set. > [!NOTE] diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/pod/patch/resource_updates_test.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/pod/patch/resource_updates_test.go index 63bda557a7a7..fb4547c6e9f3 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/pod/patch/resource_updates_test.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/pod/patch/resource_updates_test.go @@ -730,6 +730,75 @@ func TestCalculatePatches_StartupBoost(t *testing.T) { GetAddAnnotationPatch(ResourceUpdatesAnnotation, "Pod resources updated by name: container 0: cpu request"), }, }, + { + name: "startup boost invalid type", + pod: &core.Pod{ + Spec: core.PodSpec{ + Containers: []core.Container{ + { + Name: "container1", + Resources: core.ResourceRequirements{ + Requests: core.ResourceList{ + cpu: resource.MustParse("1m"), + }, + }, + }, + }, + }, + }, + vpa: test.VerticalPodAutoscaler().WithName("name").WithContainer("container1").WithCPUStartupBoost("Invalid", &factor2, nil, "10s").Get(), + recommendResources: []vpa_api_util.ContainerResources{ + { + Requests: core.ResourceList{ + cpu: resource.MustParse("100m"), + }, + }, + }, + maxAllowedCpu: resource.QuantityValue{}, + featureGateEnabled: true, + expectError: fmt.Errorf("unsupported startup boost type: Invalid"), + }, + { + name: "startup boost container policy takes precedence", + pod: &core.Pod{ + Spec: core.PodSpec{ + Containers: []core.Container{ + { + Name: "container1", + Resources: core.ResourceRequirements{ + Requests: core.ResourceList{ + cpu: resource.MustParse("1m"), + }, + Limits: core.ResourceList{ + cpu: resource.MustParse("1m"), + }, + }, + }, + }, + }, + }, + vpa: test.VerticalPodAutoscaler().WithName("name").WithContainer("container1"). + WithCPUStartupBoost(vpa_types.FactorStartupBoostType, &factor2, nil, "10s"). + WithContainerCPUStartupBoost("container1", vpa_types.FactorStartupBoostType, &factor3, nil, "10s").Get(), + recommendResources: []vpa_api_util.ContainerResources{ + { + Requests: core.ResourceList{ + cpu: resource.MustParse("100m"), + }, + Limits: core.ResourceList{ + cpu: resource.MustParse("100m"), + }, + }, + }, + maxAllowedCpu: resource.QuantityValue{}, + featureGateEnabled: true, + expectPatches: []resource_admission.PatchRecord{ + GetAddAnnotationPatch(annotations.StartupCPUBoostAnnotation, "{\"requests\":{\"cpu\":\"1m\"},\"limits\":{\"cpu\":\"1m\"}}"), + addResourceRequestPatch(0, cpu, "300m"), + addResourceLimitPatch(0, cpu, "300m"), + GetAddAnnotationPatch(ResourceUpdatesAnnotation, "Pod resources updated by name: container 0: cpu request, cpu limit"), + }, + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go index e40008422035..930f26381546 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go @@ -602,6 +602,37 @@ func TestValidateVPA(t *testing.T) { }, isCreate: true, }, + { + name: "top-level and container startupBoost", + vpa: vpa_types.VerticalPodAutoscaler{ + Spec: vpa_types.VerticalPodAutoscalerSpec{ + TargetRef: &autoscaling.CrossVersionObjectReference{ + Kind: "Deployment", + Name: "my-app", + }, + StartupBoost: &vpa_types.StartupBoost{ + CPU: &vpa_types.GenericStartupBoost{ + Type: validCPUBoostTypeFactor, + Factor: &validCPUBoostFactor, + }, + }, + ResourcePolicy: &vpa_types.PodResourcePolicy{ + ContainerPolicies: []vpa_types.ContainerResourcePolicy{ + { + ContainerName: "loot box", + StartupBoost: &vpa_types.StartupBoost{ + CPU: &vpa_types.GenericStartupBoost{ + Type: validCPUBoostTypeQuantity, + Quantity: &validCPUBoostQuantity, + }, + }, + }, + }, + }, + }, + }, + isCreate: true, + }, { name: "per-vpa config active and used", vpa: vpa_types.VerticalPodAutoscaler{ diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go index acd1a4e0b31b..21d280d4ab08 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go @@ -131,14 +131,14 @@ type GenericStartupBoost struct { // +required Type StartupBoostType `json:"type" protobuf:"bytes,1,opt,name=type"` // factor specifies the factor to apply to the resource request. - // This field is to be used only when Type is "Factor". + // This field is required when Type is "Factor". // +unionMember=Factor // +optional Factor *int32 `json:"factor,omitempty" protobuf:"bytes,2,opt,name=factor"` // quantity specifies the absolute resource quantity to be used as the // resource request and limit during the boost phase. - // This field is to be used only when Type is "Quantity". + // This field is required when Type is "Quantity". // +unionMember=Quantity // +optional Quantity *resource.Quantity `json:"quantity,omitempty" protobuf:"bytes,3,opt,name=quantity"` diff --git a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go index fbb238dec39e..384e6a083048 100644 --- a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go +++ b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go @@ -51,6 +51,7 @@ type VerticalPodAutoscalerBuilder interface { WithOOMBumpUpRatio(ratio *resource.Quantity) VerticalPodAutoscalerBuilder WithOOMMinBumpUp(minBumpUp *resource.Quantity) VerticalPodAutoscalerBuilder WithCPUStartupBoost(boostType vpa_types.StartupBoostType, factor *int32, quantity *resource.Quantity, duration string) VerticalPodAutoscalerBuilder + WithContainerCPUStartupBoost(containerName string, boostType vpa_types.StartupBoostType, factor *int32, quantity *resource.Quantity, duration string) VerticalPodAutoscalerBuilder AppendCondition(conditionType vpa_types.VerticalPodAutoscalerConditionType, status core.ConditionStatus, reason, message string, lastTransitionTime time.Time) VerticalPodAutoscalerBuilder AppendRecommendation(vpa_types.RecommendedContainerResources) VerticalPodAutoscalerBuilder @@ -71,6 +72,7 @@ func VerticalPodAutoscaler() VerticalPodAutoscalerBuilder { maxAllowed: map[string]core.ResourceList{}, controlledValues: map[string]*vpa_types.ContainerControlledValues{}, scalingMode: map[string]*vpa_types.ContainerScalingMode{}, + containerStartupBoost: map[string]*vpa_types.StartupBoost{}, } } @@ -85,6 +87,7 @@ type verticalPodAutoscalerBuilder struct { maxAllowed map[string]core.ResourceList controlledValues map[string]*vpa_types.ContainerControlledValues scalingMode map[string]*vpa_types.ContainerScalingMode + containerStartupBoost map[string]*vpa_types.StartupBoost startupBoost *vpa_types.StartupBoost recommendation RecommendationBuilder conditions []vpa_types.VerticalPodAutoscalerCondition @@ -260,7 +263,8 @@ func (b *verticalPodAutoscalerBuilder) WithCPUStartupBoost(boostType vpa_types.S } if factor != nil { cpuStartupBoost.Factor = factor - } else { + } + if quantity != nil { cpuStartupBoost.Quantity = quantity } c.startupBoost = &vpa_types.StartupBoost{ @@ -269,6 +273,25 @@ func (b *verticalPodAutoscalerBuilder) WithCPUStartupBoost(boostType vpa_types.S return &c } +func (b *verticalPodAutoscalerBuilder) WithContainerCPUStartupBoost(containerName string, boostType vpa_types.StartupBoostType, factor *int32, quantity *resource.Quantity, duration string) VerticalPodAutoscalerBuilder { + c := *b + parsedDuration, _ := time.ParseDuration(duration) + cpuStartupBoost := &vpa_types.GenericStartupBoost{ + Type: boostType, + Duration: &meta.Duration{Duration: parsedDuration}, + } + if factor != nil { + cpuStartupBoost.Factor = factor + } + if quantity != nil { + cpuStartupBoost.Quantity = quantity + } + c.containerStartupBoost[containerName] = &vpa_types.StartupBoost{ + CPU: cpuStartupBoost, + } + return &c +} + func (b *verticalPodAutoscalerBuilder) Get() *vpa_types.VerticalPodAutoscaler { if len(b.containerNames) == 0 { panic("Must call WithContainer() before Get()") @@ -289,6 +312,7 @@ func (b *verticalPodAutoscalerBuilder) Get() *vpa_types.VerticalPodAutoscaler { Mode: &scalingModeAuto, OOMBumpUpRatio: b.oomBumpUpRatio, OOMMinBumpUp: b.oomMinBumpUp, + StartupBoost: b.containerStartupBoost[containerName], } if scalingMode, ok := b.scalingMode[containerName]; ok { containerResourcePolicy.Mode = scalingMode