Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[receiver/kubeletstats] Add resource getters #26690

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 73 additions & 1 deletion receiver/kubeletstatsreceiver/internal/kubelet/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,87 @@ type Metadata struct {
Labels map[MetadataLabel]bool
PodsMetadata *v1.PodList
DetailedPVCResourceSetter func(rb *metadata.ResourceBuilder, volCacheID, volumeClaim, namespace string) error
podResources map[string]resources
containerResources map[string]resources
}

type resources struct {
cpuRequest float64
cpuLimit float64
memoryRequest int64
memoryLimit int64
}

func getContainerResources(r *v1.ResourceRequirements) resources {
if r == nil {
return resources{}
}

return resources{
cpuRequest: r.Requests.Cpu().AsApproximateFloat64(),
cpuLimit: r.Limits.Cpu().AsApproximateFloat64(),
memoryRequest: r.Requests.Memory().Value(),
memoryLimit: r.Limits.Memory().Value(),
}
}

func NewMetadata(labels []MetadataLabel, podsMetadata *v1.PodList,
detailedPVCResourceSetter func(rb *metadata.ResourceBuilder, volCacheID, volumeClaim, namespace string) error) Metadata {
return Metadata{
m := Metadata{
Labels: getLabelsMap(labels),
PodsMetadata: podsMetadata,
DetailedPVCResourceSetter: detailedPVCResourceSetter,
podResources: make(map[string]resources, 0),
containerResources: make(map[string]resources, 0),
}

if podsMetadata != nil {
for _, pod := range podsMetadata.Items {
var podResource resources
allContainersCPULimitsDefined := true
allContainersCPURequestsDefined := true
allContainersMemoryLimitsDefined := true
allContainersMemoryRequestsDefined := true
for _, container := range pod.Spec.Containers {
containerResource := getContainerResources(&container.Resources)

if allContainersCPULimitsDefined && containerResource.cpuLimit == 0 {
allContainersCPULimitsDefined = false
podResource.cpuLimit = 0
}
if allContainersCPURequestsDefined && containerResource.cpuRequest == 0 {
allContainersCPURequestsDefined = false
podResource.cpuRequest = 0
}
if allContainersMemoryLimitsDefined && containerResource.memoryLimit == 0 {
allContainersMemoryLimitsDefined = false
podResource.memoryLimit = 0
}
if allContainersMemoryRequestsDefined && containerResource.memoryRequest == 0 {
allContainersMemoryRequestsDefined = false
podResource.memoryRequest = 0
}

if allContainersCPULimitsDefined {
podResource.cpuLimit += containerResource.cpuLimit
}
if allContainersCPURequestsDefined {
podResource.cpuRequest += containerResource.cpuRequest
}
if allContainersMemoryLimitsDefined {
podResource.memoryLimit += containerResource.memoryLimit
}
if allContainersMemoryRequestsDefined {
podResource.memoryRequest += containerResource.memoryRequest
}

m.containerResources[string(pod.UID)+container.Name] = containerResource
}
m.podResources[string(pod.UID)] = podResource
}
}

return m
}

func getLabelsMap(metadataLabels []MetadataLabel) map[MetadataLabel]bool {
Expand Down
298 changes: 298 additions & 0 deletions receiver/kubeletstatsreceiver/internal/kubelet/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
k8sresource "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
stats "k8s.io/kubelet/pkg/apis/stats/v1alpha1"

Expand Down Expand Up @@ -386,3 +387,300 @@ func TestSetExtraLabelsForVolumeTypes(t *testing.T) {
})
}
}

// Test happy paths for volume type metadata.
func TestCpuAndMemoryGetters(t *testing.T) {

tests := []struct {
name string
metadata Metadata
podUID string
containerName string
wantPodCPULimit float64
wantPodCPURequest float64
wantContainerCPULimit float64
wantContainerCPURequest float64
wantPodMemoryLimit int64
wantPodMemoryRequest int64
wantContainerMemoryLimit int64
wantContainerMemoryRequest int64
}{
{
name: "no metadata",
metadata: NewMetadata([]MetadataLabel{}, nil, nil),
},
{
name: "pod happy path",
metadata: NewMetadata([]MetadataLabel{}, &v1.PodList{
Items: []v1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
UID: "uid-1234",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "container-1",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("100m"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
Limits: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("100m"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
},
},
{
Name: "container-2",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("2"),
v1.ResourceMemory: k8sresource.MustParse("3G"),
},
Limits: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("2"),
v1.ResourceMemory: k8sresource.MustParse("3G"),
},
},
},
},
},
},
},
}, nil),
podUID: "uid-1234",
containerName: "container-2",
wantPodCPULimit: 2.1,
wantPodCPURequest: 2.1,
wantContainerCPULimit: 2,
wantContainerCPURequest: 2,
wantPodMemoryLimit: 4000000000,
wantPodMemoryRequest: 4000000000,
wantContainerMemoryLimit: 3000000000,
wantContainerMemoryRequest: 3000000000,
},
{
name: "unknown pod",
metadata: NewMetadata([]MetadataLabel{}, &v1.PodList{
Items: []v1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
UID: "uid-1234",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "container-1",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("1"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
Limits: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("1"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
},
},
{
Name: "container-2",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("2"),
v1.ResourceMemory: k8sresource.MustParse("3G"),
},
Limits: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("2"),
v1.ResourceMemory: k8sresource.MustParse("3G"),
},
},
},
},
},
},
},
}, nil),
podUID: "uid-12345",
},
{
name: "unknown container",
metadata: NewMetadata([]MetadataLabel{}, &v1.PodList{
Items: []v1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
UID: "uid-1234",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "container-1",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("300m"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
Limits: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("300m"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
},
},
{
Name: "container-2",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("400m"),
v1.ResourceMemory: k8sresource.MustParse("3G"),
},
Limits: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("400m"),
v1.ResourceMemory: k8sresource.MustParse("3G"),
},
},
},
},
},
},
},
}, nil),
podUID: "uid-1234",
containerName: "container-3",
wantPodCPULimit: 0.7,
wantPodCPURequest: 0.7,
wantPodMemoryLimit: 4000000000,
wantPodMemoryRequest: 4000000000,
},
{
name: "container limit not set",
metadata: NewMetadata([]MetadataLabel{}, &v1.PodList{
Items: []v1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
UID: "uid-1234",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "container-1",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("1"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
},
},
{
Name: "container-2",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("1"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
},
},
},
},
},
},
}, nil),
podUID: "uid-1234",
containerName: "container-2",
wantPodCPURequest: 2,
wantContainerCPURequest: 1,
wantPodMemoryRequest: 2000000000,
wantContainerMemoryRequest: 1000000000,
},
{
name: "container request not set",
metadata: NewMetadata([]MetadataLabel{}, &v1.PodList{
Items: []v1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
UID: "uid-1234",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "container-1",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("1"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
},
},
{
Name: "container-2",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("1"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
},
},
},
},
},
},
}, nil),
podUID: "uid-1234",
containerName: "container-2",
wantPodCPULimit: 2,
wantContainerCPULimit: 1,
wantPodMemoryLimit: 2000000000,
wantContainerMemoryLimit: 1000000000,
},
{
name: "container limit not set but other is",
metadata: NewMetadata([]MetadataLabel{}, &v1.PodList{
Items: []v1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
UID: "uid-1234",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "container-1",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("1"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
Limits: v1.ResourceList{
v1.ResourceCPU: k8sresource.MustParse("1"),
v1.ResourceMemory: k8sresource.MustParse("1G"),
},
},
},
{
Name: "container-2",
},
},
},
},
},
}, nil),
podUID: "uid-1234",
containerName: "container-1",
wantContainerCPULimit: 1,
wantContainerCPURequest: 1,
wantContainerMemoryLimit: 1000000000,
wantContainerMemoryRequest: 1000000000,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.wantPodCPULimit, tt.metadata.podResources[tt.podUID].cpuLimit)
require.Equal(t, tt.wantPodCPURequest, tt.metadata.podResources[tt.podUID].cpuRequest)
require.Equal(t, tt.wantContainerCPULimit, tt.metadata.containerResources[tt.podUID+tt.containerName].cpuLimit)
require.Equal(t, tt.wantContainerCPURequest, tt.metadata.containerResources[tt.podUID+tt.containerName].cpuRequest)
require.Equal(t, tt.wantPodMemoryLimit, tt.metadata.podResources[tt.podUID].memoryLimit)
require.Equal(t, tt.wantPodMemoryRequest, tt.metadata.podResources[tt.podUID].memoryRequest)
require.Equal(t, tt.wantContainerMemoryLimit, tt.metadata.containerResources[tt.podUID+tt.containerName].memoryLimit)
require.Equal(t, tt.wantContainerMemoryRequest, tt.metadata.containerResources[tt.podUID+tt.containerName].memoryRequest)
})
}
}