diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index 62173e25873..193ff54bc74 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -2074,24 +2074,18 @@ func (kl *Kubelet) convertToAPIContainerStatuses(pod *v1.Pod, podStatus *kubecon } convertContainerStatusResources := func(cName string, status *v1.ContainerStatus, cStatus *kubecontainer.Status, oldStatuses map[string]v1.ContainerStatus) *v1.ResourceRequirements { - var requests, limits v1.ResourceList // oldStatus should always exist if container is running oldStatus, oldStatusFound := oldStatuses[cName] - // Initialize limits/requests from container's spec upon transition to Running state - // For cpu & memory, values queried from runtime via CRI always supercedes spec values - // For ephemeral-storage, a running container's status.limit/request equals spec.limit/request - determineResource := func(rName v1.ResourceName, v1ContainerResource, oldStatusResource, resource v1.ResourceList) { - if oldStatusFound { - if oldStatus.State.Running == nil || status.ContainerID != oldStatus.ContainerID { - if r, exists := v1ContainerResource[rName]; exists { - resource[rName] = r.DeepCopy() - } - } else { - if oldStatusResource != nil { - if r, exists := oldStatusResource[rName]; exists { - resource[rName] = r.DeepCopy() - } - } + + // If the new status is missing resources, then if the container is running and previous + // status was also running, preserve the resources previously reported. + preserveOldResourcesValue := func(rName v1.ResourceName, oldStatusResource, resource v1.ResourceList) { + if cStatus.State == kubecontainer.ContainerStateRunning && + oldStatusFound && oldStatus.State.Running != nil && + status.ContainerID == oldStatus.ContainerID && + oldStatusResource != nil { + if r, exists := oldStatusResource[rName]; exists { + resource[rName] = r.DeepCopy() } } } @@ -2100,73 +2094,43 @@ func (kl *Kubelet) convertToAPIContainerStatuses(pod *v1.Pod, podStatus *kubecon // allocation used by the current sync loop. alloc, found := kl.statusManager.GetContainerResourceAllocation(string(pod.UID), cName) if !found { - // This case is expected for non-resizeable containers. + // This case is expected for non-resizable containers. // Don't set status.Resources in this case. return nil } + if cStatus.State != kubecontainer.ContainerStateRunning { + // If the container isn't running, just use the allocated resources. + return &alloc + } if oldStatus.Resources == nil { oldStatus.Resources = &v1.ResourceRequirements{} } - convertCustomResources := func(inResources, outResources v1.ResourceList) { - for resourceName, resourceQuantity := range inResources { - if resourceName == v1.ResourceCPU || resourceName == v1.ResourceMemory || - resourceName == v1.ResourceStorage || resourceName == v1.ResourceEphemeralStorage { - continue - } - - outResources[resourceName] = resourceQuantity.DeepCopy() - } - } - - // Convert Limits - if alloc.Limits != nil { - limits = make(v1.ResourceList) + // Status resources default to the allocated resources. + // For non-running containers this will be the reported values. + // For non-resizable resources, these values will also be used. + resources := alloc + if resources.Limits != nil { if cStatus.Resources != nil && cStatus.Resources.CPULimit != nil { - limits[v1.ResourceCPU] = cStatus.Resources.CPULimit.DeepCopy() + resources.Limits[v1.ResourceCPU] = cStatus.Resources.CPULimit.DeepCopy() } else { - determineResource(v1.ResourceCPU, alloc.Limits, oldStatus.Resources.Limits, limits) + preserveOldResourcesValue(v1.ResourceCPU, oldStatus.Resources.Limits, resources.Limits) } if cStatus.Resources != nil && cStatus.Resources.MemoryLimit != nil { - limits[v1.ResourceMemory] = cStatus.Resources.MemoryLimit.DeepCopy() + resources.Limits[v1.ResourceMemory] = cStatus.Resources.MemoryLimit.DeepCopy() } else { - determineResource(v1.ResourceMemory, alloc.Limits, oldStatus.Resources.Limits, limits) + preserveOldResourcesValue(v1.ResourceMemory, oldStatus.Resources.Limits, resources.Limits) } - if ephemeralStorage, found := alloc.Limits[v1.ResourceEphemeralStorage]; found { - limits[v1.ResourceEphemeralStorage] = ephemeralStorage.DeepCopy() - } - if storage, found := alloc.Limits[v1.ResourceStorage]; found { - limits[v1.ResourceStorage] = storage.DeepCopy() - } - - convertCustomResources(alloc.Limits, limits) } - // Convert Requests - if alloc.Requests != nil { - requests = make(v1.ResourceList) + if resources.Requests != nil { if cStatus.Resources != nil && cStatus.Resources.CPURequest != nil { - requests[v1.ResourceCPU] = cStatus.Resources.CPURequest.DeepCopy() + resources.Requests[v1.ResourceCPU] = cStatus.Resources.CPURequest.DeepCopy() } else { - determineResource(v1.ResourceCPU, alloc.Requests, oldStatus.Resources.Requests, requests) + preserveOldResourcesValue(v1.ResourceCPU, oldStatus.Resources.Requests, resources.Requests) } - if memory, found := alloc.Requests[v1.ResourceMemory]; found { - requests[v1.ResourceMemory] = memory.DeepCopy() - } - if ephemeralStorage, found := alloc.Requests[v1.ResourceEphemeralStorage]; found { - requests[v1.ResourceEphemeralStorage] = ephemeralStorage.DeepCopy() - } - if storage, found := alloc.Requests[v1.ResourceStorage]; found { - requests[v1.ResourceStorage] = storage.DeepCopy() - } - - convertCustomResources(alloc.Requests, requests) } - resources := &v1.ResourceRequirements{ - Limits: limits, - Requests: requests, - } - return resources + return &resources } convertContainerStatusUser := func(cStatus *kubecontainer.Status) *v1.ContainerUser { @@ -2335,9 +2299,7 @@ func (kl *Kubelet) convertToAPIContainerStatuses(pod *v1.Pod, podStatus *kubecon } status := convertContainerStatus(cStatus, oldStatusPtr) if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) { - if status.State.Running != nil { - status.Resources = convertContainerStatusResources(cName, status, cStatus, oldStatuses) - } + status.Resources = convertContainerStatusResources(cName, status, cStatus, oldStatuses) if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScalingAllocatedStatus) { if alloc, found := kl.statusManager.GetContainerResourceAllocation(string(pod.UID), cName); found { @@ -2345,6 +2307,7 @@ func (kl *Kubelet) convertToAPIContainerStatuses(pod *v1.Pod, podStatus *kubecon } } } + if utilfeature.DefaultFeatureGate.Enabled(features.SupplementalGroupsPolicy) { status.User = convertContainerStatusUser(cStatus) } diff --git a/pkg/kubelet/kubelet_pods_test.go b/pkg/kubelet/kubelet_pods_test.go index 706b057fe45..fa5f3230c3e 100644 --- a/pkg/kubelet/kubelet_pods_test.go +++ b/pkg/kubelet/kubelet_pods_test.go @@ -4567,26 +4567,39 @@ func TestConvertToAPIContainerStatusesForResources(t *testing.T) { ContainerStatuses: []v1.ContainerStatus{testContainerStatus}, }, } - testKubeContainerStatus := kubecontainer.Status{ - Name: testContainerName, - ID: testContainerID, - Image: "img", - ImageID: "1234", - ImageRef: "img1234", - State: kubecontainer.ContainerStateRunning, - StartedAt: nowTime, - } - testPodStatus := &kubecontainer.PodStatus{ - ID: testPod.UID, - Name: testPod.Name, - Namespace: testPod.Namespace, - ContainerStatuses: []*kubecontainer.Status{&testKubeContainerStatus}, + + testPodStatus := func(state kubecontainer.State, resources *kubecontainer.ContainerResources) *kubecontainer.PodStatus { + cStatus := kubecontainer.Status{ + Name: testContainerName, + ID: testContainerID, + Image: "img", + ImageID: "1234", + ImageRef: "img1234", + State: state, + Resources: resources, + } + switch state { + case kubecontainer.ContainerStateRunning: + cStatus.StartedAt = nowTime + case kubecontainer.ContainerStateExited: + cStatus.StartedAt = nowTime + cStatus.FinishedAt = nowTime + } + return &kubecontainer.PodStatus{ + ID: testPod.UID, + Name: testPod.Name, + Namespace: testPod.Namespace, + ContainerStatuses: []*kubecontainer.Status{&cStatus}, + } } + CPU1AndMem1G := v1.ResourceList{v1.ResourceCPU: resource.MustParse("1"), v1.ResourceMemory: resource.MustParse("1Gi")} CPU2AndMem2G := v1.ResourceList{v1.ResourceCPU: resource.MustParse("2"), v1.ResourceMemory: resource.MustParse("2Gi")} CPU1AndMem1GAndStorage2G := CPU1AndMem1G.DeepCopy() CPU1AndMem1GAndStorage2G[v1.ResourceEphemeralStorage] = resource.MustParse("2Gi") CPU1AndMem1GAndStorage2G[v1.ResourceStorage] = resource.MustParse("2Gi") + CPU1AndMem2GAndStorage2G := CPU1AndMem1GAndStorage2G.DeepCopy() + CPU1AndMem2GAndStorage2G[v1.ResourceMemory] = resource.MustParse("2Gi") CPU2AndMem2GAndStorage2G := CPU2AndMem2G.DeepCopy() CPU2AndMem2GAndStorage2G[v1.ResourceEphemeralStorage] = resource.MustParse("2Gi") CPU2AndMem2GAndStorage2G[v1.ResourceStorage] = resource.MustParse("2Gi") @@ -4612,254 +4625,298 @@ func TestConvertToAPIContainerStatusesForResources(t *testing.T) { idx := 0 for tdesc, tc := range map[string]struct { - Resources []v1.ResourceRequirements - OldStatus []v1.ContainerStatus - Expected []v1.ContainerStatus + State kubecontainer.State // Defaults to Running + Resources v1.ResourceRequirements + AllocatedResources *v1.ResourceRequirements // Defaults to Resources + OldStatus v1.ContainerStatus + Expected v1.ContainerStatus }{ "GuaranteedQoSPod with CPU and memory CRI status": { - Resources: []v1.ResourceRequirements{{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}}, - OldStatus: []v1.ContainerStatus{ - { - Name: testContainerName, - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, - Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}, - }, + Resources: v1.ResourceRequirements{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}, }, - Expected: []v1.ContainerStatus{ - { - Name: testContainerName, - ContainerID: testContainerID.String(), - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, - AllocatedResources: CPU1AndMem1G, - Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}, - }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + AllocatedResources: CPU1AndMem1G, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}, }, }, "BurstableQoSPod with CPU and memory CRI status": { - Resources: []v1.ResourceRequirements{{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}}, - OldStatus: []v1.ContainerStatus{ - { - Name: testContainerName, - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, - Resources: &v1.ResourceRequirements{Limits: CPU2AndMem2G, Requests: CPU1AndMem1G}, - }, + Resources: v1.ResourceRequirements{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, + Resources: &v1.ResourceRequirements{Limits: CPU2AndMem2G, Requests: CPU1AndMem1G}, }, - Expected: []v1.ContainerStatus{ - { - Name: testContainerName, - ContainerID: testContainerID.String(), - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, - AllocatedResources: CPU1AndMem1G, - Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}, - }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + AllocatedResources: CPU1AndMem1G, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}, }, }, "GuaranteedQoSPod with CPU and memory CRI status, with ephemeral storage": { - Resources: []v1.ResourceRequirements{{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}}, - OldStatus: []v1.ContainerStatus{ - { - Name: testContainerName, - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, - Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}, - }, + Resources: v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1G, Requests: CPU1AndMem1G}, }, - Expected: []v1.ContainerStatus{ - { - Name: testContainerName, - ContainerID: testContainerID.String(), - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, - AllocatedResources: CPU1AndMem1GAndStorage2G, - Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, - }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + AllocatedResources: CPU1AndMem1GAndStorage2G, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, }, }, "BurstableQoSPod with CPU and memory CRI status, with ephemeral storage": { - Resources: []v1.ResourceRequirements{{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}}, - OldStatus: []v1.ContainerStatus{ - { - Name: testContainerName, - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, - Resources: &v1.ResourceRequirements{Limits: CPU2AndMem2GAndStorage2G, Requests: CPU2AndMem2GAndStorage2G}, - }, + Resources: v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, + Resources: &v1.ResourceRequirements{Limits: CPU2AndMem2GAndStorage2G, Requests: CPU2AndMem2GAndStorage2G}, }, - Expected: []v1.ContainerStatus{ - { - Name: testContainerName, - ContainerID: testContainerID.String(), - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, - AllocatedResources: CPU1AndMem1GAndStorage2G, - Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, - }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + AllocatedResources: CPU1AndMem1GAndStorage2G, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, }, }, "BurstableQoSPod with CPU and memory CRI status, with ephemeral storage, nil resources in OldStatus": { - Resources: []v1.ResourceRequirements{{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}}, - OldStatus: []v1.ContainerStatus{ - { - Name: testContainerName, - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, - }, + Resources: v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, }, - Expected: []v1.ContainerStatus{ - { - Name: testContainerName, - ContainerID: testContainerID.String(), - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, - AllocatedResources: CPU1AndMem1GAndStorage2G, - Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, - }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + AllocatedResources: CPU1AndMem1GAndStorage2G, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, }, }, "BestEffortQoSPod": { - OldStatus: []v1.ContainerStatus{ - { - Name: testContainerName, - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, - Resources: &v1.ResourceRequirements{}, - }, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, + Resources: &v1.ResourceRequirements{}, }, - Expected: []v1.ContainerStatus{ - { - Name: testContainerName, - ContainerID: testContainerID.String(), - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, - Resources: &v1.ResourceRequirements{}, - }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + Resources: &v1.ResourceRequirements{}, }, }, "BestEffort QoSPod with extended resources": { - Resources: []v1.ResourceRequirements{{Requests: addExtendedResource(v1.ResourceList{})}}, - OldStatus: []v1.ContainerStatus{ - { - Name: testContainerName, - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, - Resources: &v1.ResourceRequirements{}, - }, + Resources: v1.ResourceRequirements{Requests: addExtendedResource(v1.ResourceList{})}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, + Resources: &v1.ResourceRequirements{}, }, - Expected: []v1.ContainerStatus{ - { - Name: testContainerName, - ContainerID: testContainerID.String(), - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, - AllocatedResources: addExtendedResource(v1.ResourceList{}), - Resources: &v1.ResourceRequirements{Requests: addExtendedResource(v1.ResourceList{})}, - }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + AllocatedResources: addExtendedResource(v1.ResourceList{}), + Resources: &v1.ResourceRequirements{Requests: addExtendedResource(v1.ResourceList{})}, }, }, "BurstableQoSPod with extended resources": { - Resources: []v1.ResourceRequirements{{Requests: addExtendedResource(CPU1AndMem1G)}}, - OldStatus: []v1.ContainerStatus{ - { - Name: testContainerName, - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, - Resources: &v1.ResourceRequirements{}, - }, + Resources: v1.ResourceRequirements{Requests: addExtendedResource(CPU1AndMem1G)}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, + Resources: &v1.ResourceRequirements{}, }, - Expected: []v1.ContainerStatus{ - { - Name: testContainerName, - ContainerID: testContainerID.String(), - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, - AllocatedResources: addExtendedResource(CPU1AndMem1G), - Resources: &v1.ResourceRequirements{Requests: addExtendedResource(CPU1AndMem1G)}, - }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + AllocatedResources: addExtendedResource(CPU1AndMem1G), + Resources: &v1.ResourceRequirements{Requests: addExtendedResource(CPU1AndMem1G)}, }, }, "BurstableQoSPod with storage, ephemeral storage and extended resources": { - Resources: []v1.ResourceRequirements{{Requests: addExtendedResource(CPU1AndMem1GAndStorage2G)}}, - OldStatus: []v1.ContainerStatus{ - { - Name: testContainerName, - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, - Resources: &v1.ResourceRequirements{}, - }, + Resources: v1.ResourceRequirements{Requests: addExtendedResource(CPU1AndMem1GAndStorage2G)}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, + Resources: &v1.ResourceRequirements{}, }, - Expected: []v1.ContainerStatus{ - { - Name: testContainerName, - ContainerID: testContainerID.String(), - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, - AllocatedResources: addExtendedResource(CPU1AndMem1GAndStorage2G), - Resources: &v1.ResourceRequirements{Requests: addExtendedResource(CPU1AndMem1GAndStorage2G)}, - }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + AllocatedResources: addExtendedResource(CPU1AndMem1GAndStorage2G), + Resources: &v1.ResourceRequirements{Requests: addExtendedResource(CPU1AndMem1GAndStorage2G)}, }, }, "GuaranteedQoSPod with extended resources": { - Resources: []v1.ResourceRequirements{{Requests: addExtendedResource(CPU1AndMem1G), Limits: addExtendedResource(CPU1AndMem1G)}}, - OldStatus: []v1.ContainerStatus{ - { - Name: testContainerName, - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, - Resources: &v1.ResourceRequirements{}, - }, + Resources: v1.ResourceRequirements{Requests: addExtendedResource(CPU1AndMem1G), Limits: addExtendedResource(CPU1AndMem1G)}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, + Resources: &v1.ResourceRequirements{}, }, - Expected: []v1.ContainerStatus{ - { - Name: testContainerName, - ContainerID: testContainerID.String(), - Image: "img", - ImageID: "img1234", - State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, - AllocatedResources: addExtendedResource(CPU1AndMem1G), - Resources: &v1.ResourceRequirements{Requests: addExtendedResource(CPU1AndMem1G), Limits: addExtendedResource(CPU1AndMem1G)}, - }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + AllocatedResources: addExtendedResource(CPU1AndMem1G), + Resources: &v1.ResourceRequirements{Requests: addExtendedResource(CPU1AndMem1G), Limits: addExtendedResource(CPU1AndMem1G)}, + }, + }, + "newly created Pod": { + State: kubecontainer.ContainerStateCreated, + Resources: v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + OldStatus: v1.ContainerStatus{}, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Waiting: &v1.ContainerStateWaiting{}}, + AllocatedResources: CPU1AndMem1GAndStorage2G, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + }, + }, + "newly running Pod": { + Resources: v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Waiting: &v1.ContainerStateWaiting{}}, + }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + AllocatedResources: CPU1AndMem1GAndStorage2G, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + }, + }, + "newly terminated Pod": { + State: kubecontainer.ContainerStateExited, + // Actual resources were different, but they should be ignored once the container is terminated. + Resources: v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + AllocatedResources: &v1.ResourceRequirements{Limits: CPU2AndMem2GAndStorage2G, Requests: CPU2AndMem2GAndStorage2G}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Terminated: &v1.ContainerStateTerminated{ + ContainerID: testContainerID.String(), + StartedAt: metav1.NewTime(nowTime), + FinishedAt: metav1.NewTime(nowTime), + }}, + AllocatedResources: CPU2AndMem2GAndStorage2G, + Resources: &v1.ResourceRequirements{Limits: CPU2AndMem2GAndStorage2G, Requests: CPU2AndMem2GAndStorage2G}, + }, + }, + "resizing Pod": { + Resources: v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + AllocatedResources: &v1.ResourceRequirements{Limits: CPU2AndMem2GAndStorage2G, Requests: CPU2AndMem2GAndStorage2G}, + OldStatus: v1.ContainerStatus{ + Name: testContainerName, + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + }, + Expected: v1.ContainerStatus{ + Name: testContainerName, + ContainerID: testContainerID.String(), + Image: "img", + ImageID: "img1234", + State: v1.ContainerState{Running: &v1.ContainerStateRunning{StartedAt: metav1.NewTime(nowTime)}}, + AllocatedResources: CPU2AndMem2GAndStorage2G, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem2GAndStorage2G}, }, }, } { t.Run(tdesc, func(t *testing.T) { tPod := testPod.DeepCopy() tPod.Name = fmt.Sprintf("%s-%d", testPod.Name, idx) - for i := range tPod.Spec.Containers { - if tc.Resources != nil { - tPod.Spec.Containers[i].Resources = tc.Resources[i] - } - kubelet.statusManager.SetPodAllocation(tPod) - if tc.Resources != nil { - testPodStatus.ContainerStatuses[i].Resources = &kubecontainer.ContainerResources{ - MemoryLimit: tc.Resources[i].Limits.Memory(), - CPULimit: tc.Resources[i].Limits.Cpu(), - CPURequest: tc.Resources[i].Requests.Cpu(), - } - } + + if tc.AllocatedResources != nil { + tPod.Spec.Containers[0].Resources = *tc.AllocatedResources + } else { + tPod.Spec.Containers[0].Resources = tc.Resources } + kubelet.statusManager.SetPodAllocation(tPod) + resources := &kubecontainer.ContainerResources{ + MemoryLimit: tc.Resources.Limits.Memory(), + CPULimit: tc.Resources.Limits.Cpu(), + CPURequest: tc.Resources.Requests.Cpu(), + } + state := kubecontainer.ContainerStateRunning + if tc.State != "" { + state = tc.State + } + podStatus := testPodStatus(state, resources) for _, enableAllocatedStatus := range []bool{true, false} { t.Run(fmt.Sprintf("AllocatedStatus=%t", enableAllocatedStatus), func(t *testing.T) { @@ -4867,15 +4924,12 @@ func TestConvertToAPIContainerStatusesForResources(t *testing.T) { expected := tc.Expected if !enableAllocatedStatus { - for i, status := range expected { - noAllocated := *status.DeepCopy() - noAllocated.AllocatedResources = nil - expected[i] = noAllocated - } + expected = *expected.DeepCopy() + expected.AllocatedResources = nil } - cStatuses := kubelet.convertToAPIContainerStatuses(tPod, testPodStatus, tc.OldStatus, tPod.Spec.Containers, false, false) - assert.Equal(t, expected, cStatuses) + cStatuses := kubelet.convertToAPIContainerStatuses(tPod, podStatus, []v1.ContainerStatus{tc.OldStatus}, tPod.Spec.Containers, false, false) + assert.Equal(t, expected, cStatuses[0]) }) } })