Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,14 @@ spec:
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
- jsonPath: .spec.updatePolicy.minReplicas
name: MinReplicas
priority: 1
type: integer
- jsonPath: .spec.updatePolicy.evictAfterOOMThreshold
name: OOMThreshold
priority: 1
type: string
name: v1
schema:
openAPIV3Schema:
Expand Down Expand Up @@ -425,6 +433,14 @@ spec:
If not specified, all fields in the `PodUpdatePolicy` are set to their
default values.
properties:
evictAfterOOMThreshold:
description: |-
EvictAfterOOMThreshold specifies the time to wait after an OOM event before
considering the pod for eviction. Pods that have OOMed in less than this threshold
since start will be evicted.
format: duration
pattern: ^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$
type: string
evictionRequirements:
description: |-
EvictionRequirements is a list of EvictionRequirements that need to
Expand Down Expand Up @@ -478,6 +494,10 @@ spec:
- Auto
type: string
type: object
x-kubernetes-validations:
- message: evictAfterOOMThreshold must be greater than 0
rule: '!has(self.evictAfterOOMThreshold) || duration(self.evictAfterOOMThreshold)
>= duration(''0s'')'
required:
- targetRef
type: object
Expand Down
20 changes: 20 additions & 0 deletions vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,14 @@ spec:
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
- jsonPath: .spec.updatePolicy.minReplicas
name: MinReplicas
priority: 1
type: integer
- jsonPath: .spec.updatePolicy.evictAfterOOMThreshold
name: OOMThreshold
priority: 1
type: string
name: v1
schema:
openAPIV3Schema:
Expand Down Expand Up @@ -425,6 +433,14 @@ spec:
If not specified, all fields in the `PodUpdatePolicy` are set to their
default values.
properties:
evictAfterOOMThreshold:
description: |-
EvictAfterOOMThreshold specifies the time to wait after an OOM event before
considering the pod for eviction. Pods that have OOMed in less than this threshold
since start will be evicted.
format: duration
pattern: ^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$
type: string
evictionRequirements:
description: |-
EvictionRequirements is a list of EvictionRequirements that need to
Expand Down Expand Up @@ -478,6 +494,10 @@ spec:
- Auto
type: string
type: object
x-kubernetes-validations:
- message: evictAfterOOMThreshold must be greater than 0
rule: '!has(self.evictAfterOOMThreshold) || duration(self.evictAfterOOMThreshold)
>= duration(''0s'')'
required:
- targetRef
type: object
Expand Down
3 changes: 1 addition & 2 deletions vertical-pod-autoscaler/docs/flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ This document is auto-generated from the flag definitions in the VPA updater cod
| `add-dir-header` | | | If true, adds the file directory to the header of the log messages |
| `address` | string | ":8943" | The address to expose Prometheus metrics. |
| `alsologtostderr` | | | log to standard error as well as files (no effect when -logtostderr=true) |
| `evict-after-oom-threshold` | | 10m0s | duration Evict pod that has OOMed in less than evict-after-oom-threshold since start. |
| `evict-after-oom-threshold` | | 10m0s | duration The default duration to evict pod that has OOMed in less than evict-after-oom-threshold since start. |
| `eviction-rate-burst` | int | 1 | Burst of pods that can be evicted. |
| `eviction-rate-limit` | float | | Number of pods that can be evicted per seconds. A rate limit set to 0 or -1 will disable<br>the rate limiter. (default -1) |
| `eviction-tolerance` | float | 0.5 | Fraction of replica count that can be evicted for update, if more than one pod can be evicted. |
Expand Down Expand Up @@ -174,4 +174,3 @@ This document is auto-generated from the flag definitions in the VPA updater cod
| `v,` | | : 4 | , --v Level set the log level verbosity (default 4) |
| `vmodule` | moduleSpec | | comma-separated list of pattern=N settings for file-filtered logging |
| `vpa-object-namespace` | string | | Specifies the namespace to search for VPA objects. Leave empty to include all namespaces. If provided, the garbage collector will only clean this namespace. |

5 changes: 4 additions & 1 deletion vertical-pod-autoscaler/e2e/utils/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ var RecommenderLabels = map[string]string{"app": "vpa-recommender"}
// HamsterLabels are labels of hamster app
var HamsterLabels = map[string]string{"app": "hamster"}

// OOMLabels are labels for OOM test pods
var OOMLabels = map[string]string{"app": "oom-test"}

// SIGDescribe adds sig-autoscaling tag to test description.
// Takes args that are passed to ginkgo.Describe.
func SIGDescribe(scenario, name string, args ...interface{}) bool {
Expand Down Expand Up @@ -226,7 +229,7 @@ func NewNHamstersDeployment(f *framework.Framework, n int) *appsv1.Deployment {
DefaultHamsterReplicas, /*replicas*/
HamsterLabels, /*podLabels*/
GetHamsterContainerNameByIndex(0), /*imageName*/
"registry.k8s.io/ubuntu-slim:0.14", /*image*/
"ubuntu:25.10", /*image*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a quick grep of the kubernetes codebase, and I see they use ubuntu:latest
Unsure if it really matters though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm I always prefer to set a specific version rather than latest, but no strong opinions here

appsv1.RollingUpdateDeploymentStrategyType, /*strategyType*/
)
d.ObjectMeta.Namespace = f.Namespace.Name
Expand Down
235 changes: 235 additions & 0 deletions vertical-pod-autoscaler/e2e/v1/admission_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -1111,3 +1111,238 @@ func waitForVpaWebhookRegistration(f *framework.Framework) {
return false
}, 3*time.Minute, 5*time.Second).Should(gomega.BeTrue(), "Webhook was not registered in the cluster")
}

var _ = AdmissionControllerE2eDescribe("Admission-controller", func() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the tests in this block look like they're dupes of tests higher-up in the file

f := framework.NewDefaultFramework("vertical-pod-autoscaling")
f.NamespacePodSecurityEnforceLevel = podsecurity.LevelBaseline

ginkgo.BeforeEach(func() {
waitForVpaWebhookRegistration(f)
})

f.It("accepts valid and rejects invalid VPA object", framework.WithFeatureGate(features.PerVPAConfig), func() {
ginkgo.By("Setting up valid VPA object")
validVPA := []byte(`{
"kind": "VerticalPodAutoscaler",
"apiVersion": "autoscaling.k8s.io/v1",
"metadata": {"name": "hamster-vpa-valid"},
"spec": {
"targetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name":"hamster"
},
"resourcePolicy": {
"containerPolicies": [{"containerName": "*", "minAllowed":{"cpu":"50m"}}]
}
}
}`)
err := InstallRawVPA(f, validVPA)
gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Valid VPA object rejected")

ginkgo.By("Setting up invalid VPA objects")
testCases := []struct {
name string
vpaJSON string
expectedErr string
}{
{
name: "Invalid oomBumpUpRatio (negative value)",
vpaJSON: `{
"apiVersion": "autoscaling.k8s.io/v1",
"kind": "VerticalPodAutoscaler",
"metadata": {"name": "oom-test-vpa"},
"spec": {
"targetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "oom-test"
},
"updatePolicy": {
"updateMode": "Auto"
},
"resourcePolicy": {
"containerPolicies": [{
"containerName": "*",
"oomBumpUpRatio": -1,
"oomMinBumpUp": 104857600
}]
}
}
}`,
expectedErr: "admission webhook \"vpa.k8s.io\" denied the request: oomBumpUpRatio must be greater than or equal to 1.0, got -1",
},
{
name: "Invalid oomBumpUpRatio (string value)",
vpaJSON: `{
"apiVersion": "autoscaling.k8s.io/v1",
"kind": "VerticalPodAutoscaler",
"metadata": {"name": "oom-test-vpa"},
"spec": {
"targetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "oom-test"
},
"updatePolicy": {
"updateMode": "Auto"
},
"resourcePolicy": {
"containerPolicies": [{
"containerName": "*",
"oomBumpUpRatio": "not-a-number",
"oomMinBumpUp": 104857600
}]
}
}
}`,
expectedErr: "admission webhook \"vpa\\.k8s\\.io\" denied the request: quantities must match the regular expression",
},
{
name: "Invalid oomBumpUpRatio (less than 1)",
vpaJSON: `{
"apiVersion": "autoscaling.k8s.io/v1",
"kind": "VerticalPodAutoscaler",
"metadata": {"name": "oom-test-vpa"},
"spec": {
"targetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "oom-test"
},
"updatePolicy": {
"updateMode": "Auto"
},
"resourcePolicy": {
"containerPolicies": [{
"containerName": "*",
"oomBumpUpRatio": 0.5,
"oomMinBumpUp": 104857600
}]
}
}
}`,
expectedErr: "admission webhook \"vpa.k8s.io\" denied the request: oomBumpUpRatio must be greater than or equal to 1.0, got 0.5",
},
{
name: "Invalid oomMinBumpUp (negative value)",
vpaJSON: `{
"apiVersion": "autoscaling.k8s.io/v1",
"kind": "VerticalPodAutoscaler",
"metadata": {"name": "oom-test-vpa"},
"spec": {
"targetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "oom-test"
},
"updatePolicy": {
"updateMode": "Auto"
},
"resourcePolicy": {
"containerPolicies": [{
"containerName": "*",
"oomBumpUpRatio": 2,
"oomMinBumpUp": -1
}]
}
}
}`,
expectedErr: "admission webhook \"vpa\\.k8s\\.io\" denied the request: oomMinBumpUp must be greater than or equal to 0, got -1 bytes",
},
{
name: "Invalid evictAfterOOMThreshold (negative duration)",
vpaJSON: `{
"apiVersion": "autoscaling.k8s.io/v1",
"kind": "VerticalPodAutoscaler",
"metadata": {"name": "evict-threshold-vpa"},
"spec": {
"targetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "hamster"
},
"updatePolicy": {
"updateMode": "Auto",
"evictAfterOOMThreshold": "-5m"
}
}
}`,
expectedErr: "spec\\.updatePolicy\\.evictAfterOOMThreshold: Invalid value:.*evictAfterOOMThreshold must be greater than 0",
},
{
name: "Invalid evictAfterOOMThreshold (invalid format)",
vpaJSON: `{
"apiVersion": "autoscaling.k8s.io/v1",
"kind": "VerticalPodAutoscaler",
"metadata": {"name": "evict-threshold-vpa"},
"spec": {
"targetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "hamster"
},
"updatePolicy": {
"updateMode": "Auto",
"evictAfterOOMThreshold": "not-a-duration"
}
}
}`,
expectedErr: "admission webhook.*denied the request:.*invalid duration",
},
{
name: "Invalid evictAfterOOMThreshold (invalid unit)",
vpaJSON: `{
"apiVersion": "autoscaling.k8s.io/v1",
"kind": "VerticalPodAutoscaler",
"metadata": {"name": "evict-threshold-vpa"},
"spec": {
"targetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "hamster"
},
"updatePolicy": {
"updateMode": "Auto",
"evictAfterOOMThreshold": "5x"
}
}
}`,
expectedErr: "admission webhook.*denied the request:.*unknown unit.*in duration",
},
{
name: "Invalid minAllowed (invalid requests field)",
vpaJSON: `{
"apiVersion": "autoscaling.k8s.io/v1",
"kind": "VerticalPodAutoscaler",
"metadata": {"name": "hamster-vpa-invalid"},
"spec": {
"targetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "hamster"
},
"resourcePolicy": {
"containerPolicies": [{
"containerName": "*",
"minAllowed": {
"requests": {
"cpu": "50m"
}
}
}]
}
}
}`,
expectedErr: "admission webhook .*vpa.* denied the request:",
},
}
for _, tc := range testCases {
err := InstallRawVPA(f, []byte(tc.vpaJSON))
gomega.Expect(err).To(gomega.HaveOccurred(),
fmt.Sprintf("Test case '%s': Invalid VPA object accepted", tc.name))
gomega.Expect(err.Error()).To(gomega.MatchRegexp(tc.expectedErr),
fmt.Sprintf("Test case '%s': Expected error pattern not matched. Got error: %v", tc.name, err))
}
})
})
2 changes: 2 additions & 0 deletions vertical-pod-autoscaler/e2e/v1/autoscaling_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/autoscaler/vertical-pod-autoscaler/e2e/utils"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/test/e2e/framework"
e2edebug "k8s.io/kubernetes/test/e2e/framework/debug"
Expand Down Expand Up @@ -444,6 +445,7 @@ func runOomingReplicationController(c clientset.Interface, ns, name string, repl
Namespace: ns,
Timeout: timeoutRC,
Replicas: replicas,
Labels: utils.OOMLabels,
Annotations: make(map[string]string),
MemRequest: 1024 * 1024 * 1024,
MemLimit: 1024 * 1024 * 1024,
Expand Down
Loading
Loading