mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-11-06 05:17:08 +00:00
Remove InPlacePodVerticalScaling feature gate
The InPlacePodVerticalScaling feature gate has been GA and locked to true since v1.35, and is now removed. Code paths that handled the disabled state are removed, along with gate overrides in tests. The node version skew check in pod resize validation is also removed: the oldest kubelet supported by a v1.38 apiserver is v1.35, where the feature is always enabled.
This commit is contained in:
1 parent
a6f04b605a
commit
be90f569f3
51 files changed
+223
-1110
No files matched your search
@@ -214,7 +214,7 @@ function usage {
|
||||
echo " CPUMANAGER_POLICY_OPTIONS=full-pcpus-only=\"true\" \\"
|
||||
echo " CPUMANAGER_RECONCILE_PERIOD=\"5s\" \\"
|
||||
echo " KUBELET_FLAGS=\"--kube-reserved=cpu=1,memory=4Gi --system-reserved=cpu=1,memory=1Gi --reserved-memory 0:memory=3Gi;1:memory=2148Mi --resolv-conf=/run/systemd/resolve/resolv.conf\" \\"
|
||||
echo " hack/local-up-cluster.sh ( run with Topology, CPU, Memory Management policies alongside InPlacePodVerticalScaling, extra flags for etcd and coredns)"
|
||||
echo " hack/local-up-cluster.sh ( run with Topology, CPU, Memory Management policies alongside InPlacePodVerticalScalingExclusiveCPUs, extra flags for etcd and coredns)"
|
||||
echo ""
|
||||
echo "-d dry-run: prepare for running commands, then show their command lines instead of running them"
|
||||
}
|
||||
|
||||
@@ -420,7 +420,6 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
|
||||
AllowNonLocalProjectedTokenPath: false,
|
||||
PodLevelResourcesEnabled: utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources),
|
||||
AllowInvalidLabelValueInRequiredNodeAffinity: false,
|
||||
AllowSidecarResizePolicy: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling),
|
||||
AllowMatchLabelKeysInPodTopologySpread: utilfeature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpread),
|
||||
AllowMatchLabelKeysInPodTopologySpreadSelectorMerge: utilfeature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpreadSelectorMerge),
|
||||
InPlacePodLevelResourcesVerticalScalingEnabled: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodLevelResourcesVerticalScaling),
|
||||
@@ -478,9 +477,6 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
|
||||
}
|
||||
}
|
||||
|
||||
// If oldPod has resize policy set on the restartable init container, we must allow it
|
||||
opts.AllowSidecarResizePolicy = opts.AllowSidecarResizePolicy || hasRestartableInitContainerResizePolicy(oldPodSpec)
|
||||
|
||||
opts.AllowContainerRestartPolicyRules = opts.AllowContainerRestartPolicyRules || containerRestartRulesInUse(oldPodSpec)
|
||||
opts.AllowRestartAllContainers = opts.AllowRestartAllContainers || restartAllContainersActionInUse(oldPodSpec)
|
||||
|
||||
@@ -770,20 +766,6 @@ func dropDisabledFields(
|
||||
dropDisabledGRPCContainerProbeTLS(podSpec, oldPodSpec)
|
||||
dropDisabledEvictionResponders(podSpec, oldPodSpec)
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) && !inPlacePodVerticalScalingInUse(oldPodSpec) {
|
||||
// Drop ResizePolicy fields. Don't drop updates to Resources field as template.spec.resources
|
||||
// field is mutable for certain controllers. Let ValidatePodUpdate handle it.
|
||||
for i := range podSpec.Containers {
|
||||
podSpec.Containers[i].ResizePolicy = nil
|
||||
}
|
||||
for i := range podSpec.InitContainers {
|
||||
podSpec.InitContainers[i].ResizePolicy = nil
|
||||
}
|
||||
for i := range podSpec.EphemeralContainers {
|
||||
podSpec.EphemeralContainers[i].ResizePolicy = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ContainerRestartRules) && !containerRestartRulesInUse(oldPodSpec) {
|
||||
dropContainerRestartRules(podSpec)
|
||||
}
|
||||
@@ -1043,28 +1025,6 @@ func dropDisabledPodStatusFields(podStatus, oldPodStatus *api.PodStatus, podSpec
|
||||
podStatus.AllocatedResources = nil
|
||||
}
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) && !inPlacePodVerticalScalingInUse(oldPodSpec) {
|
||||
// Drop Resources fields
|
||||
dropResourcesField := func(csl []api.ContainerStatus) {
|
||||
for i := range csl {
|
||||
csl[i].Resources = nil
|
||||
}
|
||||
}
|
||||
dropResourcesField(podStatus.ContainerStatuses)
|
||||
dropResourcesField(podStatus.InitContainerStatuses)
|
||||
dropResourcesField(podStatus.EphemeralContainerStatuses)
|
||||
|
||||
// Drop AllocatedResources field
|
||||
dropAllocatedResourcesField := func(csl []api.ContainerStatus) {
|
||||
for i := range csl {
|
||||
csl[i].AllocatedResources = nil
|
||||
}
|
||||
}
|
||||
dropAllocatedResourcesField(podStatus.ContainerStatuses)
|
||||
dropAllocatedResourcesField(podStatus.InitContainerStatuses)
|
||||
dropAllocatedResourcesField(podStatus.EphemeralContainerStatuses)
|
||||
}
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.DRAExtendedResource) && !draExendedResourceInUse(oldPodStatus) {
|
||||
podStatus.ExtendedResourceClaimStatus = nil
|
||||
}
|
||||
@@ -1367,23 +1327,6 @@ func podLevelStatusResourcesInUse(podStatus *api.PodStatus) bool {
|
||||
return podStatus.Resources != nil || podStatus.AllocatedResources != nil
|
||||
}
|
||||
|
||||
// inPlacePodVerticalScalingInUse returns true if pod spec is non-nil and ResizePolicy is set
|
||||
func inPlacePodVerticalScalingInUse(podSpec *api.PodSpec) bool {
|
||||
if podSpec == nil {
|
||||
return false
|
||||
}
|
||||
var inUse bool
|
||||
containersMask := Containers | InitContainers
|
||||
VisitContainers(podSpec, containersMask, func(c *api.Container, containerType ContainerType) bool {
|
||||
if len(c.ResizePolicy) > 0 {
|
||||
inUse = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return inUse
|
||||
}
|
||||
|
||||
// procMountInUse returns true if the pod spec is non-nil and has a SecurityContext's ProcMount field set to a non-default value
|
||||
func procMountInUse(podSpec *api.PodSpec) bool {
|
||||
if podSpec == nil {
|
||||
@@ -1863,20 +1806,6 @@ func hasUserNamespacesWithVolumeDevices(podSpec *api.PodSpec) bool {
|
||||
return hasVolumeDevices
|
||||
}
|
||||
|
||||
// hasRestartableInitContainerResizePolicy returns true if the pod spec is non-nil and
|
||||
// it has any init container with ContainerRestartPolicyAlways and non-nil ResizePolicy.
|
||||
func hasRestartableInitContainerResizePolicy(podSpec *api.PodSpec) bool {
|
||||
if podSpec == nil {
|
||||
return false
|
||||
}
|
||||
for _, c := range podSpec.InitContainers {
|
||||
if IsRestartableInitContainer(&c) && len(c.ResizePolicy) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// hasEmptyImageVolumeReference returns true if the pod spec has any image volume with an empty reference.
|
||||
func hasEmptyImageVolumeReference(podSpec *api.PodSpec) bool {
|
||||
if podSpec == nil {
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/component-base/featuregate"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
@@ -2564,145 +2563,6 @@ func TestDropAtomicWriteVolumeUserFields(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropInPlacePodVerticalScaling(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.34"))
|
||||
podWithInPlaceVerticalScaling := func() *api.Pod {
|
||||
return &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: "c1",
|
||||
Image: "image",
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
|
||||
Limits: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
|
||||
},
|
||||
ResizePolicy: []api.ContainerResizePolicy{
|
||||
{ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired},
|
||||
{ResourceName: api.ResourceMemory, RestartPolicy: api.RestartContainer},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
ContainerStatuses: []api.ContainerStatus{
|
||||
{
|
||||
Name: "c1",
|
||||
Image: "image",
|
||||
AllocatedResources: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
|
||||
Resources: &api.ResourceRequirements{
|
||||
Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
|
||||
Limits: api.ResourceList{api.ResourceCPU: resource.MustParse("300m")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
podWithoutInPlaceVerticalScaling := func() *api.Pod {
|
||||
return &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: "c1",
|
||||
Image: "image",
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
|
||||
Limits: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
ContainerStatuses: []api.ContainerStatus{
|
||||
{
|
||||
Name: "c1",
|
||||
Image: "image",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
podInfo := []struct {
|
||||
description string
|
||||
hasInPlaceVerticalScaling bool
|
||||
pod func() *api.Pod
|
||||
}{
|
||||
{
|
||||
description: "has in-place vertical scaling enabled with resources",
|
||||
hasInPlaceVerticalScaling: true,
|
||||
pod: podWithInPlaceVerticalScaling,
|
||||
},
|
||||
{
|
||||
description: "has in-place vertical scaling disabled",
|
||||
hasInPlaceVerticalScaling: false,
|
||||
pod: podWithoutInPlaceVerticalScaling,
|
||||
},
|
||||
{
|
||||
description: "is nil",
|
||||
hasInPlaceVerticalScaling: false,
|
||||
pod: func() *api.Pod { return nil },
|
||||
},
|
||||
}
|
||||
|
||||
for _, ippvsEnabled := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("InPlacePodVerticalScaling=%t", ippvsEnabled), func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, ippvsEnabled)
|
||||
|
||||
for _, oldPodInfo := range podInfo {
|
||||
for _, newPodInfo := range podInfo {
|
||||
oldPodHasInPlaceVerticalScaling, oldPod := oldPodInfo.hasInPlaceVerticalScaling, oldPodInfo.pod()
|
||||
newPodHasInPlaceVerticalScaling, newPod := newPodInfo.hasInPlaceVerticalScaling, newPodInfo.pod()
|
||||
if newPod == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
t.Run(fmt.Sprintf("old pod %v, new pod %v", oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
|
||||
var oldPodSpec *api.PodSpec
|
||||
var oldPodStatus *api.PodStatus
|
||||
if oldPod != nil {
|
||||
oldPodSpec = &oldPod.Spec
|
||||
oldPodStatus = &oldPod.Status
|
||||
}
|
||||
dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
|
||||
dropDisabledPodStatusFields(&newPod.Status, oldPodStatus, &newPod.Spec, oldPodSpec)
|
||||
|
||||
// old pod should never be changed
|
||||
if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
|
||||
t.Errorf("old pod changed: %v", cmp.Diff(oldPod, oldPodInfo.pod()))
|
||||
}
|
||||
|
||||
switch {
|
||||
case ippvsEnabled || oldPodHasInPlaceVerticalScaling:
|
||||
// new pod shouldn't change if feature enabled or if old pod has ResizePolicy set
|
||||
expected := newPodInfo.pod()
|
||||
if !reflect.DeepEqual(newPod, expected) {
|
||||
t.Errorf("new pod changed: %v", cmp.Diff(newPod, expected))
|
||||
}
|
||||
case newPodHasInPlaceVerticalScaling:
|
||||
// new pod should be changed
|
||||
if reflect.DeepEqual(newPod, newPodInfo.pod()) {
|
||||
t.Errorf("new pod was not changed")
|
||||
}
|
||||
// new pod should not have ResizePolicy
|
||||
if !reflect.DeepEqual(newPod, podWithoutInPlaceVerticalScaling()) {
|
||||
t.Errorf("new pod has ResizePolicy: %v", cmp.Diff(newPod, podWithoutInPlaceVerticalScaling()))
|
||||
}
|
||||
default:
|
||||
// new pod should not need to be changed
|
||||
if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
|
||||
t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropPodLevelResources(t *testing.T) {
|
||||
containers := []api.Container{
|
||||
{
|
||||
@@ -3783,178 +3643,6 @@ func TestDropSELinuxChangePolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAllowSidecarResizePolicy(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.34"))
|
||||
restartPolicyAlways := api.ContainerRestartPolicyAlways
|
||||
testCases := []struct {
|
||||
name string
|
||||
oldPodSpec *api.PodSpec
|
||||
wantOption bool
|
||||
}{
|
||||
{
|
||||
name: "old pod spec is nil",
|
||||
wantOption: false,
|
||||
},
|
||||
{
|
||||
name: "one sidecar container + one regular init container, no resize policy set on any of them",
|
||||
oldPodSpec: &api.PodSpec{
|
||||
InitContainers: []api.Container{
|
||||
{
|
||||
Name: "c1-restartable-init",
|
||||
Image: "image",
|
||||
RestartPolicy: &restartPolicyAlways,
|
||||
},
|
||||
{
|
||||
Name: "c1-init",
|
||||
Image: "image",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOption: false,
|
||||
},
|
||||
{
|
||||
name: "one sidecar container + one regular init container, resize policy set on regular init container",
|
||||
oldPodSpec: &api.PodSpec{
|
||||
InitContainers: []api.Container{
|
||||
{
|
||||
Name: "c1-restartable-init",
|
||||
Image: "image",
|
||||
RestartPolicy: &restartPolicyAlways,
|
||||
},
|
||||
{
|
||||
Name: "c1-init",
|
||||
Image: "image",
|
||||
ResizePolicy: []api.ContainerResizePolicy{
|
||||
{ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOption: false,
|
||||
},
|
||||
{
|
||||
name: "one sidecar container + one regular init container, resize policy set on sidecar container",
|
||||
oldPodSpec: &api.PodSpec{
|
||||
InitContainers: []api.Container{
|
||||
{
|
||||
Name: "c1-restartable-init",
|
||||
Image: "image",
|
||||
RestartPolicy: &restartPolicyAlways,
|
||||
ResizePolicy: []api.ContainerResizePolicy{
|
||||
{ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "c1-init",
|
||||
Image: "image",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOption: true,
|
||||
},
|
||||
{
|
||||
name: "one sidecar container + one regular init container, resize policy set on both of them",
|
||||
oldPodSpec: &api.PodSpec{
|
||||
InitContainers: []api.Container{
|
||||
{
|
||||
Name: "c1-restartable-init",
|
||||
Image: "image",
|
||||
RestartPolicy: &restartPolicyAlways,
|
||||
ResizePolicy: []api.ContainerResizePolicy{
|
||||
{ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "c1-init",
|
||||
Image: "image",
|
||||
ResizePolicy: []api.ContainerResizePolicy{
|
||||
{ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOption: true,
|
||||
},
|
||||
{
|
||||
name: "two sidecar containers, resize policy set on one of them",
|
||||
oldPodSpec: &api.PodSpec{
|
||||
InitContainers: []api.Container{
|
||||
{
|
||||
Name: "c1-restartable-init",
|
||||
Image: "image",
|
||||
RestartPolicy: &restartPolicyAlways,
|
||||
ResizePolicy: []api.ContainerResizePolicy{
|
||||
{ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "c2-restartable-init",
|
||||
Image: "image",
|
||||
RestartPolicy: &restartPolicyAlways,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOption: true,
|
||||
},
|
||||
{
|
||||
name: "two regular init containers, resize policy set on both of them",
|
||||
oldPodSpec: &api.PodSpec{
|
||||
InitContainers: []api.Container{
|
||||
{
|
||||
Name: "c1-init",
|
||||
Image: "image",
|
||||
ResizePolicy: []api.ContainerResizePolicy{
|
||||
{ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "c2-init",
|
||||
Image: "image",
|
||||
ResizePolicy: []api.ContainerResizePolicy{
|
||||
{ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOption: false,
|
||||
},
|
||||
{
|
||||
name: "two non-init containers, resize policy set on both of them",
|
||||
oldPodSpec: &api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: "c1",
|
||||
Image: "image",
|
||||
ResizePolicy: []api.ContainerResizePolicy{
|
||||
{ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "c2",
|
||||
Image: "image",
|
||||
ResizePolicy: []api.ContainerResizePolicy{
|
||||
{ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOption: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
for _, ippvsEnabled := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("%s/%t", tc.name, ippvsEnabled), func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, ippvsEnabled)
|
||||
|
||||
gotOptions := GetValidationOptionsFromPodSpecAndMeta(&api.PodSpec{}, tc.oldPodSpec, nil, nil)
|
||||
expected := tc.wantOption || ippvsEnabled
|
||||
assert.Equal(t, expected, gotOptions.AllowSidecarResizePolicy, "AllowSidecarResizePolicy")
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateInvalidLabelValueInNodeSelectorOption(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
||||
@@ -2873,7 +2873,6 @@ type Container struct {
|
||||
Resources ResourceRequirements
|
||||
// Resources resize policy for the container.
|
||||
// This field cannot be set on ephemeral containers.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
ResizePolicy []ContainerResizePolicy
|
||||
// RestartPolicy defines the restart behavior of individual containers in a pod.
|
||||
@@ -3238,7 +3237,6 @@ type ContainerStatus struct {
|
||||
AllocatedResources ResourceList
|
||||
// Resources represents the compute resource requests and limits that have been successfully
|
||||
// enacted on the running container after it has been started or has been successfully resized.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
Resources *ResourceRequirements
|
||||
// Status of volume mounts.
|
||||
@@ -4759,7 +4757,6 @@ type EphemeralContainerCommon struct {
|
||||
// +optional
|
||||
Resources ResourceRequirements
|
||||
// Resources resize policy for the container.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
ResizePolicy []ContainerResizePolicy
|
||||
// Restart policy for the container to manage the restart behavior of each
|
||||
@@ -4975,7 +4972,6 @@ type PodStatus struct {
|
||||
// Deprecated: Resize status is moved to two pod conditions PodResizePending and PodResizeInProgress.
|
||||
// PodResizePending will track states where the spec has been resized, but the Kubelet has not yet allocated the resources.
|
||||
// PodResizeInProgress will track in-progress resizes, and should be present whenever allocated resources != acknowledged resources.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
Resize PodResizeStatus
|
||||
|
||||
|
||||
@@ -3985,10 +3985,6 @@ func validateInitContainers(containers []core.Container, os *core.PodOS, regular
|
||||
}
|
||||
}
|
||||
|
||||
if !opts.AllowSidecarResizePolicy && len(ctr.ResizePolicy) > 0 {
|
||||
allErrs = append(allErrs, field.Invalid(idxPath.Child("resizePolicy"), ctr.ResizePolicy, "must not be set for init containers"))
|
||||
}
|
||||
|
||||
if !restartAlways && !opts.AllowExistingRestartContainerForNonSidecarInitContainer {
|
||||
for j, p := range ctr.ResizePolicy {
|
||||
if p.RestartPolicy == core.RestartContainer {
|
||||
@@ -4608,8 +4604,6 @@ type PodValidationOptions struct {
|
||||
// Indicates whether InPlacePodVerticalScalingMemoryBackedVolumes feature is enabled
|
||||
// or disabled.
|
||||
InPlacePodVerticalScalingMemoryBackedVolumesEnabled bool
|
||||
// Allow sidecar containers resize policy for backward compatibility
|
||||
AllowSidecarResizePolicy bool
|
||||
// Allow invalid label-value in RequiredNodeSelector
|
||||
AllowInvalidLabelValueInRequiredNodeAffinity bool
|
||||
// Allow feature of MatchLabelKeys in TopologySpreadConstraints
|
||||
@@ -6583,10 +6577,6 @@ func ValidatePodResize(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel
|
||||
allErrs = append(allErrs, field.Invalid(specPath, newPod.Status.QOSClass, "Pod QOS Class may not change as a result of resizing"))
|
||||
}
|
||||
|
||||
if !isPodResizeRequestSupported(*oldPod) {
|
||||
allErrs = append(allErrs, field.Forbidden(specPath, "Pod running on node without support for resize"))
|
||||
}
|
||||
|
||||
// Part 2: Validate that changes between pod-level resources in oldPod.Spec.Resources and
|
||||
// newPod.Spec.Resources are allowed.
|
||||
|
||||
@@ -6973,23 +6963,6 @@ func validatePodLevelResourcesCoverDRA(pod *v1.Pod) (bool, string) {
|
||||
return true, ""
|
||||
}
|
||||
|
||||
// isPodResizeRequestSupported checks whether the pod is running on a node with InPlacePodVerticalScaling enabled.
|
||||
func isPodResizeRequestSupported(pod core.Pod) bool {
|
||||
// TODO: Remove this after GA+3 releases of InPlacePodVerticalScaling
|
||||
// This code handles the version skew as described in the KEP.
|
||||
// For handling version skew we're only allowing to update the Pod's Resources
|
||||
// if the Pod already has Pod.Status.ContainerStatuses[i].Resources. This means
|
||||
// that the apiserver would only allow updates to Pods running on Nodes with
|
||||
// the InPlacePodVerticalScaling feature gate enabled.
|
||||
for _, c := range pod.Status.ContainerStatuses {
|
||||
if c.State.Running != nil {
|
||||
return c.Resources != nil
|
||||
}
|
||||
}
|
||||
// No running containers. We cannot tell whether the node supports resize at this point, so we assume it does.
|
||||
return true
|
||||
}
|
||||
|
||||
// validateContainerResize validates the changes to the container's resource requirements for a pod resize request.
|
||||
// newRequriements and oldRequirements must be non-nil.
|
||||
func validateContainerResize(newRequirements, oldRequirements *core.ResourceRequirements, resizePolicies []core.ContainerResizePolicy, fldPath *field.Path) field.ErrorList {
|
||||
|
||||
@@ -8752,7 +8752,6 @@ func TestValidatePullPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateResizePolicy(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
tSupportedResizeResources := sets.NewString(string(core.ResourceCPU), string(core.ResourceMemory))
|
||||
tSupportedResizePolicies := sets.NewString(string(core.NotRequired), string(core.RestartContainer))
|
||||
type T struct {
|
||||
@@ -10367,7 +10366,7 @@ func TestValidateInitContainers(t *testing.T) {
|
||||
}}
|
||||
|
||||
var PodRestartPolicy core.RestartPolicy = "Always"
|
||||
if errs := validateInitContainers(successCase, podOS, containers, volumeDevices, nil, defaultGracePeriod, field.NewPath("field"), PodValidationOptions{AllowSidecarResizePolicy: true, AllowExistingRestartContainerForNonSidecarInitContainer: true}, &PodRestartPolicy, noUserNamespace); len(errs) != 0 {
|
||||
if errs := validateInitContainers(successCase, podOS, containers, volumeDevices, nil, defaultGracePeriod, field.NewPath("field"), PodValidationOptions{AllowExistingRestartContainerForNonSidecarInitContainer: true}, &PodRestartPolicy, noUserNamespace); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
|
||||
@@ -10742,19 +10741,6 @@ func TestValidateInitContainers(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
field.ErrorList{{Type: field.ErrorTypeRequired, Field: "initContainers[0].lifecycle.preStop", BadValue: ""}},
|
||||
}, {
|
||||
"Not supported ResizePolicy: invalid",
|
||||
line(),
|
||||
[]core.Container{{
|
||||
Name: "init",
|
||||
Image: "image",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
TerminationMessagePolicy: "File",
|
||||
ResizePolicy: []core.ContainerResizePolicy{
|
||||
{ResourceName: "cpu", RestartPolicy: "NotRequired"},
|
||||
},
|
||||
}},
|
||||
field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "initContainers[0].resizePolicy", BadValue: []core.ContainerResizePolicy{{ResourceName: "cpu", RestartPolicy: "NotRequired"}}}},
|
||||
}, {
|
||||
"RestartContainer policy forbidden for non-sidecar init container",
|
||||
line(),
|
||||
@@ -10768,10 +10754,6 @@ func TestValidateInitContainers(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
field.ErrorList{{
|
||||
Type: field.ErrorTypeInvalid,
|
||||
Field: "initContainers[0].resizePolicy",
|
||||
BadValue: []core.ContainerResizePolicy{{ResourceName: core.ResourceCPU, RestartPolicy: core.RestartContainer}},
|
||||
}, {
|
||||
Type: field.ErrorTypeInvalid,
|
||||
Field: "initContainers[0].resizePolicy[0].restartPolicy",
|
||||
BadValue: core.RestartContainer,
|
||||
@@ -14665,7 +14647,7 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
),
|
||||
err: "spec.initContainers[0].resizePolicy[0].restartPolicy: Invalid value: \"RestartContainer\": must not be set to 'RestartContainer' for non-sidecar initContainers",
|
||||
test: "adding RestartContainer resize policy to non-sidecar init container should fail",
|
||||
opts: PodValidationOptions{AllowSidecarResizePolicy: true, AllowExistingRestartContainerForNonSidecarInitContainer: false},
|
||||
opts: PodValidationOptions{AllowExistingRestartContainerForNonSidecarInitContainer: false},
|
||||
}, {
|
||||
new: *podtest.MakePod("foo",
|
||||
podtest.SetInitContainers(core.Container{
|
||||
@@ -14681,7 +14663,7 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
),
|
||||
err: "",
|
||||
test: "existing RestartContainer resize policy on non-sidecar init container should be allowed on update",
|
||||
opts: PodValidationOptions{AllowSidecarResizePolicy: true, AllowExistingRestartContainerForNonSidecarInitContainer: true},
|
||||
opts: PodValidationOptions{AllowExistingRestartContainerForNonSidecarInitContainer: true},
|
||||
}, {
|
||||
new: *podtest.MakePod("foo",
|
||||
podtest.SetOS(core.Windows),
|
||||
@@ -30541,75 +30523,6 @@ func TestValidatePodResize(t *testing.T) {
|
||||
))),
|
||||
disableInitCtrResize: true,
|
||||
err: "Forbidden: resources for non-sidecar init containers are immutable",
|
||||
},
|
||||
|
||||
{
|
||||
test: "Pod with nil Resource field in Status",
|
||||
old: mkPod(core.ResourceList{}, getResources("100m", "0", "1Gi", ""), podtest.SetStatus(core.PodStatus{
|
||||
ContainerStatuses: []core.ContainerStatus{{
|
||||
ContainerID: "docker://numbers",
|
||||
Image: "nginx:alpine",
|
||||
Name: "main",
|
||||
Ready: true,
|
||||
Started: proto.Bool(true),
|
||||
Resources: nil,
|
||||
State: core.ContainerState{
|
||||
Running: &core.ContainerStateRunning{
|
||||
StartedAt: metav1.NewTime(time.Now()),
|
||||
},
|
||||
},
|
||||
}},
|
||||
})),
|
||||
new: mkPod(core.ResourceList{}, getResources("200m", "0", "1Gi", "")),
|
||||
err: "Forbidden: Pod running on node without support for resize",
|
||||
},
|
||||
{
|
||||
test: "Pod with non-nil Resources field in Status",
|
||||
old: mkPod(core.ResourceList{}, getResources("100m", "0", "1Gi", ""), podtest.SetStatus(core.PodStatus{
|
||||
ContainerStatuses: []core.ContainerStatus{{
|
||||
ContainerID: "docker://numbers",
|
||||
Image: "nginx:alpine",
|
||||
Name: "main",
|
||||
Ready: true,
|
||||
Started: proto.Bool(true),
|
||||
Resources: &core.ResourceRequirements{},
|
||||
State: core.ContainerState{
|
||||
Running: &core.ContainerStateRunning{
|
||||
StartedAt: metav1.NewTime(time.Now()),
|
||||
},
|
||||
},
|
||||
}},
|
||||
})),
|
||||
new: mkPod(core.ResourceList{}, getResources("200m", "0", "1Gi", "")),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
test: "Pod without running containers",
|
||||
old: mkPod(core.ResourceList{}, getResources("100m", "0", "1Gi", ""), podtest.SetStatus(core.PodStatus{
|
||||
ContainerStatuses: []core.ContainerStatus{},
|
||||
})),
|
||||
new: mkPod(core.ResourceList{}, getResources("200m", "0", "1Gi", "")),
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
test: "Pod with containers which are not running yet",
|
||||
old: mkPod(core.ResourceList{}, getResources("100m", "0", "1Gi", ""), podtest.SetStatus(core.PodStatus{
|
||||
ContainerStatuses: []core.ContainerStatus{{
|
||||
ContainerID: "docker://numbers",
|
||||
Image: "nginx:alpine",
|
||||
Name: "main",
|
||||
Ready: true,
|
||||
Started: proto.Bool(true),
|
||||
Resources: &core.ResourceRequirements{},
|
||||
State: core.ContainerState{
|
||||
Waiting: &core.ContainerStateWaiting{
|
||||
Reason: "PodInitializing",
|
||||
},
|
||||
},
|
||||
}},
|
||||
})),
|
||||
new: mkPod(core.ResourceList{}, getResources("200m", "0", "1Gi", "")),
|
||||
err: "",
|
||||
}, {
|
||||
test: "cpu limit change for sidecar containers",
|
||||
old: mkPodWithInitContainers(core.ResourceList{}, getResources("100m", "0", "1Gi", ""), core.ContainerRestartPolicyAlways),
|
||||
@@ -31396,7 +31309,6 @@ func TestValidatePodResize(t *testing.T) {
|
||||
}
|
||||
|
||||
errs := ValidatePodResize(test.new, test.old, PodValidationOptions{
|
||||
AllowSidecarResizePolicy: true,
|
||||
InPlacePodLevelResourcesVerticalScalingEnabled: true,
|
||||
PodLevelResourcesEnabled: true,
|
||||
InPlacePodVerticalScalingMemoryBackedVolumesEnabled: test.enableMemoryBackedVolumesResize,
|
||||
|
||||
@@ -521,23 +521,17 @@ const (
|
||||
// Enables specifying resources at pod-level.
|
||||
InPlacePodLevelResourcesVerticalScaling featuregate.Feature = "InPlacePodLevelResourcesVerticalScaling"
|
||||
|
||||
// owner: @vinaykul,@tallclair
|
||||
// kep: http://kep.k8s.io/1287
|
||||
//
|
||||
// Enables In-Place Pod Vertical Scaling
|
||||
InPlacePodVerticalScaling featuregate.Feature = "InPlacePodVerticalScaling"
|
||||
|
||||
// owner: @tallclair @esotsal
|
||||
//
|
||||
// Allow resource resize for containers in Guaranteed pods with integer CPU requests ( default false ).
|
||||
// Applies only in nodes with InPlacePodVerticalScaling and CPU Manager features enabled, and
|
||||
// Applies only in nodes with CPU Manager features enabled, and
|
||||
// CPU Manager Static Policy option set.
|
||||
InPlacePodVerticalScalingExclusiveCPUs featuregate.Feature = "InPlacePodVerticalScalingExclusiveCPUs"
|
||||
|
||||
// owner: @tallclair @pkrishn
|
||||
//
|
||||
// Allow memory resize for containers in Guaranteed pods (default false) when Memory Manager Policy is set to Static.
|
||||
// Applies only in nodes with InPlacePodVerticalScaling and Memory Manager features enabled.
|
||||
// Applies only in nodes with Memory Manager features enabled.
|
||||
InPlacePodVerticalScalingExclusiveMemory featuregate.Feature = "InPlacePodVerticalScalingExclusiveMemory"
|
||||
|
||||
// owner: @natasha41575
|
||||
@@ -1579,12 +1573,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
||||
{Version: version.MustParse("1.36"), Default: true, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
InPlacePodVerticalScaling: {
|
||||
{Version: version.MustParse("1.27"), Default: false, PreRelease: featuregate.Alpha},
|
||||
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.35"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.38
|
||||
},
|
||||
|
||||
InPlacePodVerticalScalingExclusiveCPUs: {
|
||||
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha},
|
||||
},
|
||||
@@ -2437,7 +2425,7 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature
|
||||
|
||||
DisableNodeKubeProxyVersion: {},
|
||||
|
||||
DynamicNodeLocalEphemeralVolumes: {InPlacePodVerticalScaling, NodeDeclaredFeatures},
|
||||
DynamicNodeLocalEphemeralVolumes: {NodeDeclaredFeatures},
|
||||
|
||||
EmptyDirVolumeMode: {},
|
||||
|
||||
@@ -2481,19 +2469,17 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature
|
||||
|
||||
ImageVolumeWithDigest: {ImageVolume},
|
||||
|
||||
InPlacePodLevelResourcesVerticalScaling: {InPlacePodVerticalScaling, PodLevelResources, NodeDeclaredFeatures},
|
||||
InPlacePodLevelResourcesVerticalScaling: {PodLevelResources, NodeDeclaredFeatures},
|
||||
|
||||
InPlacePodVerticalScaling: {},
|
||||
InPlacePodVerticalScalingExclusiveCPUs: {},
|
||||
|
||||
InPlacePodVerticalScalingExclusiveCPUs: {InPlacePodVerticalScaling},
|
||||
InPlacePodVerticalScalingExclusiveMemory: {},
|
||||
|
||||
InPlacePodVerticalScalingExclusiveMemory: {InPlacePodVerticalScaling},
|
||||
InPlacePodVerticalScalingInitContainers: {NodeDeclaredFeatures},
|
||||
|
||||
InPlacePodVerticalScalingInitContainers: {InPlacePodVerticalScaling, NodeDeclaredFeatures},
|
||||
InPlacePodVerticalScalingMemoryBackedVolumes: {NodeDeclaredFeatures},
|
||||
|
||||
InPlacePodVerticalScalingMemoryBackedVolumes: {InPlacePodVerticalScaling, NodeDeclaredFeatures},
|
||||
|
||||
InPlacePodVerticalScalingSchedulerPreemption: {InPlacePodVerticalScaling},
|
||||
InPlacePodVerticalScalingSchedulerPreemption: {},
|
||||
|
||||
InterPodAffinityHostnameFastPath: {},
|
||||
|
||||
@@ -2503,7 +2489,7 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature
|
||||
|
||||
KubeProxyNFTablesLocalhostNodePorts: {},
|
||||
|
||||
KubeletAllocatedPodsEndpoint: {InPlacePodVerticalScaling},
|
||||
KubeletAllocatedPodsEndpoint: {},
|
||||
|
||||
KubeletCgroupDriverFromCRI: {},
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ func DefaultOffAdmissionPlugins() sets.Set[string] {
|
||||
mutatingadmissionpolicy.PluginName, // Mutatingadmissionpolicy, only active when feature gate MutatingAdmissionpolicy is enabled
|
||||
validatingadmissionpolicy.PluginName, // ValidatingAdmissionPolicy, only active when feature gate ValidatingAdmissionPolicy is enabled
|
||||
nodedeclaredfeatures.PluginName, // NodeDeclaredFeatureValidator, only active when feature gate NodeDeclaredFeatures is enabled
|
||||
podresize.PluginName, // PodResizeValidator, only active when feature gate InPlacePodVerticalScaling is enabled
|
||||
podresize.PluginName, // PodResizeValidator
|
||||
conditionsenforcer.PluginName, // AuthorizationConditionsEnforcer, only active when feature gate ConditionalAuthorization is enabled
|
||||
)
|
||||
|
||||
|
||||
@@ -157,10 +157,6 @@ func NewManager(checkpointDirectory string,
|
||||
}
|
||||
|
||||
func newStateImpl(logger klog.Logger, checkpointDirectory, checkpointName string) state.State {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
return state.NewNoopStateCheckpoint()
|
||||
}
|
||||
|
||||
stateImpl, err := state.NewStateCheckpoint(logger, checkpointDirectory, checkpointName, nil)
|
||||
if err != nil {
|
||||
// This is a critical, non-recoverable failure.
|
||||
@@ -515,17 +511,15 @@ func (m *manager) AddPod(ctx context.Context, activePods []*v1.Pod, pod *v1.Pod)
|
||||
m.allocationMutex.Lock()
|
||||
defer m.allocationMutex.Unlock()
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
// To handle kubelet restarts, test pod admissibility using AllocatedResources values
|
||||
// (for cpu & memory) from checkpoint store. If found, that is the source of truth.
|
||||
pod, _ = m.UpdatePodFromAllocation(pod)
|
||||
}
|
||||
// To handle kubelet restarts, test pod admissibility using AllocatedResources values
|
||||
// (for cpu & memory) from checkpoint store. If found, that is the source of truth.
|
||||
pod, _ = m.UpdatePodFromAllocation(pod)
|
||||
|
||||
// Check if we can admit the pod; if so, update the allocation.
|
||||
allocatedPods := m.getAllocatedPods(activePods)
|
||||
ok, reason, message := m.canAdmitPod(ctx, allocatedPods, pod, lifecycle.AddOperation)
|
||||
|
||||
if ok && utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
if ok {
|
||||
// Checkpoint the resource values at which the Pod has been admitted or resized.
|
||||
if err := m.SetAllocatedResources(logger, pod); err != nil {
|
||||
// TODO(vinaykul,InPlacePodVerticalScaling): Can we recover from this in some way? Investigate
|
||||
@@ -652,10 +646,6 @@ func (m *manager) canAdmitPod(ctx context.Context, allocatedPods []*v1.Pod, pod
|
||||
}
|
||||
|
||||
func (m *manager) getAllocatedPods(activePods []*v1.Pod) []*v1.Pod {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
return activePods
|
||||
}
|
||||
|
||||
allocatedPods := make([]*v1.Pod, 0, len(activePods))
|
||||
for _, pod := range activePods {
|
||||
// Filter out pods that don't yet have an allocation, which will filter pods that
|
||||
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/tools/record"
|
||||
@@ -301,7 +300,6 @@ func TestUpdatePodFromAllocation(t *testing.T) {
|
||||
// of a container is allocated. A field that is added to the API fails it until it is classified
|
||||
// here and, if it is passthrough, copied in copyPassthroughFields.
|
||||
func TestUpdatePodFromAllocationFieldClassification(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodLevelResourcesVerticalScaling, true)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScalingMemoryBackedVolumes, true)
|
||||
|
||||
@@ -480,7 +478,6 @@ func TestUpdatePodFromAllocationFieldClassification(t *testing.T) {
|
||||
//
|
||||
// TODO: Remove in v1.39 when the V1 checkpoint format is no longer supported.
|
||||
func TestUpdatePodFromAllocationMigratedFromV1(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodLevelResourcesVerticalScaling, true)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScalingMemoryBackedVolumes, true)
|
||||
logger, ctx := ktesting.NewTestContext(t)
|
||||
@@ -588,8 +585,6 @@ func TestUpdatePodFromAllocationMigratedFromV1(t *testing.T) {
|
||||
//
|
||||
// TODO: Remove the checks of the overlay in v1.39 when the V1 checkpoint format is no longer supported.
|
||||
func TestUpdatePodFromAllocationPending(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
|
||||
cpu := func(quantity string) v1.ResourceRequirements {
|
||||
return v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse(quantity)}}
|
||||
}
|
||||
@@ -695,7 +690,6 @@ func TestRetryPendingResizes(t *testing.T) {
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
|
||||
containerRestartPolicyAlways := v1.ContainerRestartPolicyAlways
|
||||
@@ -1247,7 +1241,6 @@ func TestRetryPendingResizesGuanteedQOSPods(t *testing.T) {
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
|
||||
nodeConfig := cm.NodeConfig{}
|
||||
@@ -1487,7 +1480,6 @@ func TestRetryPendingResizesMemoryBackedVolumes(t *testing.T) {
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
tCtx := ktesting.Init(t)
|
||||
|
||||
logger := klog.FromContext(tCtx)
|
||||
@@ -1692,9 +1684,6 @@ func TestRetryPendingResizesWithSwap(t *testing.T) {
|
||||
metrics.Register()
|
||||
metrics.PodInfeasibleResizes.Reset()
|
||||
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.InPlacePodVerticalScaling: true,
|
||||
})
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
noSwapContainerName, swapContainerName := "test-container-noswap", "test-container-limitedswap"
|
||||
|
||||
@@ -1856,7 +1845,6 @@ func TestRetryPendingResizesMultipleConditions(t *testing.T) {
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
|
||||
cpu500m := resource.MustParse("500m")
|
||||
@@ -2052,7 +2040,6 @@ func TestRetryPendingResizesNonResourceChange(t *testing.T) {
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
|
||||
cpu := func(quantity string) v1.ResourceRequirements {
|
||||
return v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse(quantity)}}
|
||||
@@ -2527,7 +2514,6 @@ func TestAllocationManagerAddPod(t *testing.T) {
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
ipprFeatureGate bool
|
||||
initialAllocatedResourcesState map[types.UID]v1.ResourceList
|
||||
currentActivePods []*v1.Pod
|
||||
podToAdd *v1.Pod
|
||||
@@ -2538,8 +2524,7 @@ func TestAllocationManagerAddPod(t *testing.T) {
|
||||
expectedAllocatedResourcesState map[types.UID]v1.ResourceList
|
||||
}{
|
||||
{
|
||||
name: "IPPR Enabled - New pod admitted and allocated resources updated",
|
||||
ipprFeatureGate: true,
|
||||
name: "New pod admitted and allocated resources updated",
|
||||
initialAllocatedResourcesState: map[types.UID]v1.ResourceList{},
|
||||
currentActivePods: []*v1.Pod{},
|
||||
podToAdd: pod1Small,
|
||||
@@ -2549,18 +2534,7 @@ func TestAllocationManagerAddPod(t *testing.T) {
|
||||
expectedAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu1Mem1G},
|
||||
},
|
||||
{
|
||||
name: "IPPR Disabled - New pod admitted but allocated resources not updated",
|
||||
ipprFeatureGate: false,
|
||||
initialAllocatedResourcesState: map[types.UID]v1.ResourceList{},
|
||||
currentActivePods: []*v1.Pod{},
|
||||
podToAdd: pod1Small,
|
||||
admitFunc: nil,
|
||||
expectAdmit: true,
|
||||
expectedAllocatedResourcesState: map[types.UID]v1.ResourceList{},
|
||||
},
|
||||
{
|
||||
name: "IPPR Enabled - New pod not admititted due to insufficient resources",
|
||||
ipprFeatureGate: true,
|
||||
name: "New pod not admititted due to insufficient resources",
|
||||
initialAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu1Mem1G},
|
||||
currentActivePods: []*v1.Pod{pod1Small},
|
||||
podToAdd: pod2Large,
|
||||
@@ -2582,31 +2556,7 @@ func TestAllocationManagerAddPod(t *testing.T) {
|
||||
expectedAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu1Mem1G},
|
||||
},
|
||||
{
|
||||
name: "IPPR Disabled - New pod not admitted due to insufficient resources",
|
||||
ipprFeatureGate: false,
|
||||
initialAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu1Mem1G},
|
||||
currentActivePods: []*v1.Pod{pod1Small},
|
||||
podToAdd: pod2Large,
|
||||
admitFunc: func(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult {
|
||||
cpuRequest := attrs.Pod.Spec.Containers[0].Resources.Requests.Cpu().Value()
|
||||
if cpuRequest > 1 {
|
||||
return lifecycle.PodAdmitResult{
|
||||
Admit: false,
|
||||
Reason: "OutOfcpu",
|
||||
Message: fmt.Sprintf("not enough CPUs available for pod %s/%s, requested: %d, available:1", attrs.Pod.Namespace, attrs.Pod.Name, cpuRequest),
|
||||
}
|
||||
}
|
||||
return lifecycle.PodAdmitResult{Admit: true}
|
||||
},
|
||||
expectAdmit: false,
|
||||
admissionFailureReason: "OutOfcpu",
|
||||
admissionFailureMessage: "not enough CPUs available for pod ns2/pod2, requested: 2, available:1",
|
||||
// allocated resources not modified
|
||||
expectedAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu1Mem1G},
|
||||
},
|
||||
{
|
||||
name: "IPPR Enabled - no pod resize request. Resource request same as existing allocation",
|
||||
ipprFeatureGate: true,
|
||||
name: "no pod resize request. Resource request same as existing allocation",
|
||||
initialAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu1Mem1G, pod2UID: cpu1Mem1G},
|
||||
currentActivePods: []*v1.Pod{pod1Small},
|
||||
podToAdd: pod1Small,
|
||||
@@ -2615,8 +2565,7 @@ func TestAllocationManagerAddPod(t *testing.T) {
|
||||
expectedAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu1Mem1G, pod2UID: cpu1Mem1G},
|
||||
},
|
||||
{
|
||||
name: "IPPR Enabled - current allocation not found for added pod. Allocated resources updated.",
|
||||
ipprFeatureGate: true,
|
||||
name: "current allocation not found for added pod. Allocated resources updated.",
|
||||
initialAllocatedResourcesState: map[types.UID]v1.ResourceList{},
|
||||
currentActivePods: []*v1.Pod{pod1Small},
|
||||
podToAdd: pod1Large,
|
||||
@@ -2626,8 +2575,7 @@ func TestAllocationManagerAddPod(t *testing.T) {
|
||||
expectedAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu2Mem2G},
|
||||
},
|
||||
{
|
||||
name: "IPPR Enabled - request different from current allocation. Pod still admitted based on existing allocation, but allocated resources remains unchanges.",
|
||||
ipprFeatureGate: true,
|
||||
name: "request different from current allocation. Pod still admitted based on existing allocation, but allocated resources remains unchanges.",
|
||||
initialAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu1Mem1G, pod2UID: cpu1Mem1G},
|
||||
currentActivePods: []*v1.Pod{pod1Small, pod2Small},
|
||||
podToAdd: pod1Large,
|
||||
@@ -2647,37 +2595,11 @@ func TestAllocationManagerAddPod(t *testing.T) {
|
||||
// allocated Resources state must not be updated.
|
||||
expectedAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu1Mem1G, pod2UID: cpu1Mem1G},
|
||||
},
|
||||
{
|
||||
name: "IPPR Disabled - request different from current allocation. Admission fails. Allocated resources not updated.",
|
||||
ipprFeatureGate: false,
|
||||
initialAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu1Mem1G},
|
||||
currentActivePods: []*v1.Pod{pod1Small},
|
||||
podToAdd: pod1Large,
|
||||
admitFunc: func(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult {
|
||||
cpuRequest := attrs.Pod.Spec.Containers[0].Resources.Requests.Cpu().Value()
|
||||
if cpuRequest > 1 {
|
||||
return lifecycle.PodAdmitResult{
|
||||
Admit: false,
|
||||
Reason: "OutOfcpu",
|
||||
Message: fmt.Sprintf("not enough CPUs available for pod %s/%s, requested: %d, available:1", attrs.Pod.Namespace, attrs.Pod.Name, cpuRequest),
|
||||
}
|
||||
}
|
||||
return lifecycle.PodAdmitResult{Admit: true}
|
||||
},
|
||||
// pod is still admitted as allocated resources are considered during admission.
|
||||
expectAdmit: false,
|
||||
admissionFailureReason: "OutOfcpu",
|
||||
admissionFailureMessage: "not enough CPUs available for pod ns1/pod1, requested: 2, available:1",
|
||||
// allocated Resources state must not be updated.
|
||||
expectedAllocatedResourcesState: map[types.UID]v1.ResourceList{pod1UID: cpu1Mem1G},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.34"))
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tCtx := ktesting.Init(t)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, tc.ipprFeatureGate)
|
||||
allocationManager := makeAllocationManager(t, &containertest.FakeRuntime{}, []*v1.Pod{}, nil)
|
||||
|
||||
podForAllocation := func(uid types.UID, resources v1.ResourceList) *v1.Pod {
|
||||
@@ -3118,7 +3040,6 @@ func TestAllocationManager_EmptyDirVolumeLimits_AddPod(t *testing.T) {
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
tCtx := ktesting.Init(t)
|
||||
|
||||
tests := []struct {
|
||||
@@ -3313,7 +3234,6 @@ func TestAllocationManager_EmptyDirVolumeLimits_UpdatePodFromAllocation(t *testi
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -3518,7 +3438,6 @@ func TestAllocationManager_EmptyDirVolumeLimits_RetryPendingResizes(t *testing.T
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
|
||||
tests := []struct {
|
||||
@@ -4166,7 +4085,6 @@ func setupNonAllocatedCapacityTest(t *testing.T) (Manager, *v1.Pod, *v1.Pod, klo
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeDeclaredFeatures, true)
|
||||
logger, _ := ktesting.NewTestContext(t)
|
||||
|
||||
@@ -4250,7 +4168,6 @@ func TestRetryPendingResizesTerminatingPods(t *testing.T) {
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
|
||||
cpu100m := resource.MustParse("100m")
|
||||
cpu200m := resource.MustParse("200m")
|
||||
|
||||
@@ -25,9 +25,6 @@ import (
|
||||
)
|
||||
|
||||
func IsInPlacePodVerticalScalingAllowed(pod *v1.Pod) (allowed bool, msg, reason string) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
return false, "InPlacePodVerticalScaling is disabled", "feature_gate_off"
|
||||
}
|
||||
return true, "", ""
|
||||
}
|
||||
|
||||
|
||||
+88
-112
@@ -2114,19 +2114,17 @@ func (kl *Kubelet) SyncPod(ctx context.Context, updateType kubetypes.SyncPodType
|
||||
}
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
// Check whether a resize is in progress so we can set the PodResizeInProgressCondition accordingly.
|
||||
if kl.containerRuntime.IsPodResizeInProgress(pod, podStatus) {
|
||||
kl.statusManager.SetPodResizeInProgressCondition(pod.UID, "", "", pod.Generation)
|
||||
} else if generation, cleared := kl.statusManager.ClearPodResizeInProgressCondition(pod.UID); cleared {
|
||||
// (Allocated == Actual) => clear the resize in-progress status.
|
||||
msg := events.PodResizeCompletedMsg(logger, pod, generation)
|
||||
kl.recorder.WithLogger(logger).Eventf(pod, v1.EventTypeNormal, events.ResizeCompleted, "%s", msg)
|
||||
}
|
||||
// TODO(natasha41575): There is a race condition here, where the goroutine in the
|
||||
// allocation manager may allocate a new resize and unconditionally set the
|
||||
// PodResizeInProgressCondition before we set the status below.
|
||||
// Check whether a resize is in progress so we can set the PodResizeInProgressCondition accordingly.
|
||||
if kl.containerRuntime.IsPodResizeInProgress(pod, podStatus) {
|
||||
kl.statusManager.SetPodResizeInProgressCondition(pod.UID, "", "", pod.Generation)
|
||||
} else if generation, cleared := kl.statusManager.ClearPodResizeInProgressCondition(pod.UID); cleared {
|
||||
// (Allocated == Actual) => clear the resize in-progress status.
|
||||
msg := events.PodResizeCompletedMsg(logger, pod, generation)
|
||||
kl.recorder.WithLogger(logger).Eventf(pod, v1.EventTypeNormal, events.ResizeCompleted, "%s", msg)
|
||||
}
|
||||
// TODO(natasha41575): There is a race condition here, where the goroutine in the
|
||||
// allocation manager may allocate a new resize and unconditionally set the
|
||||
// PodResizeInProgressCondition before we set the status below.
|
||||
|
||||
// Generate final API pod status with pod and status manager status
|
||||
apiPodStatus := kl.generateAPIPodStatus(ctx, pod, podStatus, SyncPod)
|
||||
@@ -2315,14 +2313,12 @@ func (kl *Kubelet) SyncPod(ctx context.Context, updateType kubetypes.SyncPodType
|
||||
}
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
for _, r := range result.SyncResults {
|
||||
if r.Action == kubecontainer.ResizePodInPlace && r.Error != nil {
|
||||
// If the condition already exists, the observedGeneration does not get updated.
|
||||
if generation, updated := kl.statusManager.SetPodResizeInProgressCondition(pod.UID, v1.PodReasonError, r.Message, pod.Generation); updated {
|
||||
msg := events.PodResizeErrorMsg(logger, pod, generation, r.Message)
|
||||
kl.recorder.WithLogger(logger).Eventf(pod, v1.EventTypeWarning, events.ResizeError, "%s", msg)
|
||||
}
|
||||
for _, r := range result.SyncResults {
|
||||
if r.Action == kubecontainer.ResizePodInPlace && r.Error != nil {
|
||||
// If the condition already exists, the observedGeneration does not get updated.
|
||||
if generation, updated := kl.statusManager.SetPodResizeInProgressCondition(pod.UID, v1.PodReasonError, r.Message, pod.Generation); updated {
|
||||
msg := events.PodResizeErrorMsg(logger, pod, generation, r.Message)
|
||||
kl.recorder.WithLogger(logger).Eventf(pod, v1.EventTypeWarning, events.ResizeError, "%s", msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2965,14 +2961,12 @@ func (kl *Kubelet) HandlePodAdditions(ctx context.Context, pods []*v1.Pod) {
|
||||
|
||||
kl.podCertificateManager.TrackPod(ctx, pod)
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
// Backfill the queue of pending resizes, but only after all the pods have
|
||||
// been added. This ensures that no resizes get resolved until all the
|
||||
// existing pods are added.
|
||||
_, updatedFromAllocation := kl.allocationManager.UpdatePodFromAllocation(pod)
|
||||
if updatedFromAllocation {
|
||||
pendingResizes = append(pendingResizes, pod.UID)
|
||||
}
|
||||
// Backfill the queue of pending resizes, but only after all the pods have
|
||||
// been added. This ensures that no resizes get resolved until all the
|
||||
// existing pods are added.
|
||||
_, updatedFromAllocation := kl.allocationManager.UpdatePodFromAllocation(pod)
|
||||
if updatedFromAllocation {
|
||||
pendingResizes = append(pendingResizes, pod.UID)
|
||||
}
|
||||
}
|
||||
kl.podWorkers.UpdatePod(ctx, UpdatePodOptions{
|
||||
@@ -2982,16 +2976,14 @@ func (kl *Kubelet) HandlePodAdditions(ctx context.Context, pods []*v1.Pod) {
|
||||
StartTime: start,
|
||||
})
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
kl.statusManager.BackfillPodResizeConditions(pods)
|
||||
// Admitted pods with DeletionTimestamp set are still pushed here so RetryPendingResizes
|
||||
// clears any backfilled PodResizePending condition with DeferredResizeResolutionTerminated.
|
||||
for _, uid := range pendingResizes {
|
||||
kl.allocationManager.PushPendingResize(logger, uid)
|
||||
}
|
||||
if len(pendingResizes) > 0 {
|
||||
kl.allocationManager.RetryPendingResizes(ctx, allocation.TriggerReasonPodsAdded)
|
||||
}
|
||||
kl.statusManager.BackfillPodResizeConditions(pods)
|
||||
// Admitted pods with DeletionTimestamp set are still pushed here so RetryPendingResizes
|
||||
// clears any backfilled PodResizePending condition with DeferredResizeResolutionTerminated.
|
||||
for _, uid := range pendingResizes {
|
||||
kl.allocationManager.PushPendingResize(logger, uid)
|
||||
}
|
||||
if len(pendingResizes) > 0 {
|
||||
kl.allocationManager.RetryPendingResizes(ctx, allocation.TriggerReasonPodsAdded)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3004,13 +2996,11 @@ func (kl *Kubelet) HandlePodUpdates(ctx context.Context, pods []*v1.Pod) {
|
||||
oldPod, _ := kl.podManager.GetPodByUID(pod.UID)
|
||||
kl.podManager.UpdatePod(pod)
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
// Skip pods that haven't been allocated yet to avoid counting them against
|
||||
// node capacity before they've been admitted.
|
||||
if !kl.allocationManager.HasPodAllocatedResources(pod.UID) {
|
||||
logger.V(4).Info("Skipping pod update for non-allocated pod", "pod", klog.KObj(pod), "podUID", pod.UID)
|
||||
continue
|
||||
}
|
||||
// Skip pods that haven't been allocated yet to avoid counting them against
|
||||
// node capacity before they've been admitted.
|
||||
if !kl.allocationManager.HasPodAllocatedResources(pod.UID) {
|
||||
logger.V(4).Info("Skipping pod update for non-allocated pod", "pod", klog.KObj(pod), "podUID", pod.UID)
|
||||
continue
|
||||
}
|
||||
|
||||
pod, mirrorPod, wasMirror := kl.podManager.GetPodAndMirrorPod(pod)
|
||||
@@ -3021,21 +3011,19 @@ func (kl *Kubelet) HandlePodUpdates(ctx context.Context, pods []*v1.Pod) {
|
||||
}
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
recordResizeOperations(oldPod, pod)
|
||||
_, updatedFromAllocation := kl.allocationManager.UpdatePodFromAllocation(pod)
|
||||
if updatedFromAllocation {
|
||||
if !kl.IsPodTerminatingOrTerminal(pod) {
|
||||
kl.allocationManager.PushPendingResize(logger, pod.UID)
|
||||
}
|
||||
// TODO(natasha41575): If the resize is immediately actuated, it will trigger a pod sync
|
||||
// and we will end up calling UpdatePod twice. Figure out if there is a way to avoid this.
|
||||
kl.allocationManager.RetryPendingResizes(ctx, allocation.TriggerReasonPodUpdated)
|
||||
} else {
|
||||
// We can hit this case if a pending resize has been reverted,
|
||||
// so we need to clear the pending resize condition.
|
||||
kl.statusManager.ClearPodResizePendingCondition(pod.UID, metrics.DeferredResizeResolutionReverted)
|
||||
recordResizeOperations(oldPod, pod)
|
||||
_, updatedFromAllocation := kl.allocationManager.UpdatePodFromAllocation(pod)
|
||||
if updatedFromAllocation {
|
||||
if !kl.IsPodTerminatingOrTerminal(pod) {
|
||||
kl.allocationManager.PushPendingResize(logger, pod.UID)
|
||||
}
|
||||
// TODO(natasha41575): If the resize is immediately actuated, it will trigger a pod sync
|
||||
// and we will end up calling UpdatePod twice. Figure out if there is a way to avoid this.
|
||||
kl.allocationManager.RetryPendingResizes(ctx, allocation.TriggerReasonPodUpdated)
|
||||
} else {
|
||||
// We can hit this case if a pending resize has been reverted,
|
||||
// so we need to clear the pending resize condition.
|
||||
kl.statusManager.ClearPodResizePendingCondition(pod.UID, metrics.DeferredResizeResolutionReverted)
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.NodeDeclaredFeatures) && oldPod != nil {
|
||||
@@ -3082,10 +3070,6 @@ func recordResizeOperations(oldPod, newPod *v1.Pod) {
|
||||
|
||||
// recordPodLevelResourceResizeOperations records if any of the pod level resources need to be resized.
|
||||
func recordPodLevelResourceResizeOperations(oldPod, newPod *v1.Pod) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(ndixita): add metrics for pod-level resources resize.
|
||||
}
|
||||
|
||||
@@ -3182,9 +3166,7 @@ func (kl *Kubelet) HandlePodRemoves(ctx context.Context, pods []*v1.Pod) {
|
||||
}
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
kl.allocationManager.RetryPendingResizes(ctx, allocation.TriggerReasonPodsRemoved)
|
||||
}
|
||||
kl.allocationManager.RetryPendingResizes(ctx, allocation.TriggerReasonPodsRemoved)
|
||||
}
|
||||
|
||||
// HandlePodReconcile is the callback in the SyncHandler interface for pods
|
||||
@@ -3211,46 +3193,44 @@ func (kl *Kubelet) HandlePodReconcile(ctx context.Context, pods []*v1.Pod) {
|
||||
// Static pods should be reconciled the same way as regular pods
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
if hasPendingResizes && !retryPendingResizes && oldPod != nil {
|
||||
// If the pod has reached a terminal phase, we retry all pending resizes.
|
||||
// A terminated pod releases capacity even if its allocation has already
|
||||
// been purged, so check this before the non-allocated skip below.
|
||||
if podutil.IsPodTerminal(pod) && !podutil.IsPodTerminal(oldPod) {
|
||||
retryPendingResizes = true
|
||||
triggerReason = allocation.TriggerReasonPodTerminated
|
||||
}
|
||||
|
||||
// If there are pending resizes, check whether the requests shrank as a result of the status
|
||||
// resources changing.
|
||||
opts := resourcehelper.PodResourcesOptions{
|
||||
UseStatusResources: true,
|
||||
SkipPodLevelResources: !utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources),
|
||||
InPlacePodLevelResourcesVerticalScalingEnabled: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodLevelResourcesVerticalScaling),
|
||||
UseDRANodeAllocatableResourceClaimStatus: utilfeature.DefaultFeatureGate.Enabled(features.DRANodeAllocatableResources),
|
||||
}
|
||||
|
||||
// Ignore desired resources when aggregating the resources.
|
||||
allocatedOldPod, _ := kl.allocationManager.UpdatePodFromAllocation(oldPod)
|
||||
allocatedPod, _ := kl.allocationManager.UpdatePodFromAllocation(pod)
|
||||
|
||||
oldRequest := resourcehelper.PodRequests(allocatedOldPod, opts)
|
||||
newRequest := resourcehelper.PodRequests(allocatedPod, opts)
|
||||
|
||||
// If cpu or memory requests shrank, then retry the pending resizes.
|
||||
if newRequest.Memory().Cmp(*oldRequest.Memory()) < 0 ||
|
||||
newRequest.Cpu().Cmp(*oldRequest.Cpu()) < 0 {
|
||||
retryPendingResizes = true
|
||||
triggerReason = allocation.TriggerReasonPodResized
|
||||
}
|
||||
if hasPendingResizes && !retryPendingResizes && oldPod != nil {
|
||||
// If the pod has reached a terminal phase, we retry all pending resizes.
|
||||
// A terminated pod releases capacity even if its allocation has already
|
||||
// been purged, so check this before the non-allocated skip below.
|
||||
if podutil.IsPodTerminal(pod) && !podutil.IsPodTerminal(oldPod) {
|
||||
retryPendingResizes = true
|
||||
triggerReason = allocation.TriggerReasonPodTerminated
|
||||
}
|
||||
|
||||
// Skip further reconciliation for pods that haven't been allocated yet.
|
||||
// We still updated podManager above to keep status in sync with the API server.
|
||||
if !kl.allocationManager.HasPodAllocatedResources(pod.UID) {
|
||||
logger.V(4).Info("Skipping pod reconcile operations for non-allocated pod", "pod", klog.KObj(pod), "podUID", pod.UID)
|
||||
continue
|
||||
// If there are pending resizes, check whether the requests shrank as a result of the status
|
||||
// resources changing.
|
||||
opts := resourcehelper.PodResourcesOptions{
|
||||
UseStatusResources: true,
|
||||
SkipPodLevelResources: !utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources),
|
||||
InPlacePodLevelResourcesVerticalScalingEnabled: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodLevelResourcesVerticalScaling),
|
||||
UseDRANodeAllocatableResourceClaimStatus: utilfeature.DefaultFeatureGate.Enabled(features.DRANodeAllocatableResources),
|
||||
}
|
||||
|
||||
// Ignore desired resources when aggregating the resources.
|
||||
allocatedOldPod, _ := kl.allocationManager.UpdatePodFromAllocation(oldPod)
|
||||
allocatedPod, _ := kl.allocationManager.UpdatePodFromAllocation(pod)
|
||||
|
||||
oldRequest := resourcehelper.PodRequests(allocatedOldPod, opts)
|
||||
newRequest := resourcehelper.PodRequests(allocatedPod, opts)
|
||||
|
||||
// If cpu or memory requests shrank, then retry the pending resizes.
|
||||
if newRequest.Memory().Cmp(*oldRequest.Memory()) < 0 ||
|
||||
newRequest.Cpu().Cmp(*oldRequest.Cpu()) < 0 {
|
||||
retryPendingResizes = true
|
||||
triggerReason = allocation.TriggerReasonPodResized
|
||||
}
|
||||
}
|
||||
|
||||
// Skip further reconciliation for pods that haven't been allocated yet.
|
||||
// We still updated podManager above to keep status in sync with the API server.
|
||||
if !kl.allocationManager.HasPodAllocatedResources(pod.UID) {
|
||||
logger.V(4).Info("Skipping pod reconcile operations for non-allocated pod", "pod", klog.KObj(pod), "podUID", pod.UID)
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO: reconcile being calculated in the config manager is questionable, and avoiding
|
||||
@@ -3282,10 +3262,8 @@ func (kl *Kubelet) HandlePodReconcile(ctx context.Context, pods []*v1.Pod) {
|
||||
}
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
if retryPendingResizes {
|
||||
kl.allocationManager.RetryPendingResizes(ctx, triggerReason)
|
||||
}
|
||||
if retryPendingResizes {
|
||||
kl.allocationManager.RetryPendingResizes(ctx, triggerReason)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3701,12 +3679,10 @@ func (a *allocatedPodManager) getAllocatedPod(pod *v1.Pod) (*v1.Pod, bool) {
|
||||
if pod == nil {
|
||||
return nil, false
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
if allocatedPod, ok := a.allocationManager.UpdatePodFromAllocation(pod); ok {
|
||||
return allocatedPod, true
|
||||
}
|
||||
if allocatedPod, ok := a.allocationManager.UpdatePodFromAllocation(pod); ok {
|
||||
return allocatedPod, true
|
||||
}
|
||||
// When the feature gate is disabled or there is no allocation stored for this pod, return the pod as-is.
|
||||
// When there is no allocation stored for this pod, return the pod as-is.
|
||||
return pod, true
|
||||
}
|
||||
|
||||
|
||||
+20
-28
@@ -245,10 +245,6 @@ func (kl *Kubelet) GetActivePods() []*v1.Pod {
|
||||
// allocated state.
|
||||
func (kl *Kubelet) getAllocatedPods() []*v1.Pod {
|
||||
activePods := kl.GetActivePods()
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
return activePods
|
||||
}
|
||||
|
||||
allocatedPods := make([]*v1.Pod, len(activePods))
|
||||
for i, pod := range activePods {
|
||||
allocatedPods[i], _ = kl.allocationManager.UpdatePodFromAllocation(pod)
|
||||
@@ -2024,18 +2020,16 @@ func (kl *Kubelet) generateAPIPodStatus(ctx context.Context, pod *v1.Pod, podSta
|
||||
s.Conditions = append(s.Conditions, c)
|
||||
}
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
resizeStatus := kl.determinePodResizeStatus(pod, podState)
|
||||
for _, c := range resizeStatus {
|
||||
// Clear the condition's observed generation if BOTH The FG is disabled AND the condition's
|
||||
// observed generation is not already set. We avoid overwriting the condition's observedGeneration
|
||||
// in other cases, because the condition may be reflecting an older podspec.
|
||||
gen := podutil.CalculatePodConditionObservedGeneration(&oldPodStatus, pod.Generation, c.Type)
|
||||
if gen == 0 {
|
||||
c.ObservedGeneration = 0
|
||||
}
|
||||
s.Conditions = append(s.Conditions, *c)
|
||||
resizeStatus := kl.determinePodResizeStatus(pod, podState)
|
||||
for _, c := range resizeStatus {
|
||||
// Clear the condition's observed generation if BOTH The FG is disabled AND the condition's
|
||||
// observed generation is not already set. We avoid overwriting the condition's observedGeneration
|
||||
// in other cases, because the condition may be reflecting an older podspec.
|
||||
gen := podutil.CalculatePodConditionObservedGeneration(&oldPodStatus, pod.Generation, c.Type)
|
||||
if gen == 0 {
|
||||
c.ObservedGeneration = 0
|
||||
}
|
||||
s.Conditions = append(s.Conditions, *c)
|
||||
}
|
||||
|
||||
// copy over the pod disruption conditions from state which is already
|
||||
@@ -2752,19 +2746,17 @@ func (kl *Kubelet) convertToAPIContainerStatuses(ctx context.Context, pod *v1.Po
|
||||
}
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
allocatedContainer := kubecontainer.GetContainerSpec(pod, cName)
|
||||
if allocatedContainer != nil {
|
||||
// status.Resources reflects cgroup-actuated resources. If the container is running,
|
||||
// it is populated from CRI status.
|
||||
status.Resources = convertContainerStatusResources(allocatedContainer, status, cStatus, oldStatuses)
|
||||
// status.AllocatedResources represents the desired cgroup resource state for resize,
|
||||
// and is strictly based on the uninflated standard cgroup resources requested in the spec.
|
||||
// DRA based allocations are not included in AllocatedResources at the container level.
|
||||
// This is because the claims can be shared across containers in the pod and can be
|
||||
// shared unevenly (enforced by the DRA driver).
|
||||
status.AllocatedResources = allocatedContainer.Resources.Requests
|
||||
}
|
||||
allocatedContainer := kubecontainer.GetContainerSpec(pod, cName)
|
||||
if allocatedContainer != nil {
|
||||
// status.Resources reflects cgroup-actuated resources. If the container is running,
|
||||
// it is populated from CRI status.
|
||||
status.Resources = convertContainerStatusResources(allocatedContainer, status, cStatus, oldStatuses)
|
||||
// status.AllocatedResources represents the desired cgroup resource state for resize,
|
||||
// and is strictly based on the uninflated standard cgroup resources requested in the spec.
|
||||
// DRA based allocations are not included in AllocatedResources at the container level.
|
||||
// This is because the claims can be shared across containers in the pod and can be
|
||||
// shared unevenly (enforced by the DRA driver).
|
||||
status.AllocatedResources = allocatedContainer.Resources.Requests
|
||||
}
|
||||
|
||||
status.User = convertContainerStatusUser(cStatus)
|
||||
|
||||
@@ -6202,7 +6202,6 @@ func Test_generateAPIPodStatusForInPlaceVPAEnabled(t *testing.T) {
|
||||
if goruntime.GOOS != "linux" {
|
||||
t.Skip("InPlacePodVerticalScaling cgroup resource reporting is only supported on Linux")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
testContainerName := "ctr0"
|
||||
testContainerID := kubecontainer.ContainerID{Type: "test", ID: testContainerName}
|
||||
|
||||
@@ -7024,7 +7023,6 @@ func TestConvertToAPIContainerStatusesForResources(t *testing.T) {
|
||||
if goruntime.GOOS != "linux" {
|
||||
t.Skip("InPlacePodVerticalScaling cgroup resource reporting is only supported on Linux")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
|
||||
nowTime := time.Now()
|
||||
testContainerName := "ctr0"
|
||||
@@ -10175,8 +10173,6 @@ func TestMakeMountsBindMountOptions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateAPIPodStatusResizeConditionsByPodWorkerState(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
podState PodWorkerState
|
||||
|
||||
@@ -2855,7 +2855,6 @@ func TestHandlePodAdditionsTracksCertificatesOnlyForAdmittedPods(t *testing.T) {
|
||||
func TestPodResourceAllocationReset(t *testing.T) {
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
testKubelet := newTestKubelet(t, false)
|
||||
defer testKubelet.Cleanup()
|
||||
kubelet := testKubelet.kubelet
|
||||
@@ -5215,7 +5214,6 @@ func TestHandlePodUpdates_VolumeResize(t *testing.T) {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScalingMemoryBackedVolumes, true)
|
||||
tCtx := ktesting.Init(t)
|
||||
|
||||
@@ -5456,7 +5454,6 @@ func TestHandlePodReconcile_RetryPendingResizes(t *testing.T) {
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRANodeAllocatableResources, true)
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
|
||||
@@ -6000,7 +5997,6 @@ func TestHandlePodUpdates_ResizeTerminatingPods(t *testing.T) {
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported for Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
metrics.Register()
|
||||
|
||||
const podUID types.UID = "pod-1"
|
||||
|
||||
@@ -701,25 +701,22 @@ func (m *kubeGenericRuntimeManager) getPodContainerStatuses(ctx context.Context,
|
||||
func (m *kubeGenericRuntimeManager) toKubeContainerStatus(ctx context.Context, podUID kubetypes.UID, status *runtimeapi.ContainerStatus, runtimeName string) *kubecontainer.Status {
|
||||
annotatedInfo := getContainerInfoFromAnnotations(ctx, status.Annotations)
|
||||
labeledInfo := getContainerInfoFromLabels(ctx, status.Labels)
|
||||
var cStatusResources *kubecontainer.ContainerResources
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
// If runtime reports cpu & memory resources info, add it to container status
|
||||
cStatusResources = toKubeContainerResources(status.Resources)
|
||||
// If runtime reports cpu & memory resources info, add it to container status
|
||||
cStatusResources := toKubeContainerResources(status.Resources)
|
||||
|
||||
// Fill missing resource values from the actuated resources.
|
||||
if cStatusResources == nil || cStatusResources.MemoryRequest == nil {
|
||||
if actuatedResources, ok := m.actuatedState.GetContainerResources(podUID, labeledInfo.ContainerName); ok {
|
||||
if cStatusResources == nil {
|
||||
cStatusResources = &kubecontainer.ContainerResources{
|
||||
CPURequest: actuatedResources.Requests.Cpu(),
|
||||
CPULimit: actuatedResources.Limits.Cpu(),
|
||||
MemoryRequest: actuatedResources.Requests.Memory(),
|
||||
MemoryLimit: actuatedResources.Limits.Memory(),
|
||||
}
|
||||
} else if cStatusResources.MemoryRequest == nil {
|
||||
if memReq := actuatedResources.Requests[v1.ResourceMemory]; !memReq.IsZero() {
|
||||
cStatusResources.MemoryRequest = &memReq
|
||||
}
|
||||
// Fill missing resource values from the actuated resources.
|
||||
if cStatusResources == nil || cStatusResources.MemoryRequest == nil {
|
||||
if actuatedResources, ok := m.actuatedState.GetContainerResources(podUID, labeledInfo.ContainerName); ok {
|
||||
if cStatusResources == nil {
|
||||
cStatusResources = &kubecontainer.ContainerResources{
|
||||
CPURequest: actuatedResources.Requests.Cpu(),
|
||||
CPULimit: actuatedResources.Limits.Cpu(),
|
||||
MemoryRequest: actuatedResources.Requests.Memory(),
|
||||
MemoryLimit: actuatedResources.Limits.Memory(),
|
||||
}
|
||||
} else if cStatusResources.MemoryRequest == nil {
|
||||
if memReq := actuatedResources.Requests[v1.ResourceMemory]; !memReq.IsZero() {
|
||||
cStatusResources.MemoryRequest = &memReq
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,6 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||
@@ -42,7 +40,6 @@ import (
|
||||
apitest "k8s.io/cri-api/pkg/apis/testing"
|
||||
kubelettypes "k8s.io/kubelet/pkg/types"
|
||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
@@ -316,7 +313,6 @@ func TestToKubeContainerStatusWithResources(t *testing.T) {
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is not currently supported on Windows.")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
|
||||
const (
|
||||
|
||||
@@ -831,9 +831,6 @@ func (m *kubeGenericRuntimeManager) computePodResizeAction(ctx context.Context,
|
||||
// TODO: Move InitializeActuatedPod, UpdateActuatedPodLevelResources, and IsPodResizeInProgress
|
||||
// out of the runtime now that actuatedState is owned by the Kubelet.
|
||||
func (m *kubeGenericRuntimeManager) InitializeActuatedPod(logger klog.Logger, allocatedPod *v1.Pod) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
return
|
||||
}
|
||||
stored, hasPod := m.actuatedState.GetPod(allocatedPod.UID)
|
||||
// TODO: In v1.39, when the V1 checkpoint format is no longer supported, we will still need to
|
||||
// check stored.Name == "" (and preserve stored.Spec.Resources and stored.Spec.Volumes below)
|
||||
@@ -2371,11 +2368,6 @@ func (m *kubeGenericRuntimeManager) ListPodSandboxMetrics(ctx context.Context) (
|
||||
}
|
||||
|
||||
func (m *kubeGenericRuntimeManager) UpdateActuatedPodLevelResources(logger klog.Logger, actuatedPod *v1.Pod) error {
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if actuatedPod.Spec.Resources == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3118,7 +3118,6 @@ func TestComputePodActionsForPodResize(t *testing.T) {
|
||||
if goruntime.GOOS == "windows" {
|
||||
t.Skip("InPlacePodVerticalScaling is currently not supported on Windows")
|
||||
}
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
_, _, m, err := createTestRuntimeManager(tCtx)
|
||||
@@ -4041,7 +4040,6 @@ func TestComputePodResizeActionForOOMKilledContainer(t *testing.T) {
|
||||
t.Skip("in-place resize is only supported on Linux")
|
||||
}
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
_, _, m, err := createTestRuntimeManager(tCtx)
|
||||
m.machineInfo.MemoryCapacity = 17179860387 // 16GB
|
||||
require.NoError(t, err)
|
||||
@@ -4088,7 +4086,6 @@ func TestComputePodResizeActionForOOMKilledInitContainer(t *testing.T) {
|
||||
t.Skip("in-place resize is only supported on Linux")
|
||||
}
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
_, _, m, err := createTestRuntimeManager(tCtx)
|
||||
m.machineInfo.MemoryCapacity = 17179860387 // 16GB
|
||||
require.NoError(t, err)
|
||||
@@ -4149,7 +4146,6 @@ func TestComputePodResizeActionForOOMKilledSidecarContainer(t *testing.T) {
|
||||
t.Skip("in-place resize is only supported on Linux")
|
||||
}
|
||||
logger, tCtx := ktesting.NewTestContext(t)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
_, _, m, err := createTestRuntimeManager(tCtx)
|
||||
m.machineInfo.MemoryCapacity = 17179860387 // 16GB
|
||||
require.NoError(t, err)
|
||||
@@ -4223,7 +4219,6 @@ func TestUpdatePodContainerResources(t *testing.T) {
|
||||
}
|
||||
|
||||
tCtx := ktesting.Init(t)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
fakeRuntime, _, m, err := createTestRuntimeManager(tCtx)
|
||||
m.machineInfo.MemoryCapacity = 17179860387 // 16GB
|
||||
m.cpuCFSQuota = true
|
||||
@@ -4519,7 +4514,6 @@ func TestDoPodResizeAction(t *testing.T) {
|
||||
|
||||
tCtx := ktesting.Init(t)
|
||||
logger := tCtx.Logger()
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
metrics.Register()
|
||||
metrics.PodResizeDurationMilliseconds.Reset()
|
||||
|
||||
@@ -7173,7 +7167,6 @@ func TestSyncResultErrorsMetric(t *testing.T) {
|
||||
func TestInitializeActuatedPod_PreservesActuatedVolumes(t *testing.T) {
|
||||
tCtx := ktesting.Init(t)
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.InPlacePodVerticalScaling: true,
|
||||
features.InPlacePodLevelResourcesVerticalScaling: true,
|
||||
features.InPlacePodVerticalScalingMemoryBackedVolumes: true,
|
||||
})
|
||||
|
||||
@@ -23,10 +23,7 @@ import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
"k8s.io/ktesting"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
)
|
||||
|
||||
@@ -163,7 +160,6 @@ func TestContainerAnnotations(t *testing.T) {
|
||||
PreStopHandler: container.Lifecycle.PreStop,
|
||||
}
|
||||
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
// Test whether we can get right information from label
|
||||
annotations := newContainerAnnotations(tCtx, container, pod, restartCount, opts)
|
||||
containerInfo := getContainerInfoFromAnnotations(tCtx, annotations)
|
||||
|
||||
@@ -1598,15 +1598,13 @@ func Register() {
|
||||
legacyregistry.MustRegister(ImageVolumeMountedErrorsTotal)
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
legacyregistry.MustRegister(ContainerRequestedResizes)
|
||||
legacyregistry.MustRegister(PodResizeDurationMilliseconds)
|
||||
legacyregistry.MustRegister(PodPendingResizes)
|
||||
legacyregistry.MustRegister(PodInfeasibleResizes)
|
||||
legacyregistry.MustRegister(PodInProgressResizes)
|
||||
legacyregistry.MustRegister(PodDeferredAcceptedResizes)
|
||||
legacyregistry.MustRegister(PodDeferredResizeDurationSeconds)
|
||||
}
|
||||
legacyregistry.MustRegister(ContainerRequestedResizes)
|
||||
legacyregistry.MustRegister(PodResizeDurationMilliseconds)
|
||||
legacyregistry.MustRegister(PodPendingResizes)
|
||||
legacyregistry.MustRegister(PodInfeasibleResizes)
|
||||
legacyregistry.MustRegister(PodInProgressResizes)
|
||||
legacyregistry.MustRegister(PodDeferredAcceptedResizes)
|
||||
legacyregistry.MustRegister(PodDeferredResizeDurationSeconds)
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResourceManagers) {
|
||||
legacyregistry.MustRegister(ResourceManagerAllocationsTotal)
|
||||
|
||||
@@ -29,11 +29,9 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/tools/record"
|
||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/allocation"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/events"
|
||||
@@ -998,9 +996,7 @@ func (p *podWorkers) UpdatePod(ctx context.Context, options UpdatePodOptions) {
|
||||
|
||||
// notify the pod worker there is a pending update
|
||||
status.pendingUpdate = &options
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
status.pendingUpdate.Pod, _ = p.allocationManager.UpdatePodFromAllocation(options.Pod)
|
||||
}
|
||||
status.pendingUpdate.Pod, _ = p.allocationManager.UpdatePodFromAllocation(options.Pod)
|
||||
status.working = true
|
||||
updateLogger.V(4).Info("Notifying pod of pending update", "workType", status.WorkType())
|
||||
select {
|
||||
|
||||
@@ -1133,13 +1133,11 @@ func (m *manager) deletePodStatus(uid types.UID) {
|
||||
defer m.podStatusesLock.Unlock()
|
||||
delete(m.podStatuses, uid)
|
||||
m.podStartupLatencyHelper.DeletePodStartupState(uid)
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
if conds, exists := m.podResizeConditions[uid]; exists {
|
||||
m.observeDeferredResizeDuration(uid, conds.PodResizePending, metrics.DeferredResizeResolutionTerminated)
|
||||
delete(m.podResizeConditions, uid)
|
||||
m.recordInProgressResizeCount()
|
||||
m.observePendingResizeCount()
|
||||
}
|
||||
if conds, exists := m.podResizeConditions[uid]; exists {
|
||||
m.observeDeferredResizeDuration(uid, conds.PodResizePending, metrics.DeferredResizeResolutionTerminated)
|
||||
delete(m.podResizeConditions, uid)
|
||||
m.recordInProgressResizeCount()
|
||||
m.observePendingResizeCount()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1151,13 +1149,11 @@ func (m *manager) RemoveOrphanedStatuses(logger klog.Logger, podUIDs map[types.U
|
||||
if _, ok := podUIDs[key]; !ok {
|
||||
logger.V(5).Info("Removing pod from status map", "podUID", key)
|
||||
delete(m.podStatuses, key)
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
if conds, exists := m.podResizeConditions[key]; exists {
|
||||
m.observeDeferredResizeDuration(key, conds.PodResizePending, metrics.DeferredResizeResolutionTerminated)
|
||||
delete(m.podResizeConditions, key)
|
||||
m.recordInProgressResizeCount()
|
||||
m.observePendingResizeCount()
|
||||
}
|
||||
if conds, exists := m.podResizeConditions[key]; exists {
|
||||
m.observeDeferredResizeDuration(key, conds.PodResizePending, metrics.DeferredResizeResolutionTerminated)
|
||||
delete(m.podResizeConditions, key)
|
||||
m.recordInProgressResizeCount()
|
||||
m.observePendingResizeCount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@ func PodUsageFunc(obj runtime.Object, clock clock.Clock) (corev1.ResourceList, e
|
||||
}
|
||||
|
||||
opts := resourcehelper.PodResourcesOptions{
|
||||
UseStatusResources: feature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling),
|
||||
UseStatusResources: true,
|
||||
// SkipPodLevelResources is set to false when PodLevelResources feature is enabled.
|
||||
SkipPodLevelResources: !feature.DefaultFeatureGate.Enabled(features.PodLevelResources),
|
||||
}
|
||||
|
||||
@@ -20,9 +20,7 @@ import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/quota/v1/evaluator/core"
|
||||
"k8s.io/utils/clock"
|
||||
)
|
||||
@@ -35,7 +33,7 @@ func DefaultUpdateFilter() func(resource schema.GroupVersionResource, oldObj, ne
|
||||
oldPod := oldObj.(*v1.Pod)
|
||||
newPod := newObj.(*v1.Pod)
|
||||
// when Resources changed
|
||||
if feature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) && hasResourcesChanged(oldPod, newPod) {
|
||||
if hasResourcesChanged(oldPod, newPod) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -4932,34 +4932,7 @@ func TestStatusPrepareForUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "drop disabled status fields/InPlacePodVerticalScaling=false",
|
||||
features: map[featuregate.Feature]bool{
|
||||
features.InPlacePodVerticalScaling: false,
|
||||
},
|
||||
oldPod: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
|
||||
Status: api.PodStatus{},
|
||||
},
|
||||
newPod: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
|
||||
Status: api.PodStatus{
|
||||
ContainerStatuses: []api.ContainerStatus{
|
||||
{Resources: &api.ResourceRequirements{}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
|
||||
Status: api.PodStatus{
|
||||
ContainerStatuses: []api.ContainerStatus{{}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "drop disabled status fields/InPlacePodVerticalScaling=true",
|
||||
features: map[featuregate.Feature]bool{
|
||||
features.InPlacePodVerticalScaling: true,
|
||||
},
|
||||
description: "preserve container status resources",
|
||||
oldPod: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
|
||||
Status: api.PodStatus{},
|
||||
@@ -5500,9 +5473,6 @@ func TestStatusPrepareForUpdate(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
if v, ok := tc.features[features.InPlacePodVerticalScaling]; ok && !v {
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.34"))
|
||||
}
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, tc.features)
|
||||
StatusStrategy.PrepareForUpdate(genericapirequest.NewContext(), tc.newPod, tc.oldPod)
|
||||
if !cmp.Equal(tc.expected, tc.newPod) {
|
||||
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/apiserver/pkg/util/proxy"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
admissionregistrationv1client "k8s.io/client-go/kubernetes/typed/admissionregistration/v1"
|
||||
@@ -42,7 +41,6 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/cluster/ports"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||
"k8s.io/kubernetes/pkg/registry/core/componentstatus"
|
||||
endpointsstore "k8s.io/kubernetes/pkg/registry/core/endpoint/storage"
|
||||
@@ -245,9 +243,7 @@ func (p *legacyProvider) NewRESTStorage(apiResourceConfigSource serverstorage.AP
|
||||
storage[resource+"/eviction"] = podStorage.Eviction
|
||||
}
|
||||
storage[resource+"/ephemeralcontainers"] = podStorage.EphemeralContainers
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
storage[resource+"/resize"] = podStorage.Resize
|
||||
}
|
||||
storage[resource+"/resize"] = podStorage.Resize
|
||||
}
|
||||
if resource := "bindings"; apiResourceConfigSource.ResourceEnabled(corev1.SchemeGroupVersion.WithResource(resource)) {
|
||||
storage[resource] = podStorage.LegacyBinding
|
||||
|
||||
@@ -107,7 +107,7 @@ type podChangeExtractor func(newPod *v1.Pod, oldPod *v1.Pod) fwk.ActionType
|
||||
// extractPodScaleDown interprets the update of a pod and returns PodRequestScaledDown event if any pod's resource request(s) is scaled down.
|
||||
func extractPodScaleDown(newPod, oldPod *v1.Pod) fwk.ActionType {
|
||||
opt := resource.PodResourcesOptions{
|
||||
UseStatusResources: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling),
|
||||
UseStatusResources: true,
|
||||
InPlacePodLevelResourcesVerticalScalingEnabled: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodLevelResourcesVerticalScaling),
|
||||
}
|
||||
newPodRequests := resource.PodRequests(newPod, opt)
|
||||
@@ -132,7 +132,7 @@ func extractPodScaleDown(newPod, oldPod *v1.Pod) fwk.ActionType {
|
||||
// extractPodScaleUp interprets the update of a pod and returns PodRequestScaledUp event if any pod's resource request(s) is scaled up.
|
||||
func extractPodScaleUp(newPod, oldPod *v1.Pod) fwk.ActionType {
|
||||
opt := resource.PodResourcesOptions{
|
||||
UseStatusResources: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling),
|
||||
UseStatusResources: true,
|
||||
InPlacePodLevelResourcesVerticalScalingEnabled: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodLevelResourcesVerticalScaling),
|
||||
}
|
||||
newPodRequests := resource.PodRequests(newPod, opt)
|
||||
|
||||
@@ -45,7 +45,6 @@ type Features struct {
|
||||
EnableVolumeLimitScaling bool
|
||||
EnableNodeInclusionPolicyInPodTopologySpread bool
|
||||
EnableMatchLabelKeysInPodTopologySpread bool
|
||||
EnableInPlacePodVerticalScaling bool
|
||||
EnableAsyncPreemption bool
|
||||
EnablePodLevelResources bool
|
||||
EnableStorageCapacityScoring bool
|
||||
@@ -81,7 +80,6 @@ func NewSchedulerFeaturesFromGates(featureGate featuregate.FeatureGate) Features
|
||||
EnableVolumeLimitScaling: featureGate.Enabled(features.VolumeLimitScaling),
|
||||
EnableNodeInclusionPolicyInPodTopologySpread: featureGate.Enabled(features.NodeInclusionPolicyInPodTopologySpread),
|
||||
EnableMatchLabelKeysInPodTopologySpread: featureGate.Enabled(features.MatchLabelKeysInPodTopologySpread),
|
||||
EnableInPlacePodVerticalScaling: featureGate.Enabled(features.InPlacePodVerticalScaling),
|
||||
EnableAsyncPreemption: featureGate.Enabled(features.SchedulerAsyncPreemption),
|
||||
EnablePodLevelResources: featureGate.Enabled(features.PodLevelResources),
|
||||
EnableDRAPartitionableDevices: featureGate.Enabled(features.DRAPartitionableDevices),
|
||||
|
||||
@@ -88,7 +88,7 @@ func ShouldDelegateResourceToDRA(rName v1.ResourceName, nodeInfo fwk.NodeInfo, d
|
||||
//
|
||||
// Result: CPU: 3, Memory: 3G
|
||||
func ComputePodResourceRequest(pod *v1.Pod, opts ResourceRequestsOptions) framework.Resource {
|
||||
// pod hasn't scheduled yet so we don't need to worry about InPlacePodVerticalScalingEnabled
|
||||
// pod hasn't scheduled yet so we don't need to worry about UseStatusResources
|
||||
reqs := resource.PodRequests(pod, resource.PodResourcesOptions{
|
||||
// SkipPodLevelResources is set to false when PodLevelResources feature is enabled.
|
||||
SkipPodLevelResources: !opts.EnablePodLevelResources,
|
||||
|
||||
@@ -189,13 +189,12 @@ func NewBalancedAllocation(_ context.Context, baArgs runtime.Object, h fwk.Handl
|
||||
return &BalancedAllocation{
|
||||
handle: h,
|
||||
resourceAllocationScorer: resourceAllocationScorer{
|
||||
Name: BalancedAllocationName,
|
||||
enableInPlacePodVerticalScaling: fts.EnableInPlacePodVerticalScaling,
|
||||
enablePodLevelResources: fts.EnablePodLevelResources,
|
||||
enableDRAExtendedResource: fts.EnableDRAExtendedResource,
|
||||
scorer: balancedResourceScorer,
|
||||
useRequested: true,
|
||||
resources: args.Resources,
|
||||
Name: BalancedAllocationName,
|
||||
enablePodLevelResources: fts.EnablePodLevelResources,
|
||||
enableDRAExtendedResource: fts.EnableDRAExtendedResource,
|
||||
scorer: balancedResourceScorer,
|
||||
useRequested: true,
|
||||
resources: args.Resources,
|
||||
enableInPlacePodLevelResourcesVerticalScaling: fts.EnableInPlacePodLevelResourcesVerticalScaling,
|
||||
draManager: h.SharedDRAManager(),
|
||||
},
|
||||
|
||||
@@ -92,7 +92,6 @@ var nodeResourceStrategyTypeMap = map[config.ScoringStrategyType]scorer{
|
||||
type Fit struct {
|
||||
ignoredResources sets.Set[string]
|
||||
ignoredResourceGroups sets.Set[string]
|
||||
enableInPlacePodVerticalScaling bool
|
||||
enablePodLevelResources bool
|
||||
enableDRAExtendedResource bool
|
||||
enableInPlacePodLevelResourcesVerticalScaling bool
|
||||
@@ -203,7 +202,6 @@ func getScorer(strategy *config.ScoringStrategy, fts feature.Features) (*resourc
|
||||
return nil, fmt.Errorf("scoring strategy %s is not supported", strategy.Type)
|
||||
}
|
||||
scorer := scorerFactory(strategy)
|
||||
scorer.enableInPlacePodVerticalScaling = fts.EnableInPlacePodVerticalScaling
|
||||
scorer.enablePodLevelResources = fts.EnablePodLevelResources
|
||||
scorer.enableInPlacePodLevelResourcesVerticalScaling = fts.EnableInPlacePodLevelResourcesVerticalScaling
|
||||
return scorer, nil
|
||||
@@ -236,12 +234,11 @@ func NewFit(_ context.Context, plArgs runtime.Object, h fwk.Handle, fts feature.
|
||||
}
|
||||
|
||||
pl := &Fit{
|
||||
ignoredResources: sets.New(args.IgnoredResources...),
|
||||
ignoredResourceGroups: sets.New(args.IgnoredResourceGroups...),
|
||||
enableInPlacePodVerticalScaling: fts.EnableInPlacePodVerticalScaling,
|
||||
handle: h,
|
||||
enablePodLevelResources: fts.EnablePodLevelResources,
|
||||
enableDRAExtendedResource: fts.EnableDRAExtendedResource,
|
||||
ignoredResources: sets.New(args.IgnoredResources...),
|
||||
ignoredResourceGroups: sets.New(args.IgnoredResourceGroups...),
|
||||
handle: h,
|
||||
enablePodLevelResources: fts.EnablePodLevelResources,
|
||||
enableDRAExtendedResource: fts.EnableDRAExtendedResource,
|
||||
|
||||
enableInPlacePodLevelResourcesVerticalScaling: fts.EnableInPlacePodLevelResourcesVerticalScaling,
|
||||
|
||||
@@ -313,19 +310,14 @@ func (f *Fit) EventsToRegister(_ context.Context) ([]fwk.ClusterEventWithHint, e
|
||||
events := []fwk.ClusterEventWithHint{
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.AssignedPod, ActionType: fwk.Delete}, QueueingHintFn: f.isSchedulableAfterAssignedPodDelete},
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.Node, ActionType: fwk.Add | fwk.UpdateNodeAllocatable}, QueueingHintFn: f.isSchedulableAfterNodeChange},
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.AssignedPod, ActionType: fwk.UpdatePodScaleDown}, QueueingHintFn: f.isSchedulableAfterAssignedPodScaleDown},
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.TargetPod, ActionType: fwk.UpdatePodScaleDown}, QueueingHintFn: f.isSchedulableAfterTargetPodScaleDown},
|
||||
}
|
||||
if f.enableDRAExtendedResource {
|
||||
events = append(events,
|
||||
// A pod might be waiting for an exteneded resurce from a class to get created or modified.
|
||||
fwk.ClusterEventWithHint{Event: fwk.ClusterEvent{Resource: fwk.DeviceClass, ActionType: fwk.Add | fwk.Update}, QueueingHintFn: f.isSchedulableAfterDeviceClassEvent})
|
||||
}
|
||||
if f.enableInPlacePodVerticalScaling {
|
||||
// If InPlacePodVerticalScaling (KEP 1287) is enabled, then UpdatePodScaleDown event should be registered
|
||||
// for this plugin since a Pod update may free up resources that make other Pods schedulable.
|
||||
events = append(events,
|
||||
fwk.ClusterEventWithHint{Event: fwk.ClusterEvent{Resource: fwk.AssignedPod, ActionType: fwk.UpdatePodScaleDown}, QueueingHintFn: f.isSchedulableAfterAssignedPodScaleDown},
|
||||
fwk.ClusterEventWithHint{Event: fwk.ClusterEvent{Resource: fwk.TargetPod, ActionType: fwk.UpdatePodScaleDown}, QueueingHintFn: f.isSchedulableAfterTargetPodScaleDown})
|
||||
}
|
||||
if f.enableInPlacePodVerticalScalingSchedulerPreemption {
|
||||
events = append(events,
|
||||
fwk.ClusterEventWithHint{Event: fwk.ClusterEvent{Resource: fwk.AssignedPod, ActionType: fwk.UpdatePodScaleUp}, QueueingHintFn: f.isSchedulableAfterAssignedPodScaleUp},
|
||||
@@ -436,7 +428,7 @@ func (f *Fit) haveEnoughRelevantResourcesDecreased(targetPod *v1.Pod, originalPo
|
||||
|
||||
// the other pod was scheduled, so modification or deletion may free up some resources.
|
||||
originalMaxResourceReq, modifiedMaxResourceReq := &framework.Resource{}, &framework.Resource{}
|
||||
opts := resource.PodResourcesOptions{UseStatusResources: f.enableInPlacePodVerticalScaling, InPlacePodLevelResourcesVerticalScalingEnabled: f.enableInPlacePodLevelResourcesVerticalScaling}
|
||||
opts := resource.PodResourcesOptions{UseStatusResources: true, InPlacePodLevelResourcesVerticalScalingEnabled: f.enableInPlacePodLevelResourcesVerticalScaling}
|
||||
originalMaxResourceReq.SetMaxResource(resource.PodRequests(originalPod, opts))
|
||||
modifiedMaxResourceReq.SetMaxResource(resource.PodRequests(modifiedPod, opts))
|
||||
|
||||
|
||||
@@ -1459,14 +1459,12 @@ func BenchmarkTestFitScore(b *testing.B) {
|
||||
|
||||
func TestEventsToRegister(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
enableInPlacePodVerticalScaling bool
|
||||
enableDRAExtendedResource bool
|
||||
expectedClusterEvents []fwk.ClusterEventWithHint
|
||||
name string
|
||||
enableDRAExtendedResource bool
|
||||
expectedClusterEvents []fwk.ClusterEventWithHint
|
||||
}{
|
||||
{
|
||||
name: "Register events with InPlacePodVerticalScaling feature enabled",
|
||||
enableInPlacePodVerticalScaling: true,
|
||||
name: "Register events with default features",
|
||||
expectedClusterEvents: []fwk.ClusterEventWithHint{
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.AssignedPod, ActionType: fwk.Delete}},
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.Node, ActionType: fwk.Add | fwk.UpdateNodeAllocatable}},
|
||||
@@ -1474,19 +1472,14 @@ func TestEventsToRegister(t *testing.T) {
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.TargetPod, ActionType: fwk.UpdatePodScaleDown}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Register events with default features",
|
||||
expectedClusterEvents: []fwk.ClusterEventWithHint{
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.AssignedPod, ActionType: fwk.Delete}},
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.Node, ActionType: fwk.Add | fwk.UpdateNodeAllocatable}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Register events with DRAExtendedResource feature enabled",
|
||||
enableDRAExtendedResource: true,
|
||||
expectedClusterEvents: []fwk.ClusterEventWithHint{
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.AssignedPod, ActionType: fwk.Delete}},
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.Node, ActionType: fwk.Add | fwk.UpdateNodeAllocatable}},
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.AssignedPod, ActionType: fwk.UpdatePodScaleDown}},
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.TargetPod, ActionType: fwk.UpdatePodScaleDown}},
|
||||
{Event: fwk.ClusterEvent{Resource: fwk.DeviceClass, ActionType: fwk.Add | fwk.Update}},
|
||||
},
|
||||
},
|
||||
@@ -1494,7 +1487,7 @@ func TestEventsToRegister(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
fp := &Fit{enableInPlacePodVerticalScaling: test.enableInPlacePodVerticalScaling, enableDRAExtendedResource: test.enableDRAExtendedResource}
|
||||
fp := &Fit{enableDRAExtendedResource: test.enableDRAExtendedResource}
|
||||
_, ctx := ktesting.NewTestContext(t)
|
||||
actualClusterEvents, err := fp.EventsToRegister(ctx)
|
||||
if err != nil {
|
||||
@@ -1594,9 +1587,7 @@ func Test_isSchedulableAfterTargetPodScaleDown(t *testing.T) {
|
||||
for name, tc := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
logger, ctx := ktesting.NewTestContext(t)
|
||||
p, err := NewFit(ctx, &config.NodeResourcesFitArgs{ScoringStrategy: defaultScoringStrategy}, nil, plfeature.Features{
|
||||
EnableInPlacePodVerticalScaling: true,
|
||||
})
|
||||
p, err := NewFit(ctx, &config.NodeResourcesFitArgs{ScoringStrategy: defaultScoringStrategy}, nil, plfeature.Features{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1678,7 +1669,6 @@ func Test_isSchedulableAfterAssignedPodScaleDown(t *testing.T) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
logger, ctx := ktesting.NewTestContext(t)
|
||||
p, err := NewFit(ctx, &config.NodeResourcesFitArgs{ScoringStrategy: defaultScoringStrategy}, nil, plfeature.Features{
|
||||
EnableInPlacePodVerticalScaling: true,
|
||||
EnableInPlacePodVerticalScalingSchedulerPreemption: true,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -3042,7 +3032,7 @@ func TestScorePlacement_Resources(t *testing.T) {
|
||||
expectedScore: 30,
|
||||
},
|
||||
{
|
||||
name: "Ignores container status resources when InPlacePodVerticalScaling is disabled",
|
||||
name: "Honors container status resources",
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
@@ -3056,25 +3046,6 @@ func TestScorePlacement_Resources(t *testing.T) {
|
||||
{Name: "cpu", Weight: 1},
|
||||
{Name: "memory", Weight: 1},
|
||||
},
|
||||
features: plfeature.Features{EnableInPlacePodVerticalScaling: false},
|
||||
expectedScore: 10,
|
||||
},
|
||||
{
|
||||
name: "Honors container status resources when InPlacePodVerticalScaling is enabled",
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
},
|
||||
placementNodeNames: []string{"node1", "node2"},
|
||||
podGroupPods: []*v1.Pod{
|
||||
withContainerStatusResources(st.MakePod().UID("foo").Req(cpuAndMemory("1000m", "1000")).Obj(), cpuAndMemory("4000m", "4000")),
|
||||
},
|
||||
podGroupAssignments: map[types.UID]string{"foo": "node1"},
|
||||
resourceSpec: []config.ResourceSpec{
|
||||
{Name: "cpu", Weight: 1},
|
||||
{Name: "memory", Weight: 1},
|
||||
},
|
||||
features: plfeature.Features{EnableInPlacePodVerticalScaling: true},
|
||||
expectedScore: 40,
|
||||
},
|
||||
{
|
||||
@@ -3097,7 +3068,6 @@ func TestScorePlacement_Resources(t *testing.T) {
|
||||
},
|
||||
features: plfeature.Features{
|
||||
EnablePodLevelResources: true,
|
||||
EnableInPlacePodVerticalScaling: true,
|
||||
EnableInPlacePodLevelResourcesVerticalScaling: false,
|
||||
},
|
||||
expectedScore: 10,
|
||||
@@ -3122,7 +3092,6 @@ func TestScorePlacement_Resources(t *testing.T) {
|
||||
},
|
||||
features: plfeature.Features{
|
||||
EnablePodLevelResources: true,
|
||||
EnableInPlacePodVerticalScaling: true,
|
||||
EnableInPlacePodLevelResourcesVerticalScaling: true,
|
||||
},
|
||||
expectedScore: 40,
|
||||
@@ -3880,25 +3849,11 @@ func TestFitScoreFeatureGates(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "InPlacePodVerticalScaling disabled ignores container status resources",
|
||||
name: "Uses container status resources",
|
||||
pod: withContainerStatusResources(
|
||||
st.MakePod().Req(cpuAndMemory("1000m", "400Mi")).Obj(),
|
||||
cpuAndMemory("2000m", "800Mi"),
|
||||
),
|
||||
features: plfeature.Features{EnableInPlacePodVerticalScaling: false},
|
||||
expectedScores: map[config.ScoringStrategyType]fwk.NodeScoreList{
|
||||
config.LeastAllocated: {{Name: "node1", Score: 80}, {Name: "node2", Score: 85}},
|
||||
config.MostAllocated: {{Name: "node1", Score: 20}, {Name: "node2", Score: 15}},
|
||||
config.RequestedToCapacityRatio: {{Name: "node1", Score: 40}, {Name: "node2", Score: 30}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "InPlacePodVerticalScaling enabled uses container status resources",
|
||||
pod: withContainerStatusResources(
|
||||
st.MakePod().Req(cpuAndMemory("1000m", "400Mi")).Obj(),
|
||||
cpuAndMemory("2000m", "800Mi"),
|
||||
),
|
||||
features: plfeature.Features{EnableInPlacePodVerticalScaling: true},
|
||||
expectedScores: map[config.ScoringStrategyType]fwk.NodeScoreList{
|
||||
config.LeastAllocated: {{Name: "node1", Score: 60}, {Name: "node2", Score: 70}},
|
||||
config.MostAllocated: {{Name: "node1", Score: 40}, {Name: "node2", Score: 30}},
|
||||
@@ -3916,7 +3871,6 @@ func TestFitScoreFeatureGates(t *testing.T) {
|
||||
),
|
||||
features: plfeature.Features{
|
||||
EnablePodLevelResources: true,
|
||||
EnableInPlacePodVerticalScaling: true,
|
||||
EnableInPlacePodLevelResourcesVerticalScaling: false,
|
||||
},
|
||||
expectedScores: map[config.ScoringStrategyType]fwk.NodeScoreList{
|
||||
@@ -3936,7 +3890,6 @@ func TestFitScoreFeatureGates(t *testing.T) {
|
||||
),
|
||||
features: plfeature.Features{
|
||||
EnablePodLevelResources: true,
|
||||
EnableInPlacePodVerticalScaling: true,
|
||||
EnableInPlacePodLevelResourcesVerticalScaling: true,
|
||||
},
|
||||
// Effective request: cpu=3000m, memory=1200Mi -> node1: 60% cpu, 60% mem; node2: 30% cpu, 60% mem
|
||||
|
||||
@@ -53,7 +53,6 @@ type DRACaches struct {
|
||||
// resourceAllocationScorer contains information to calculate resource allocation score.
|
||||
type resourceAllocationScorer struct {
|
||||
Name string
|
||||
enableInPlacePodVerticalScaling bool
|
||||
enablePodLevelResources bool
|
||||
enableDRAExtendedResource bool
|
||||
enableInPlacePodLevelResourcesVerticalScaling bool
|
||||
@@ -238,7 +237,7 @@ func (r *resourceAllocationScorer) calculateResourceAllocatableRequest(
|
||||
func (r *resourceAllocationScorer) calculatePodResourceRequest(pod *v1.Pod, resourceName v1.ResourceName) int64 {
|
||||
|
||||
opts := resourcehelper.PodResourcesOptions{
|
||||
UseStatusResources: r.enableInPlacePodVerticalScaling,
|
||||
UseStatusResources: true,
|
||||
InPlacePodLevelResourcesVerticalScalingEnabled: r.enableInPlacePodLevelResourcesVerticalScaling,
|
||||
// SkipPodLevelResources is set to false when PodLevelResources feature is enabled.
|
||||
SkipPodLevelResources: !r.enablePodLevelResources,
|
||||
|
||||
@@ -1283,8 +1283,7 @@ type PodInfo struct {
|
||||
PreferredAffinityTerms []fwk.WeightedAffinityTerm
|
||||
PreferredAntiAffinityTerms []fwk.WeightedAffinityTerm
|
||||
// cachedResource contains precomputed resources for Pod (podResource).
|
||||
// The value can change only if InPlacePodVerticalScaling is enabled.
|
||||
// In that case, the whole PodInfo object is recreated (for assigned pods in cache).
|
||||
// When a pod is resized in-place, the whole PodInfo object is recreated (for assigned pods in cache).
|
||||
// cachedResource contains a podResource, computed when adding a scheduled pod to NodeInfo.
|
||||
// When removing a pod from a NodeInfo, i.e. finding victims for preemption or removing a pod from a cluster,
|
||||
// cachedResource is used instead, what provides a noticeable performance boost.
|
||||
@@ -1355,13 +1354,12 @@ func (pi *PodInfo) CalculateResource() fwk.PodResource {
|
||||
if pi.cachedResource != nil {
|
||||
return *pi.cachedResource
|
||||
}
|
||||
inPlacePodVerticalScalingEnabled := utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling)
|
||||
podLevelResourcesEnabled := utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources)
|
||||
inPlacePodLevelResourcesVerticalScalingEnabled := utilfeature.DefaultMutableFeatureGate.Enabled(features.InPlacePodLevelResourcesVerticalScaling)
|
||||
nodeAllocatableResourcesDRAEnabled := utilfeature.DefaultFeatureGate.Enabled(features.DRANodeAllocatableResources)
|
||||
|
||||
requests := resourcehelper.PodRequests(pi.Pod, resourcehelper.PodResourcesOptions{
|
||||
UseStatusResources: inPlacePodVerticalScalingEnabled,
|
||||
UseStatusResources: true,
|
||||
InPlacePodLevelResourcesVerticalScalingEnabled: inPlacePodLevelResourcesVerticalScalingEnabled,
|
||||
// SkipPodLevelResources is set to false when PodLevelResources feature is enabled.
|
||||
SkipPodLevelResources: !podLevelResourcesEnabled,
|
||||
@@ -1372,7 +1370,7 @@ func (pi *PodInfo) CalculateResource() fwk.PodResource {
|
||||
non0Requests := requests
|
||||
if len(nonMissingContainerRequests) > 0 {
|
||||
non0Requests = resourcehelper.PodRequests(pi.Pod, resourcehelper.PodResourcesOptions{
|
||||
UseStatusResources: inPlacePodVerticalScalingEnabled,
|
||||
UseStatusResources: true,
|
||||
InPlacePodLevelResourcesVerticalScalingEnabled: inPlacePodLevelResourcesVerticalScalingEnabled,
|
||||
// SkipPodLevelResources is set to false when PodLevelResources feature is enabled.
|
||||
SkipPodLevelResources: !podLevelResourcesEnabled,
|
||||
|
||||
@@ -3159,7 +3159,6 @@ func TestPodInfoCalculateResources(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCalculatePodResourcesWithResize(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeDeclaredFeatures, true)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodLevelResourcesVerticalScaling, true)
|
||||
|
||||
|
||||
@@ -1404,7 +1404,6 @@ func TestHandleSchedulingFailureSkipsRecreatedPod(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleSchedulingFailureForDeferredResizePod(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScalingSchedulerPreemption, true)
|
||||
|
||||
logger, ctx := ktesting.NewTestContext(t)
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -36,7 +35,6 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
clientfeatures "k8s.io/client-go/features"
|
||||
clientfeaturestesting "k8s.io/client-go/features/testing"
|
||||
@@ -925,11 +923,10 @@ func Test_buildQueueingHintMap(t *testing.T) {
|
||||
// It verifies that a given set of plugins is correctly registered for a specific set of cluster events.
|
||||
func Test_UnionedGVKs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
plugins schedulerapi.PluginSet
|
||||
want map[fwk.EventResource]fwk.ActionType
|
||||
enableInPlacePodVerticalScaling bool
|
||||
enableGenericWorkload bool
|
||||
name string
|
||||
plugins schedulerapi.PluginSet
|
||||
want map[fwk.EventResource]fwk.ActionType
|
||||
enableGenericWorkload bool
|
||||
}{
|
||||
{
|
||||
name: "filter without EnqueueExtensions plugin",
|
||||
@@ -981,8 +978,7 @@ func Test_UnionedGVKs(t *testing.T) {
|
||||
fwk.DeviceClass: fwk.All,
|
||||
fwk.PodGroup: fwk.All,
|
||||
},
|
||||
enableGenericWorkload: true,
|
||||
enableInPlacePodVerticalScaling: true,
|
||||
enableGenericWorkload: true,
|
||||
},
|
||||
{
|
||||
name: "node plugin",
|
||||
@@ -1044,25 +1040,6 @@ func Test_UnionedGVKs(t *testing.T) {
|
||||
},
|
||||
want: map[fwk.EventResource]fwk.ActionType{},
|
||||
},
|
||||
{
|
||||
name: "plugins with default profile (InPlacePodVerticalScaling disabled)",
|
||||
plugins: schedulerapi.PluginSet{Enabled: defaults.PluginsV1.MultiPoint.Enabled},
|
||||
want: map[fwk.EventResource]fwk.ActionType{
|
||||
fwk.AssignedPod: fwk.Add | fwk.UpdatePodLabel | fwk.Delete,
|
||||
fwk.TargetPod: fwk.UpdatePodLabel | fwk.UpdatePodGeneratedResourceClaim | fwk.UpdatePodToleration | fwk.UpdatePodSchedulingGatesEliminated,
|
||||
fwk.Node: fwk.Add | fwk.UpdateNodeAllocatable | fwk.UpdateNodeLabel | fwk.UpdateNodeTaint | fwk.Delete,
|
||||
fwk.CSINode: fwk.All - fwk.Delete,
|
||||
fwk.CSIDriver: fwk.Update,
|
||||
fwk.CSIStorageCapacity: fwk.All - fwk.Delete,
|
||||
fwk.PersistentVolume: fwk.All - fwk.Delete,
|
||||
fwk.PersistentVolumeClaim: fwk.All - fwk.Delete,
|
||||
fwk.StorageClass: fwk.All - fwk.Delete,
|
||||
fwk.VolumeAttachment: fwk.Delete,
|
||||
fwk.DeviceClass: fwk.All - fwk.Delete,
|
||||
fwk.ResourceClaim: fwk.All,
|
||||
fwk.ResourceSlice: fwk.All - fwk.Delete,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "plugins with default profile",
|
||||
plugins: defaults.PluginsV1.MultiPoint,
|
||||
@@ -1083,7 +1060,6 @@ func Test_UnionedGVKs(t *testing.T) {
|
||||
fwk.ResourceClaim: fwk.All,
|
||||
fwk.ResourceSlice: fwk.All - fwk.Delete,
|
||||
},
|
||||
enableInPlacePodVerticalScaling: true,
|
||||
},
|
||||
{
|
||||
name: "plugin registering to a specific pod resource 'AssignedPod'",
|
||||
@@ -1119,38 +1095,15 @@ func Test_UnionedGVKs(t *testing.T) {
|
||||
fwk.ResourceSlice: fwk.All - fwk.Delete,
|
||||
fwk.PodGroup: fwk.Add | fwk.Update,
|
||||
},
|
||||
enableGenericWorkload: true,
|
||||
enableInPlacePodVerticalScaling: true,
|
||||
enableGenericWorkload: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pluginConfig := defaults.PluginConfigsV1
|
||||
|
||||
if !tt.enableInPlacePodVerticalScaling {
|
||||
// In place pod resize GA'd in 1.35. Set emulation version to 1.34 for tests that do not have the flag set
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, feature.DefaultFeatureGate, version.MustParse("1.34"))
|
||||
// DRADeviceBindingConditions is alpha in 1.34 (disabled by default).
|
||||
// Strip BindingTimeout from DynamicResources args to avoid validation failure.
|
||||
// StorageCapacityScoring is alpha in 1.34 (disabled by default).
|
||||
// Strip Shape from VolumeBinding args to avoid validation failure.
|
||||
// BindTimeoutSeconds: 600 is the default value of VolumeBindingArgs when StorageCapacityScoring is disabled.
|
||||
pluginConfig = slices.Clone(pluginConfig)
|
||||
for i := range pluginConfig {
|
||||
switch pluginConfig[i].Name {
|
||||
case "DynamicResources":
|
||||
pluginConfig[i].Args = &schedulerapi.DynamicResourcesArgs{}
|
||||
case "VolumeBinding":
|
||||
pluginConfig[i].Args = &schedulerapi.VolumeBindingArgs{BindTimeoutSeconds: 600}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, feature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.GenericWorkload: tt.enableGenericWorkload,
|
||||
})
|
||||
}
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, feature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.InPlacePodVerticalScaling: tt.enableInPlacePodVerticalScaling,
|
||||
features.GenericWorkload: tt.enableGenericWorkload,
|
||||
})
|
||||
|
||||
_, ctx := ktesting.NewTestContext(t)
|
||||
|
||||
@@ -821,7 +821,6 @@ func TestLimitRangerAllowPodResize(t *testing.T) {
|
||||
informerFactory.Start(wait.NeverStop)
|
||||
|
||||
testPod := validPod("testPod", 1, api.ResourceRequirements{})
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
err = handler.Validate(context.TODO(), admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "resize", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
|
||||
if err == nil {
|
||||
t.Errorf("expect error, but got nil")
|
||||
@@ -833,7 +832,6 @@ func TestLimitRangerAllowPodResize(t *testing.T) {
|
||||
// already holds is valid by definition and must not be rejected by a constraint
|
||||
// it no longer satisfies; a value the resize changes is checked as on create.
|
||||
func TestLimitRangerValidatePodResize(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, true)
|
||||
|
||||
cpu := func(request, limit string) api.ResourceRequirements {
|
||||
|
||||
@@ -30,11 +30,9 @@ import (
|
||||
genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
|
||||
"k8s.io/client-go/informers"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/component-base/featuregate"
|
||||
"k8s.io/component-helpers/resource"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
v1 "k8s.io/kubernetes/pkg/apis/core/v1"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -55,8 +53,7 @@ func Register(plugins *admission.Plugins) {
|
||||
// Plugin is an admission controller that validates pod resize requests against the node's capabilities.
|
||||
type Plugin struct {
|
||||
*admission.Handler
|
||||
nodeLister corev1listers.NodeLister
|
||||
inPlacePodVerticalScalingEnabled bool
|
||||
nodeLister corev1listers.NodeLister
|
||||
}
|
||||
|
||||
var _ admission.ValidationInterface = &Plugin{}
|
||||
@@ -76,11 +73,6 @@ func (p *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactor
|
||||
p.SetReadyFunc(nodeInformer.Informer().HasSynced)
|
||||
}
|
||||
|
||||
// InspectFeatureGates sets the feature gates for the plugin.
|
||||
func (p *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
|
||||
p.inPlacePodVerticalScalingEnabled = featureGates.Enabled(features.InPlacePodVerticalScaling)
|
||||
}
|
||||
|
||||
// ValidateInitialization ensures that the plugin is properly initialized.
|
||||
func (p *Plugin) ValidateInitialization() error {
|
||||
if p.nodeLister == nil {
|
||||
@@ -92,11 +84,6 @@ func (p *Plugin) ValidateInitialization() error {
|
||||
|
||||
// Validate is the core of the admission controller logic.
|
||||
func (p *Plugin) Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
// Ignore if the feature gate is not enabled.
|
||||
if !p.inPlacePodVerticalScalingEnabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if a.GetOperation() != admission.Update {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -255,7 +255,6 @@ func TestResizeAdmission(t *testing.T) {
|
||||
plugin, err := NewPlugin()
|
||||
require.NoError(t, err)
|
||||
plugin.SetExternalKubeInformerFactory(informerFactory)
|
||||
plugin.inPlacePodVerticalScalingEnabled = true
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
|
||||
@@ -801,7 +801,6 @@ message Container {
|
||||
|
||||
// resizePolicy is the resize policy for the container.
|
||||
// This field cannot be set on ephemeral containers.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
repeated ContainerResizePolicy resizePolicy = 23;
|
||||
@@ -1176,7 +1175,6 @@ message ContainerStatus {
|
||||
|
||||
// resources represents the compute resource requests and limits that have been successfully
|
||||
// enacted on the running container after it has been started or has been successfully resized.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
optional ResourceRequirements resources = 11;
|
||||
|
||||
@@ -1630,7 +1628,6 @@ message EphemeralContainerCommon {
|
||||
optional ResourceRequirements resources = 8;
|
||||
|
||||
// resizePolicy is the resources resize policy for the container.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
repeated ContainerResizePolicy resizePolicy = 23;
|
||||
@@ -5275,7 +5272,6 @@ message PodStatus {
|
||||
// Deprecated: Resize status is moved to two pod conditions PodResizePending and PodResizeInProgress.
|
||||
// PodResizePending will track states where the spec has been resized, but the Kubelet has not yet allocated the resources.
|
||||
// PodResizeInProgress will track in-progress resizes, and should be present whenever allocated resources != acknowledged resources.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
optional string resize = 14;
|
||||
|
||||
|
||||
@@ -3327,7 +3327,6 @@ type Container struct {
|
||||
Resources ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,8,opt,name=resources"`
|
||||
// resizePolicy is the resize policy for the container.
|
||||
// This field cannot be set on ephemeral containers.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
ResizePolicy []ContainerResizePolicy `json:"resizePolicy,omitempty" protobuf:"bytes,23,rep,name=resizePolicy"`
|
||||
@@ -3721,7 +3720,6 @@ type ContainerStatus struct {
|
||||
AllocatedResources ResourceList `json:"allocatedResources,omitempty" protobuf:"bytes,10,rep,name=allocatedResources,casttype=ResourceList,castkey=ResourceName"`
|
||||
// resources represents the compute resource requests and limits that have been successfully
|
||||
// enacted on the running container after it has been started or has been successfully resized.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
Resources *ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,11,opt,name=resources"`
|
||||
// volumeMounts represents the status of volume mounts.
|
||||
@@ -5656,7 +5654,6 @@ type EphemeralContainerCommon struct {
|
||||
// +optional
|
||||
Resources ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,8,opt,name=resources"`
|
||||
// resizePolicy is the resources resize policy for the container.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
ResizePolicy []ContainerResizePolicy `json:"resizePolicy,omitempty" protobuf:"bytes,23,rep,name=resizePolicy"`
|
||||
@@ -5958,7 +5955,6 @@ type PodStatus struct {
|
||||
// Deprecated: Resize status is moved to two pod conditions PodResizePending and PodResizeInProgress.
|
||||
// PodResizePending will track states where the spec has been resized, but the Kubelet has not yet allocated the resources.
|
||||
// PodResizeInProgress will track in-progress resizes, and should be present whenever allocated resources != acknowledged resources.
|
||||
// +featureGate=InPlacePodVerticalScaling
|
||||
// +optional
|
||||
Resize PodResizeStatus `json:"resize,omitempty" protobuf:"bytes,14,opt,name=resize,casttype=PodResizeStatus"`
|
||||
|
||||
|
||||
@@ -41,8 +41,7 @@ type PodResourcesOptions struct {
|
||||
// functions. All existing values in Reuse will be lost.
|
||||
Reuse v1.ResourceList
|
||||
// UseStatusResources indicates whether resources reported by the PodStatus should be considered
|
||||
// when evaluating the pod resources. This MUST be false if the InPlacePodVerticalScaling
|
||||
// feature is not enabled.
|
||||
// when evaluating the pod resources.
|
||||
UseStatusResources bool
|
||||
// InPlacePodLevelResourcesVerticalScalingEnabled indicates whether resources reported by the
|
||||
// PodStatus should be considered when evaluating the pod resources.
|
||||
@@ -313,7 +312,7 @@ func AggregateContainerRequests(pod *v1.Pod, opts PodResourcesOptions) v1.Resour
|
||||
var allocatedReqs, actuatedReqs v1.ResourceList
|
||||
// When pod-level status maps are populated, they already contain the aggregate values across all containers.
|
||||
// When unpopulated (e.g., at creation time or when feature gates are disabled), we fall back to container status aggregation.
|
||||
// Once InPlacePodLevelResourcesVerticalScaling and InPlacePodVerticalScaling are GA and feature gates are removed,
|
||||
// Once InPlacePodLevelResourcesVerticalScaling is GA and its feature gate is removed,
|
||||
// container-level fallback becomes redundant because max(spec, actuated, allocated) naturally evaluates to spec at creation time.
|
||||
if opts.InPlacePodLevelResourcesVerticalScalingEnabled && pod.Status.AllocatedResources != nil && pod.Status.Resources != nil && pod.Status.Resources.Requests != nil {
|
||||
allocatedReqs = pod.Status.AllocatedResources
|
||||
@@ -461,7 +460,7 @@ func AggregateContainerLimits(pod *v1.Pod, opts PodResourcesOptions) v1.Resource
|
||||
var actuatedLimits v1.ResourceList
|
||||
// When pod-level status maps are populated, they already contain the aggregate values across all containers.
|
||||
// When unpopulated (e.g., at creation time or when feature gates are disabled), we fall back to container status aggregation.
|
||||
// Once InPlacePodLevelResourcesVerticalScaling and InPlacePodVerticalScaling are GA and feature gates are removed,
|
||||
// Once InPlacePodLevelResourcesVerticalScaling is GA and its feature gate is removed,
|
||||
// container-level fallback becomes redundant because max(spec, actuated) naturally evaluates to spec at creation time.
|
||||
if opts.InPlacePodLevelResourcesVerticalScalingEnabled && pod.Status.Resources != nil && pod.Status.Resources.Limits != nil {
|
||||
actuatedLimits = pod.Status.Resources.Limits
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
| DetectCacheInconsistency | :ballot_box_with_check: 1.34+ | | | 1.34– | | | | [code](https://cs.k8s.io/?q=%5CbDetectCacheInconsistency%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbDetectCacheInconsistency%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| DisableCPUQuotaWithExclusiveCPUs | :ballot_box_with_check: 1.33+ | :closed_lock_with_key: 1.37+ | | 1.33–1.35 | | 1.36– | | [code](https://cs.k8s.io/?q=%5CbDisableCPUQuotaWithExclusiveCPUs%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbDisableCPUQuotaWithExclusiveCPUs%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| DisableNodeKubeProxyVersion | :ballot_box_with_check: 1.33+ | :closed_lock_with_key: 1.36+ | 1.29–1.30 | | | 1.31– | | [code](https://cs.k8s.io/?q=%5CbDisableNodeKubeProxyVersion%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbDisableNodeKubeProxyVersion%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| DynamicNodeLocalEphemeralVolumes | | | 1.38– | | | | InPlacePodVerticalScaling<br>NodeDeclaredFeatures | [code](https://cs.k8s.io/?q=%5CbDynamicNodeLocalEphemeralVolumes%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbDynamicNodeLocalEphemeralVolumes%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| DynamicNodeLocalEphemeralVolumes | | | 1.38– | | | | NodeDeclaredFeatures | [code](https://cs.k8s.io/?q=%5CbDynamicNodeLocalEphemeralVolumes%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbDynamicNodeLocalEphemeralVolumes%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| EmptyDirVolumeMode | | | 1.37– | | | | | [code](https://cs.k8s.io/?q=%5CbEmptyDirVolumeMode%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbEmptyDirVolumeMode%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| EnvFiles | :ballot_box_with_check: 1.35+ | :closed_lock_with_key: 1.38+ | 1.34 | 1.35–1.37 | 1.38– | | | [code](https://cs.k8s.io/?q=%5CbEnvFiles%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbEnvFiles%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| EtcdRangeStream | :ballot_box_with_check: 1.37+ | | | 1.37– | | | | [code](https://cs.k8s.io/?q=%5CbEtcdRangeStream%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbEtcdRangeStream%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
@@ -107,20 +107,19 @@
|
||||
| ImageVolumeWithDigest | | | 1.35– | | | | ImageVolume | [code](https://cs.k8s.io/?q=%5CbImageVolumeWithDigest%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbImageVolumeWithDigest%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InOrderInformers | :ballot_box_with_check: 1.33+ | :closed_lock_with_key: 1.36+ | | 1.33–1.35 | 1.36– | | | [code](https://cs.k8s.io/?q=%5CbInOrderInformers%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInOrderInformers%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InOrderInformersBatchProcess | :ballot_box_with_check: 1.35+ | | | 1.35– | | | | [code](https://cs.k8s.io/?q=%5CbInOrderInformersBatchProcess%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInOrderInformersBatchProcess%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodLevelResourcesVerticalScaling | :ballot_box_with_check: 1.36+ | | 1.35 | 1.36– | | | InPlacePodVerticalScaling<br>NodeDeclaredFeatures<br>PodLevelResources | [code](https://cs.k8s.io/?q=%5CbInPlacePodLevelResourcesVerticalScaling%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodLevelResourcesVerticalScaling%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodVerticalScaling | :ballot_box_with_check: 1.33+ | :closed_lock_with_key: 1.35+ | 1.27–1.32 | 1.33–1.34 | 1.35– | | | [code](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScaling%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScaling%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodVerticalScalingExclusiveCPUs | | | 1.32– | | | | InPlacePodVerticalScaling | [code](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingExclusiveCPUs%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingExclusiveCPUs%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodVerticalScalingExclusiveMemory | | | 1.34– | | | | InPlacePodVerticalScaling | [code](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingExclusiveMemory%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingExclusiveMemory%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodVerticalScalingInitContainers | :ballot_box_with_check: 1.36+ | :closed_lock_with_key: 1.37+ | | 1.36 | 1.37– | | InPlacePodVerticalScaling<br>NodeDeclaredFeatures | [code](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingInitContainers%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingInitContainers%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodVerticalScalingMemoryBackedVolumes | | | 1.37– | | | | InPlacePodVerticalScaling<br>NodeDeclaredFeatures | [code](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingMemoryBackedVolumes%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingMemoryBackedVolumes%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodVerticalScalingSchedulerPreemption | | | 1.37– | | | | InPlacePodVerticalScaling | [code](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingSchedulerPreemption%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingSchedulerPreemption%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodLevelResourcesVerticalScaling | :ballot_box_with_check: 1.36+ | | 1.35 | 1.36– | | | NodeDeclaredFeatures<br>PodLevelResources | [code](https://cs.k8s.io/?q=%5CbInPlacePodLevelResourcesVerticalScaling%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodLevelResourcesVerticalScaling%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodVerticalScalingExclusiveCPUs | | | 1.32– | | | | | [code](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingExclusiveCPUs%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingExclusiveCPUs%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodVerticalScalingExclusiveMemory | | | 1.34– | | | | | [code](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingExclusiveMemory%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingExclusiveMemory%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodVerticalScalingInitContainers | :ballot_box_with_check: 1.36+ | :closed_lock_with_key: 1.37+ | | 1.36 | 1.37– | | NodeDeclaredFeatures | [code](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingInitContainers%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingInitContainers%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodVerticalScalingMemoryBackedVolumes | | | 1.37– | | | | NodeDeclaredFeatures | [code](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingMemoryBackedVolumes%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingMemoryBackedVolumes%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InPlacePodVerticalScalingSchedulerPreemption | | | 1.37– | | | | | [code](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingSchedulerPreemption%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInPlacePodVerticalScalingSchedulerPreemption%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InformerResourceVersion | :ballot_box_with_check: 1.35+ | | 1.30–1.34 | | 1.35– | | | [code](https://cs.k8s.io/?q=%5CbInformerResourceVersion%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInformerResourceVersion%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| InterPodAffinityHostnameFastPath | | | 1.37– | | | | | [code](https://cs.k8s.io/?q=%5CbInterPodAffinityHostnameFastPath%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInterPodAffinityHostnameFastPath%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| KMSv1 | :ballot_box_with_check: 1.0+ | | | | 1.0–1.27 | 1.28– | | [code](https://cs.k8s.io/?q=%5CbKMSv1%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKMSv1%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| KubeAPIServerWorkloadsTrust | | | 1.38– | | | | ClusterTrustBundle<br>ClusterTrustBundleProjection | [code](https://cs.k8s.io/?q=%5CbKubeAPIServerWorkloadsTrust%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeAPIServerWorkloadsTrust%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| KubeProxyIPVS | :ballot_box_with_check: 1.0+ | | | | 1.0–1.36 | 1.37– | | [code](https://cs.k8s.io/?q=%5CbKubeProxyIPVS%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeProxyIPVS%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| KubeProxyNFTablesLocalhostNodePorts | :ballot_box_with_check: 1.38+ | | 1.37 | 1.38– | | | | [code](https://cs.k8s.io/?q=%5CbKubeProxyNFTablesLocalhostNodePorts%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeProxyNFTablesLocalhostNodePorts%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| KubeletAllocatedPodsEndpoint | | | 1.37– | | | | InPlacePodVerticalScaling | [code](https://cs.k8s.io/?q=%5CbKubeletAllocatedPodsEndpoint%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeletAllocatedPodsEndpoint%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| KubeletAllocatedPodsEndpoint | | | 1.37– | | | | | [code](https://cs.k8s.io/?q=%5CbKubeletAllocatedPodsEndpoint%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeletAllocatedPodsEndpoint%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| KubeletCgroupDriverFromCRI | :ballot_box_with_check: 1.31+ | :closed_lock_with_key: 1.34+ | 1.28–1.30 | 1.31–1.33 | 1.34– | | | [code](https://cs.k8s.io/?q=%5CbKubeletCgroupDriverFromCRI%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeletCgroupDriverFromCRI%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| KubeletCrashLoopBackOffMax | :ballot_box_with_check: 1.35+ | | 1.32–1.34 | 1.35– | | | | [code](https://cs.k8s.io/?q=%5CbKubeletCrashLoopBackOffMax%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeletCrashLoopBackOffMax%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| KubeletEnsureSecretPulledImages | :ballot_box_with_check: 1.35+ | | 1.33–1.34 | 1.35– | | | | [code](https://cs.k8s.io/?q=%5CbKubeletEnsureSecretPulledImages%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeletEnsureSecretPulledImages%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
|
||||
@@ -917,20 +917,6 @@
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.36"
|
||||
- name: InPlacePodVerticalScaling
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
lockToDefault: false
|
||||
preRelease: Alpha
|
||||
version: "1.27"
|
||||
- default: true
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.33"
|
||||
- default: true
|
||||
lockToDefault: true
|
||||
preRelease: GA
|
||||
version: "1.35"
|
||||
- name: InPlacePodVerticalScalingExclusiveCPUs
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
|
||||
@@ -1317,7 +1317,7 @@ func doPodResizeDeferredPreemptionTests(f *framework.Framework) {
|
||||
})
|
||||
}
|
||||
|
||||
var _ = SIGDescribe(framework.WithSerial(), "Pod InPlace Resize Container (scheduler-focused)", framework.WithFeatureGate(features.InPlacePodVerticalScaling), func() {
|
||||
var _ = SIGDescribe(framework.WithSerial(), "Pod InPlace Resize Container (scheduler-focused)", func() {
|
||||
f := framework.NewDefaultFramework("pod-resize-scheduler-tests")
|
||||
ginkgo.BeforeEach(func(ctx context.Context) {
|
||||
node, err := e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet)
|
||||
@@ -1329,7 +1329,7 @@ var _ = SIGDescribe(framework.WithSerial(), "Pod InPlace Resize Container (sched
|
||||
doPodResizeSchedulerTests(f)
|
||||
})
|
||||
|
||||
var _ = SIGDescribe(framework.WithSerial(), "Pod InPlace Resize Container (deferred-resizes)", framework.WithFeatureGate(features.InPlacePodVerticalScaling), func() {
|
||||
var _ = SIGDescribe(framework.WithSerial(), "Pod InPlace Resize Container (deferred-resizes)", func() {
|
||||
f := framework.NewDefaultFramework("pod-resize-deferred-resize-tests")
|
||||
ginkgo.BeforeEach(func(ctx context.Context) {
|
||||
node, err := e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet)
|
||||
@@ -1341,7 +1341,7 @@ var _ = SIGDescribe(framework.WithSerial(), "Pod InPlace Resize Container (defer
|
||||
doPodResizeRetryDeferredTests(f)
|
||||
})
|
||||
|
||||
var _ = SIGDescribe("Pod InPlace Resize Container (resource-quota)", framework.WithFeatureGate(features.InPlacePodVerticalScaling), func() {
|
||||
var _ = SIGDescribe("Pod InPlace Resize Container (resource-quota)", func() {
|
||||
f := framework.NewDefaultFramework("pod-resize-resource-quota-tests")
|
||||
|
||||
ginkgo.BeforeEach(func(ctx context.Context) {
|
||||
@@ -1354,7 +1354,7 @@ var _ = SIGDescribe("Pod InPlace Resize Container (resource-quota)", framework.W
|
||||
doPodResizeResourceQuotaTests(f)
|
||||
})
|
||||
|
||||
var _ = SIGDescribe("Pod InPlace Resize Container (limit-ranger)", framework.WithFeatureGate(features.InPlacePodVerticalScaling), func() {
|
||||
var _ = SIGDescribe("Pod InPlace Resize Container (limit-ranger)", func() {
|
||||
f := framework.NewDefaultFramework("pod-resize-limit-ranger-tests")
|
||||
|
||||
ginkgo.BeforeEach(func(ctx context.Context) {
|
||||
@@ -1368,7 +1368,6 @@ var _ = SIGDescribe("Pod InPlace Resize Container (limit-ranger)", framework.Wit
|
||||
})
|
||||
|
||||
var _ = SIGDescribe(framework.WithSerial(), framework.WithSlow(), "Pod InPlace Resize Container (deferred-resize-preemption)",
|
||||
framework.WithFeatureGate(features.InPlacePodVerticalScaling),
|
||||
framework.WithFeatureGate(features.InPlacePodVerticalScalingSchedulerPreemption),
|
||||
func() {
|
||||
f := framework.NewDefaultFramework("pod-resize-deferred-preemption-tests")
|
||||
|
||||
@@ -857,7 +857,6 @@ func TestPodUpdateEphemeralContainers(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPodResizeRBAC(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
server := kubeapiservertesting.StartTestServerOrDie(t, nil,
|
||||
append(framework.DefaultTestServerFlags(), "--authorization-mode=RBAC"), framework.SharedEtcd())
|
||||
defer server.TearDownFn()
|
||||
@@ -961,7 +960,6 @@ func TestPodResizeRBAC(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPodResize(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
|
||||
server := kubeapiservertesting.StartTestServerOrDie(t, nil, framework.DefaultTestServerFlags(), framework.SharedEtcd())
|
||||
defer server.TearDownFn()
|
||||
|
||||
@@ -42,7 +42,6 @@ import (
|
||||
func TestDeferredResizePodPreemption(t *testing.T) {
|
||||
// Setup API server with feature gates enabled
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.InPlacePodVerticalScaling: true,
|
||||
features.InPlacePodVerticalScalingSchedulerPreemption: true,
|
||||
})
|
||||
|
||||
@@ -451,7 +450,6 @@ func setUpPreemptionTestWithContext(t *testing.T, testCtx *testutils.TestContext
|
||||
|
||||
func TestDeferredResizeQueueingHints(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.InPlacePodVerticalScaling: true,
|
||||
features.InPlacePodVerticalScalingSchedulerPreemption: true,
|
||||
})
|
||||
|
||||
@@ -616,7 +614,6 @@ func TestDeferredResizeQueueingHints(t *testing.T) {
|
||||
|
||||
func TestDeferredResizeNodePreemptionPolicy(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.InPlacePodVerticalScaling: true,
|
||||
features.InPlacePodVerticalScalingSchedulerPreemption: true,
|
||||
})
|
||||
|
||||
@@ -743,7 +740,6 @@ func TestDeferredResizeNodePreemptionPolicy(t *testing.T) {
|
||||
func TestDeferredResizeQueueingHandlers(t *testing.T) {
|
||||
t.Run("AddPod handler enqueues existing deferred pod on scheduler startup", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.InPlacePodVerticalScaling: true,
|
||||
features.InPlacePodVerticalScalingSchedulerPreemption: true,
|
||||
})
|
||||
|
||||
@@ -833,7 +829,6 @@ func TestDeferredResizeQueueingHandlers(t *testing.T) {
|
||||
|
||||
t.Run("UpdatePod handler enqueues pod when it transitions to deferred resize", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.InPlacePodVerticalScaling: true,
|
||||
features.InPlacePodVerticalScalingSchedulerPreemption: true,
|
||||
})
|
||||
|
||||
@@ -940,7 +935,6 @@ func TestDeferredResizeQueueingHandlers(t *testing.T) {
|
||||
|
||||
t.Run("UpdatePod handler removes pod from queue when deferred condition is cleared", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.InPlacePodVerticalScaling: true,
|
||||
features.InPlacePodVerticalScalingSchedulerPreemption: true,
|
||||
})
|
||||
|
||||
@@ -1035,7 +1029,6 @@ func TestDeferredResizeQueueingHandlers(t *testing.T) {
|
||||
|
||||
t.Run("DeletePod handler removes pod from both cache and scheduling queue when deleted", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.InPlacePodVerticalScaling: true,
|
||||
features.InPlacePodVerticalScalingSchedulerPreemption: true,
|
||||
})
|
||||
|
||||
|
||||
@@ -2579,7 +2579,6 @@ var CoreResourceEnqueueTestCases = []*CoreResourceEnqueueTestCase{
|
||||
// moved properly upon their registered events.
|
||||
func RunTestCoreResourceEnqueue(t *testing.T, tt *CoreResourceEnqueueTestCase) { //nolint:forbidigo // calls ndftesting.NewMockFeature/ktesting.NewTestContext, which require *testing.T
|
||||
t.Helper()
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)
|
||||
if tt.EnableDRAExtendedResource {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAExtendedResource, true)
|
||||
}
|
||||
|
||||
@@ -144,8 +144,6 @@
|
||||
# that through QHints make the pods schedulable.
|
||||
# Plugins covered: InterPodAffinity, NodeResources, NodeUnschedulable, PodTopologySpread, SchedulingGates and TaintToleration.
|
||||
- name: EventHandlingPodUpdate
|
||||
featureGates:
|
||||
InPlacePodVerticalScaling: true
|
||||
workloadTemplate:
|
||||
# Collect metrics from all createPods ops that initially create unschedulable pods (type: unsched).
|
||||
- opcode: startCollectingMetrics
|
||||
|
||||
Reference in new issue
Block a user