Skip to content

Commit

Permalink
integration tests for pod resize
Browse files Browse the repository at this point in the history
  • Loading branch information
AnishShah committed Oct 22, 2024
1 parent 5524739 commit da3b408
Show file tree
Hide file tree
Showing 2 changed files with 250 additions and 2 deletions.
248 changes: 248 additions & 0 deletions test/integration/pods/pods_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"testing"

v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
utilfeature "k8s.io/apiserver/pkg/util/feature"
Expand Down Expand Up @@ -678,6 +679,253 @@ func TestPodUpdateEphemeralContainers(t *testing.T) {
}
}

func TestPodResize(t *testing.T) {
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
server := kubeapiservertesting.StartTestServerOrDie(t, nil,
append(framework.DefaultTestServerFlags(), "--feature-gates=InPlacePodVerticalScaling=true"),
framework.SharedEtcd())
defer server.TearDownFn()

client := clientset.NewForConfigOrDie(server.ClientConfig)

ns := framework.CreateNamespaceOrDie(client, "pod-create-ephemeral-containers", t)
defer framework.DeleteNamespaceOrDie(client, ns, t)

testPod := func(name string) *v1.Pod {
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fake-name",
Image: "fakeimage",
},
},
},
}
}

resizeCases := []struct {
name string
originalRes v1.ResourceRequirements
resize v1.ResourceRequirements
valid bool
}{
{
name: "cpu request change",
originalRes: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("10m"),
},
},
resize: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("20m"),
},
},
valid: true,
},
{
name: "memory request change",
originalRes: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceMemory: resource.MustParse("1Gi"),
},
},
resize: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceMemory: resource.MustParse("2Gi"),
},
},
valid: true,
},
{
name: "storage request change",
originalRes: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceEphemeralStorage: resource.MustParse("1Gi"),
},
},
resize: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceEphemeralStorage: resource.MustParse("2Gi"),
},
},
valid: false,
},
}

for _, tc := range resizeCases {
pod := testPod("resize")
pod.Spec.Containers[0].Resources = tc.originalRes
resp, err := client.CoreV1().Pods(ns.Name).Create(context.TODO(), pod, metav1.CreateOptions{})
if err != nil {
t.Fatalf("Unexpected error when creating pod: %v", err)
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
}

// Part 1. Resize
resp.Spec.Containers[0].Resources = tc.resize
if _, err := client.CoreV1().Pods(ns.Name).Update(context.TODO(), resp, metav1.UpdateOptions{}); err == nil {
t.Fatalf("Unexpected allowed pod update")
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
} else if !strings.Contains(err.Error(), "spec: Forbidden: pod updates may not change fields other than") {
t.Fatalf("Unexpected error when updating pod container resources: %v", err)
}

resp, err = client.CoreV1().Pods(ns.Name).Resize(context.TODO(), resp.Name, resp, metav1.UpdateOptions{})
if tc.valid && err != nil {
t.Fatalf("Unexpected pod resize failure: %v", err)
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
}
if !tc.valid && err == nil {
t.Fatalf("Unexpected pod resize success")
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
}

// Part 2. Rollback
if !tc.valid {
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
continue
}
resp.Spec.Containers[0].Resources = tc.originalRes
_, err = client.CoreV1().Pods(ns.Name).Resize(context.TODO(), resp.Name, resp, metav1.UpdateOptions{})
if tc.valid && err != nil {
t.Fatalf("Unexpected pod resize failure: %v", err)
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
}
if !tc.valid && err == nil {
t.Fatalf("Unexpected pod resize success")
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
}

integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
}

patchCases := []struct {
name string
originalRes v1.ResourceRequirements
patchBody string
patchType types.PatchType
valid bool
}{
{
name: "cpu request change (strategic)",
originalRes: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("10m"),
},
},
patchType: types.StrategicMergePatchType,
patchBody: `{
"spec":{
"containers":[
{
"name":"fake-name",
"resources": {
"requests": {
"cpu":"20m"
}
}
}
]
}
}`,
valid: true,
},
{
name: "cpu request change (merge)",
originalRes: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("10m"),
},
},
patchType: types.MergePatchType,
patchBody: `{
"spec":{
"containers":[
{
"name":"fake-name",
"resources": {
"requests": {
"cpu":"20m"
}
}
}
]
}
}`,
valid: true,
},
{
name: "cpu request change (JSON)",
originalRes: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("10m"),
},
},
patchType: types.JSONPatchType,
patchBody: `[{
"op":"add",
"path":"/spec/containers",
"value":[{
"name":"fake-name",
"resources": {
"requests": {
"cpu":"20m"
}
}
}]
}]`,
valid: true,
},
{
name: "storage request change (merge)",
originalRes: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("10m"),
},
},
patchType: types.MergePatchType,
patchBody: `{
"spec":{
"containers":[
{
"name":"fake-name",
"resources": {
"requests": {
"ephemeral-storage":"20m"
}
}
}
]
}
}`,
valid: false,
},
}

for _, tc := range patchCases {
pod := testPod("resize")
pod.Spec.Containers[0].Resources = tc.originalRes
if _, err := client.CoreV1().Pods(ns.Name).Create(context.TODO(), pod, metav1.CreateOptions{}); err != nil {
t.Fatalf("Unexpected error when creating pod: %v", err)
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
}

if _, err := client.CoreV1().Pods(ns.Name).Patch(context.TODO(), pod.Name, tc.patchType, []byte(tc.patchBody), metav1.PatchOptions{}, "resize"); tc.valid && err != nil {
t.Fatalf("Unexpected pod resize failure: %v", err)
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
} else if !tc.valid && err == nil {
t.Fatalf("Unexpected pod resize success")
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
}
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
}
}

func TestMutablePodSchedulingDirectives(t *testing.T) {
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
server := kubeapiservertesting.StartTestServerOrDie(t, nil, framework.DefaultTestServerFlags(), framework.SharedEtcd())
Expand Down
4 changes: 2 additions & 2 deletions test/integration/scheduler/queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,8 @@ func TestCoreResourceEnqueue(t *testing.T) {
triggerFn: func(testCtx *testutils.TestContext) (map[framework.ClusterEvent]uint64, error) {
// Trigger a PodUpdate event by reducing cpu requested by pod1.
// It makes Pod1 schedulable.
if _, err := testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Update(testCtx.Ctx, st.MakePod().Name("pod1").Req(map[v1.ResourceName]string{v1.ResourceCPU: "2"}).Container("image").Obj(), metav1.UpdateOptions{}); err != nil {
return nil, fmt.Errorf("failed to update the pod: %w", err)
if _, err := testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Resize(testCtx.Ctx, "pod1", st.MakePod().Name("pod1").Req(map[v1.ResourceName]string{v1.ResourceCPU: "2"}).Container("image").Obj(), metav1.UpdateOptions{}); err != nil {
return nil, fmt.Errorf("failed to resize the pod: %w", err)
}
return map[framework.ClusterEvent]uint64{{Resource: unschedulablePod, ActionType: framework.UpdatePodScaleDown}: 1}, nil
},
Expand Down

0 comments on commit da3b408

Please sign in to comment.