Skip to content

Commit 3fc7c95

Browse files
committed
Skip CPU resource status for workload-pinned pods
Signed-off-by: Harshal Patil <[email protected]>
1 parent 00e7ec7 commit 3fc7c95

File tree

3 files changed

+214
-0
lines changed

3 files changed

+214
-0
lines changed

pkg/kubelet/kuberuntime/kuberuntime_container.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import (
5656
"k8s.io/kubernetes/pkg/kubelet/cm"
5757
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
5858
"k8s.io/kubernetes/pkg/kubelet/events"
59+
"k8s.io/kubernetes/pkg/kubelet/managed"
5960
proberesults "k8s.io/kubernetes/pkg/kubelet/prober/results"
6061
"k8s.io/kubernetes/pkg/kubelet/types"
6162
"k8s.io/kubernetes/pkg/kubelet/util/format"
@@ -619,6 +620,8 @@ func (m *kubeGenericRuntimeManager) getPodContainerStatuses(ctx context.Context,
619620
return nil, nil, err
620621
}
621622

623+
isManagedPod := managed.IsManagedPodFromRuntimeService(ctx, m.runtimeService, activePodSandboxID)
624+
622625
statuses := []*kubecontainer.Status{}
623626
activeContainerStatuses := []*kubecontainer.Status{}
624627
// TODO: optimization: set maximum number of containers per container name to examine.
@@ -641,6 +644,9 @@ func (m *kubeGenericRuntimeManager) getPodContainerStatuses(ctx context.Context,
641644
return nil, nil, remote.ErrContainerStatusNil
642645
}
643646
cStatus := m.convertToKubeContainerStatus(status)
647+
if isManagedPod && cStatus.Resources != nil { // Clear CPU resources for managed pods (workload-pinned)
648+
cStatus.Resources.CPURequest, cStatus.Resources.CPULimit = nil, nil
649+
}
644650
statuses = append(statuses, cStatus)
645651
if c.PodSandboxId == activePodSandboxID {
646652
activeContainerStatuses = append(activeContainerStatuses, cStatus)

pkg/kubelet/managed/managed.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ limitations under the License.
1717
package managed
1818

1919
import (
20+
"context"
2021
"encoding/json"
2122
"fmt"
2223
"os"
2324
"strings"
2425

2526
v1 "k8s.io/api/core/v1"
2627
"k8s.io/apimachinery/pkg/api/resource"
28+
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
2729
)
2830

2931
var (
@@ -88,6 +90,40 @@ func IsPodManaged(pod *v1.Pod) (bool, string, string) {
8890
return false, "", ""
8991
}
9092

93+
// IsPodSandboxManagedPod checks if a pod sandbox belongs to a managed pod
94+
// by looking for workload pinning annotations.
95+
func IsPodSandboxManagedPod(sandboxAnnotations map[string]string) bool {
96+
if sandboxAnnotations == nil {
97+
return false
98+
}
99+
for annotation := range sandboxAnnotations {
100+
if strings.HasPrefix(annotation, WorkloadsAnnotationPrefix) {
101+
return true
102+
}
103+
}
104+
return false
105+
}
106+
107+
// podSandboxStatusGetter is an interface for getting pod sandbox status
108+
type podSandboxStatusGetter interface {
109+
PodSandboxStatus(ctx context.Context, podSandboxID string, verbose bool) (*runtimeapi.PodSandboxStatusResponse, error)
110+
}
111+
112+
// IsManagedPodFromRuntimeService checks if a pod is managed by fetching the pod sandbox
113+
// status and checking for workload pinning annotations.
114+
func IsManagedPodFromRuntimeService(ctx context.Context, runtimeService podSandboxStatusGetter, podSandboxID string) bool {
115+
if podSandboxID == "" {
116+
return false
117+
}
118+
119+
sandboxResp, err := runtimeService.PodSandboxStatus(ctx, podSandboxID, false)
120+
if err != nil || sandboxResp == nil || sandboxResp.GetStatus() == nil {
121+
return false
122+
}
123+
124+
return IsPodSandboxManagedPod(sandboxResp.GetStatus().Annotations)
125+
}
126+
91127
// ModifyStaticPodForPinnedManagement will modify a pod for pod management
92128
func ModifyStaticPodForPinnedManagement(pod *v1.Pod) (*v1.Pod, string, error) {
93129
pod = pod.DeepCopy()

pkg/kubelet/managed/managed_test.go

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package managed
22

33
import (
4+
"context"
45
"fmt"
56
"testing"
67

78
v1 "k8s.io/api/core/v1"
89
"k8s.io/apimachinery/pkg/api/resource"
910
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
1012
)
1113

1214
func TestModifyStaticPodForPinnedManagementErrorStates(t *testing.T) {
@@ -1007,3 +1009,173 @@ func createPod(annotations map[string]string, initContainer, container *v1.Conta
10071009

10081010
return pod
10091011
}
1012+
1013+
func TestIsPodSandboxManagedPod(t *testing.T) {
1014+
testCases := []struct {
1015+
name string
1016+
annotations map[string]string
1017+
expected bool
1018+
}{
1019+
{
1020+
name: "nil annotations",
1021+
annotations: nil,
1022+
expected: false,
1023+
},
1024+
{
1025+
name: "empty annotations",
1026+
annotations: map[string]string{},
1027+
expected: false,
1028+
},
1029+
{
1030+
name: "regular pod annotations without workload annotations",
1031+
annotations: map[string]string{
1032+
"some.annotation": "value",
1033+
"another.annotation": "value2",
1034+
"io.kubernetes.pod.id": "12345",
1035+
},
1036+
expected: false,
1037+
},
1038+
{
1039+
name: "managed pod with workload management annotation",
1040+
annotations: map[string]string{
1041+
"some.annotation": "value",
1042+
WorkloadsAnnotationPrefix + "management": `{"effect": "PreferredDuringScheduling"}`,
1043+
},
1044+
expected: true,
1045+
},
1046+
{
1047+
name: "managed pod with workload throttle annotation",
1048+
annotations: map[string]string{
1049+
WorkloadsAnnotationPrefix + "throttle": `{"effect": "PreferredDuringScheduling"}`,
1050+
},
1051+
expected: true,
1052+
},
1053+
{
1054+
name: "pod with annotation similar to workload prefix but not matching",
1055+
annotations: map[string]string{
1056+
"target.workload.openshift.io": "value", // missing trailing slash
1057+
"some.other.annotation": "value2",
1058+
},
1059+
expected: false,
1060+
},
1061+
{
1062+
name: "managed pod with multiple annotations",
1063+
annotations: map[string]string{
1064+
"io.kubernetes.pod.name": "test-pod",
1065+
"io.kubernetes.pod.namespace": "default",
1066+
WorkloadsAnnotationPrefix + "management": `{"effect": "PreferredDuringScheduling"}`,
1067+
"custom.annotation": "custom-value",
1068+
},
1069+
expected: true,
1070+
},
1071+
}
1072+
1073+
for _, tc := range testCases {
1074+
t.Run(tc.name, func(t *testing.T) {
1075+
result := IsPodSandboxManagedPod(tc.annotations)
1076+
if result != tc.expected {
1077+
t.Errorf("IsPodSandboxManagedPod() = %v, expected %v for annotations: %v",
1078+
result, tc.expected, tc.annotations)
1079+
}
1080+
})
1081+
}
1082+
}
1083+
1084+
// mockRuntimeService is a simple mock for testing
1085+
type mockRuntimeService struct {
1086+
sandboxStatus *runtimeapi.PodSandboxStatusResponse
1087+
err error
1088+
}
1089+
1090+
func (m *mockRuntimeService) PodSandboxStatus(ctx context.Context, podSandboxID string, verbose bool) (*runtimeapi.PodSandboxStatusResponse, error) {
1091+
return m.sandboxStatus, m.err
1092+
}
1093+
1094+
func TestIsManagedPodFromRuntimeService(t *testing.T) {
1095+
ctx := context.Background()
1096+
1097+
testCases := []struct {
1098+
name string
1099+
podSandboxID string
1100+
sandboxStatus *runtimeapi.PodSandboxStatusResponse
1101+
err error
1102+
expected bool
1103+
}{
1104+
{
1105+
name: "empty sandbox ID",
1106+
podSandboxID: "",
1107+
expected: false,
1108+
},
1109+
{
1110+
name: "runtime service returns error",
1111+
podSandboxID: "sandbox123",
1112+
err: fmt.Errorf("runtime error"),
1113+
expected: false,
1114+
},
1115+
{
1116+
name: "nil sandbox response",
1117+
podSandboxID: "sandbox123",
1118+
sandboxStatus: nil,
1119+
expected: false,
1120+
},
1121+
{
1122+
name: "nil status in response",
1123+
podSandboxID: "sandbox123",
1124+
sandboxStatus: &runtimeapi.PodSandboxStatusResponse{
1125+
Status: nil,
1126+
},
1127+
expected: false,
1128+
},
1129+
{
1130+
name: "regular pod without workload annotations",
1131+
podSandboxID: "sandbox123",
1132+
sandboxStatus: &runtimeapi.PodSandboxStatusResponse{
1133+
Status: &runtimeapi.PodSandboxStatus{
1134+
Annotations: map[string]string{
1135+
"some.annotation": "value",
1136+
},
1137+
},
1138+
},
1139+
expected: false,
1140+
},
1141+
{
1142+
name: "managed pod with workload annotations",
1143+
podSandboxID: "sandbox123",
1144+
sandboxStatus: &runtimeapi.PodSandboxStatusResponse{
1145+
Status: &runtimeapi.PodSandboxStatus{
1146+
Annotations: map[string]string{
1147+
"some.annotation": "value",
1148+
WorkloadsAnnotationPrefix + "management": `{"effect": "PreferredDuringScheduling"}`,
1149+
},
1150+
},
1151+
},
1152+
expected: true,
1153+
},
1154+
{
1155+
name: "managed pod with empty annotations but has workload prefix",
1156+
podSandboxID: "sandbox123",
1157+
sandboxStatus: &runtimeapi.PodSandboxStatusResponse{
1158+
Status: &runtimeapi.PodSandboxStatus{
1159+
Annotations: map[string]string{
1160+
WorkloadsAnnotationPrefix + "throttle": "",
1161+
},
1162+
},
1163+
},
1164+
expected: true,
1165+
},
1166+
}
1167+
1168+
for _, tc := range testCases {
1169+
t.Run(tc.name, func(t *testing.T) {
1170+
mockService := &mockRuntimeService{
1171+
sandboxStatus: tc.sandboxStatus,
1172+
err: tc.err,
1173+
}
1174+
1175+
result := IsManagedPodFromRuntimeService(ctx, mockService, tc.podSandboxID)
1176+
if result != tc.expected {
1177+
t.Errorf("IsManagedPodFromRuntimeService() = %v, expected %v", result, tc.expected)
1178+
}
1179+
})
1180+
}
1181+
}

0 commit comments

Comments
 (0)