e2e node: provide tests for memory manager pod resources metrics

- verify memory manager data returned by `GetAllocatableResources`
- verify pod container memory manager data

Signed-off-by: Artyom Lukianov <alukiano@redhat.com>
This commit is contained in:
Artyom Lukianov 2021-05-10 15:28:08 +03:00
parent 03830db82d
commit 681905706d

View File

@ -31,9 +31,12 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeletpodresourcesv1 "k8s.io/kubelet/pkg/apis/podresources/v1"
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
"k8s.io/kubernetes/pkg/kubelet/apis/podresources"
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
"k8s.io/kubernetes/pkg/kubelet/cm/memorymanager/state"
"k8s.io/kubernetes/pkg/kubelet/util"
"k8s.io/kubernetes/test/e2e/framework"
e2enode "k8s.io/kubernetes/test/e2e/framework/node"
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
@ -153,13 +156,33 @@ func getMemoryManagerState() (*state.MemoryManagerCheckpoint, error) {
return memoryManagerCheckpoint, nil
}
func getAllocatableMemoryFromStateFile(s *state.MemoryManagerCheckpoint) []state.Block {
var allocatableMemory []state.Block
for numaNodeID, numaNodeState := range s.MachineState {
for resourceName, memoryTable := range numaNodeState.MemoryMap {
if memoryTable.Allocatable == 0 {
continue
}
block := state.Block{
NUMAAffinity: []int{numaNodeID},
Type: resourceName,
Size: memoryTable.Allocatable,
}
allocatableMemory = append(allocatableMemory, block)
}
}
return allocatableMemory
}
type kubeletParams struct {
memoryManagerFeatureGate bool
memoryManagerPolicy string
systemReservedMemory []kubeletconfig.MemoryReservation
systemReserved map[string]string
kubeReserved map[string]string
evictionHard map[string]string
memoryManagerFeatureGate bool
podResourcesGetAllocatableFeatureGate bool
memoryManagerPolicy string
systemReservedMemory []kubeletconfig.MemoryReservation
systemReserved map[string]string
kubeReserved map[string]string
evictionHard map[string]string
}
func getUpdatedKubeletConfig(oldCfg *kubeletconfig.KubeletConfiguration, params *kubeletParams) *kubeletconfig.KubeletConfiguration {
@ -169,6 +192,8 @@ func getUpdatedKubeletConfig(oldCfg *kubeletconfig.KubeletConfiguration, params
newCfg.FeatureGates = map[string]bool{}
}
newCfg.FeatureGates["MemoryManager"] = params.memoryManagerFeatureGate
newCfg.FeatureGates["KubeletPodResourcesGetAllocatable"] = params.podResourcesGetAllocatableFeatureGate
newCfg.MemoryManagerPolicy = params.memoryManagerPolicy
// update system-reserved
@ -257,14 +282,15 @@ var _ = SIGDescribe("Memory Manager [Serial] [Feature:MemoryManager][NodeAlphaFe
f := framework.NewDefaultFramework("memory-manager-test")
memoryQuantatity := resource.MustParse("1100Mi")
memoryQuantity := resource.MustParse("1100Mi")
defaultKubeParams := &kubeletParams{
memoryManagerFeatureGate: true,
memoryManagerFeatureGate: true,
podResourcesGetAllocatableFeatureGate: true,
systemReservedMemory: []kubeletconfig.MemoryReservation{
{
NumaNode: 0,
Limits: v1.ResourceList{
resourceMemory: memoryQuantatity,
resourceMemory: memoryQuantity,
},
},
},
@ -367,12 +393,14 @@ var _ = SIGDescribe("Memory Manager [Serial] [Feature:MemoryManager][NodeAlphaFe
}
}
testPod = makeMemoryManagerPod(ctnParams[0].ctnName, initCtnParams, ctnParams)
if len(ctnParams) > 0 {
testPod = makeMemoryManagerPod(ctnParams[0].ctnName, initCtnParams, ctnParams)
}
})
ginkgo.JustAfterEach(func() {
// delete the test pod
if testPod.Name != "" {
if testPod != nil && testPod.Name != "" {
f.PodClient().DeleteSync(testPod.Name, metav1.DeleteOptions{}, 2*time.Minute)
}
@ -407,6 +435,48 @@ var _ = SIGDescribe("Memory Manager [Serial] [Feature:MemoryManager][NodeAlphaFe
initCtnParams = []memoryManagerCtnAttributes{}
})
// TODO: move the test to pod resource API test suite, see - https://github.com/kubernetes/kubernetes/issues/101945
ginkgo.It("should report memory data during request to pod resources GetAllocatableResources", func() {
endpoint, err := util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket)
framework.ExpectNoError(err)
cli, conn, err := podresources.GetV1Client(endpoint, defaultPodResourcesTimeout, defaultPodResourcesMaxSize)
framework.ExpectNoError(err)
defer conn.Close()
resp, err := cli.GetAllocatableResources(context.TODO(), &kubeletpodresourcesv1.AllocatableResourcesRequest{})
framework.ExpectNoError(err)
gomega.Expect(resp.Memory).ToNot(gomega.BeEmpty())
stateData, err := getMemoryManagerState()
framework.ExpectNoError(err)
stateAllocatableMemory := getAllocatableMemoryFromStateFile(stateData)
framework.ExpectEqual(len(resp.Memory), len(stateAllocatableMemory))
for _, containerMemory := range resp.Memory {
gomega.Expect(containerMemory.Topology).NotTo(gomega.BeNil())
framework.ExpectEqual(len(containerMemory.Topology.Nodes), 1)
gomega.Expect(containerMemory.Topology.Nodes[0]).NotTo(gomega.BeNil())
numaNodeID := int(containerMemory.Topology.Nodes[0].ID)
for _, numaStateMemory := range stateAllocatableMemory {
framework.ExpectEqual(len(numaStateMemory.NUMAAffinity), 1)
if numaNodeID != numaStateMemory.NUMAAffinity[0] {
continue
}
if containerMemory.MemoryType != string(numaStateMemory.Type) {
continue
}
gomega.Expect(containerMemory.Size_).To(gomega.BeEquivalentTo(numaStateMemory.Size))
}
}
gomega.Expect(resp.Memory).ToNot(gomega.BeEmpty())
})
ginkgo.When("guaranteed pod has init and app containers", func() {
ginkgo.BeforeEach(func() {
// override containers parameters
@ -499,6 +569,48 @@ var _ = SIGDescribe("Memory Manager [Serial] [Feature:MemoryManager][NodeAlphaFe
verifyMemoryPinning(testPod2, []int{0})
})
// TODO: move the test to pod resource API test suite, see - https://github.com/kubernetes/kubernetes/issues/101945
ginkgo.It("should report memory data for each guaranteed pod and container during request to pod resources List", func() {
ginkgo.By("Running the test pod and the test pod 2")
testPod = f.PodClient().CreateSync(testPod)
ginkgo.By("Running the test pod 2")
testPod2 = f.PodClient().CreateSync(testPod2)
endpoint, err := util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket)
framework.ExpectNoError(err)
cli, conn, err := podresources.GetV1Client(endpoint, defaultPodResourcesTimeout, defaultPodResourcesMaxSize)
framework.ExpectNoError(err)
defer conn.Close()
resp, err := cli.List(context.TODO(), &kubeletpodresourcesv1.ListPodResourcesRequest{})
framework.ExpectNoError(err)
for _, pod := range []*v1.Pod{testPod, testPod2} {
for _, podResource := range resp.PodResources {
if podResource.Name != pod.Name {
continue
}
for _, c := range pod.Spec.Containers {
for _, containerResource := range podResource.Containers {
if containerResource.Name != c.Name {
continue
}
for _, containerMemory := range containerResource.Memory {
q := c.Resources.Limits[v1.ResourceName(containerMemory.MemoryType)]
value, ok := q.AsInt64()
gomega.Expect(ok).To(gomega.BeTrue())
gomega.Expect(value).To(gomega.BeEquivalentTo(containerMemory.Size_))
}
}
}
}
}
})
ginkgo.JustAfterEach(func() {
// delete the test pod 2
if testPod2.Name != "" {
@ -604,6 +716,46 @@ var _ = SIGDescribe("Memory Manager [Serial] [Feature:MemoryManager][NodeAlphaFe
}
})
// TODO: move the test to pod resource API test suite, see - https://github.com/kubernetes/kubernetes/issues/101945
ginkgo.It("should not report any memory data during request to pod resources GetAllocatableResources", func() {
endpoint, err := util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket)
framework.ExpectNoError(err)
cli, conn, err := podresources.GetV1Client(endpoint, defaultPodResourcesTimeout, defaultPodResourcesMaxSize)
framework.ExpectNoError(err)
defer conn.Close()
resp, err := cli.GetAllocatableResources(context.TODO(), &kubeletpodresourcesv1.AllocatableResourcesRequest{})
framework.ExpectNoError(err)
gomega.Expect(resp.Memory).To(gomega.BeEmpty())
})
// TODO: move the test to pod resource API test suite, see - https://github.com/kubernetes/kubernetes/issues/101945
ginkgo.It("should not report any memory data during request to pod resources List", func() {
testPod = f.PodClient().CreateSync(testPod)
endpoint, err := util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket)
framework.ExpectNoError(err)
cli, conn, err := podresources.GetV1Client(endpoint, defaultPodResourcesTimeout, defaultPodResourcesMaxSize)
framework.ExpectNoError(err)
defer conn.Close()
resp, err := cli.List(context.TODO(), &kubeletpodresourcesv1.ListPodResourcesRequest{})
framework.ExpectNoError(err)
for _, podResource := range resp.PodResources {
if podResource.Name != testPod.Name {
continue
}
for _, containerResource := range podResource.Containers {
gomega.Expect(containerResource.Memory).To(gomega.BeEmpty())
}
}
})
ginkgo.It("should succeed to start the pod", func() {
testPod = f.PodClient().CreateSync(testPod)