Skip to content

Commit

Permalink
[receiver/kubeletstats] Add resource getters (#26690)
Browse files Browse the repository at this point in the history
**Description:**
Add metadata map for pod and container requests and limits. Will be used
to calculate new metrics in a future PR.

**Link to tracking Issue:** <Issue number if applicable>

#24905

**Testing:** <Describe what testing was performed and which tests were
added.>
added unit tests
  • Loading branch information
TylerHelmuth committed Sep 26, 2023
1 parent 0e0e8f3 commit dbde606
Show file tree
Hide file tree
Showing 2 changed files with 371 additions and 1 deletion.
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)
})
}
}

0 comments on commit dbde606

Please sign in to comment.