Generate an E2E resize patch from original & desired ResizableContainerInfo

This commit is contained in:
Tim Allclair
2025-07-07 11:42:28 -07:00
parent cc1fc3eea0
commit 00c1c1a58e
2 changed files with 148 additions and 7 deletions

View File

@@ -27,6 +27,7 @@ import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/strategicpatch"
helpers "k8s.io/component-helpers/resource"
"k8s.io/kubectl/pkg/util/podutils"
kubeqos "k8s.io/kubernetes/pkg/kubelet/qos"
@@ -59,18 +60,19 @@ type containerPatch struct {
CPU string `json:"cpu,omitempty"`
Memory string `json:"memory,omitempty"`
EphStor string `json:"ephemeral-storage,omitempty"`
} `json:"requests"`
} `json:"requests,omitzero"`
Limits struct {
CPU string `json:"cpu,omitempty"`
Memory string `json:"memory,omitempty"`
EphStor string `json:"ephemeral-storage,omitempty"`
} `json:"limits"`
} `json:"limits,omitzero"`
} `json:"resources"`
}
type patchSpec struct {
Spec struct {
Containers []containerPatch `json:"containers"`
Containers []containerPatch `json:"containers,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
InitContainers []containerPatch `json:"initContainers,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
} `json:"spec"`
}
@@ -370,7 +372,7 @@ func ExpectPodResized(ctx context.Context, f *framework.Framework, resizedPod *v
}
// ResizeContainerPatch generates a patch string to resize the pod container.
func ResizeContainerPatch(containers []ResizableContainerInfo) (string, error) {
func ResizeContainerPatch(containers []ResizableContainerInfo) ([]byte, error) {
var patch patchSpec
for _, container := range containers {
@@ -381,15 +383,32 @@ func ResizeContainerPatch(containers []ResizableContainerInfo) (string, error) {
cPatch.Resources.Limits.CPU = container.Resources.CPULim
cPatch.Resources.Limits.Memory = container.Resources.MemLim
patch.Spec.Containers = append(patch.Spec.Containers, cPatch)
if container.InitCtr {
patch.Spec.InitContainers = append(patch.Spec.InitContainers, cPatch)
} else {
patch.Spec.Containers = append(patch.Spec.Containers, cPatch)
}
}
patchBytes, err := json.Marshal(patch)
if err != nil {
return "", err
return nil, err
}
return string(patchBytes), nil
return patchBytes, nil
}
func MakeResizePatch(originalContainers, desiredContainers []ResizableContainerInfo) []byte {
original, err := json.Marshal(MakePodWithResizableContainers("", "", "", originalContainers))
framework.ExpectNoError(err)
desired, err := json.Marshal(MakePodWithResizableContainers("", "", "", desiredContainers))
framework.ExpectNoError(err)
patch, err := strategicpatch.CreateTwoWayMergePatch(original, desired, v1.Pod{})
framework.ExpectNoError(err)
return patch
}
// UpdateExpectedContainerRestarts updates the RestartCounts in expectedContainers by

View File

@@ -0,0 +1,122 @@
/*
Copyright 2025 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package podresize
import (
"encoding/json"
"testing"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/kubernetes/test/e2e/common/node/framework/cgroups"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMakeResizePatch(t *testing.T) {
tests := []struct {
name string
old []ResizableContainerInfo
new []ResizableContainerInfo
want string
}{
{
name: "no change",
old: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{CPUReq: "10m", MemReq: "10Mi"}},
},
new: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{CPUReq: "10m", MemReq: "10Mi"}},
},
want: `{}`,
},
{
name: "increase cpu",
old: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{CPUReq: "10m", CPULim: "15m", MemReq: "10Mi"}},
},
new: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{CPUReq: "20m", CPULim: "25m", MemReq: "10Mi"}},
},
want: `{"spec":{"containers":[{"name":"c1","resources":{"limits":{"cpu":"25m"},"requests":{"cpu":"20m"}}}]}}`,
},
{
name: "add cpu",
old: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{MemReq: "10Mi"}},
},
new: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{CPUReq: "20m", CPULim: "25m", MemReq: "10Mi"}},
},
want: `{"spec":{"containers":[{"name":"c1","resources":{"limits":{"cpu":"25m"},"requests":{"cpu":"20m"}}}]}}`,
},
{
name: "decrease memory",
old: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{CPUReq: "10m", CPULim: "15m", MemReq: "20Mi", MemLim: "25Mi"}},
},
new: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{CPUReq: "10m", CPULim: "15m", MemReq: "10Mi", MemLim: "15Mi"}},
},
want: `{"spec":{"containers":[{"name":"c1","resources":{"limits":{"memory":"15Mi"},"requests":{"memory":"10Mi"}}}]}}`,
},
{
name: "change multiple contaniers",
old: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{CPUReq: "10m", MemReq: "10Mi", CPULim: "15m", MemLim: "15Mi"}},
{Name: "c2", Resources: &cgroups.ContainerResources{CPUReq: "20m", MemReq: "20Mi"}},
},
new: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{CPUReq: "15m", MemReq: "10Mi", CPULim: "25m", MemLim: "25Mi"}},
{Name: "c2", Resources: &cgroups.ContainerResources{CPUReq: "20m", MemReq: "25Mi"}},
},
want: `{"spec":{"containers":[{"name":"c1","resources":{"limits":{"cpu":"25m","memory":"25Mi"},"requests":{"cpu":"15m"}}},{"name":"c2","resources":{"requests":{"memory":"25Mi"}}}]}}`,
},
{
name: "change init container",
old: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{CPUReq: "10m", MemReq: "10Mi"}},
{Name: "ic1", Resources: &cgroups.ContainerResources{CPUReq: "10m", MemReq: "10Mi"}, InitCtr: true},
},
new: []ResizableContainerInfo{
{Name: "c1", Resources: &cgroups.ContainerResources{CPUReq: "10m", MemReq: "10Mi"}},
{Name: "ic1", Resources: &cgroups.ContainerResources{CPUReq: "20m", MemReq: "10Mi"}, InitCtr: true},
},
want: `{"spec":{"initContainers":[{"name":"ic1","resources":{"requests":{"cpu":"20m"}}}]}}`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
patch := MakeResizePatch(tt.old, tt.new)
// Ignore the "$setElementOrder" directive for patch comparison.
patchMap := strategicpatch.JSONMap{}
err := json.Unmarshal(patch, &patchMap)
require.NoError(t, err)
if patchMap, ok := patchMap["spec"].(map[string]interface{}); ok {
delete(patchMap, "$setElementOrder/containers")
delete(patchMap, "$setElementOrder/initContainers")
}
patch, err = json.Marshal(patchMap)
require.NoError(t, err)
assert.Equal(t, tt.want, string(patch))
})
}
}