mirror of
https://github.com/optim-enterprises-bv/kubernetes.git
synced 2025-11-01 10:48:15 +00:00
Mirror pods for static pods may not be created immediately during node startup because either the node is not registered or node informer is not synced. They will be created eventually when static pods are resynced (every 1-1.5 minutes). However, during this delay of 1-1.5 mins, kube-scheduler might overcommit resources to the node and eventually cause kubelet to reject pods with OutOfCPU/OutOfMemory/OutOfPods error. To ensure kube-scheduler is aware of static pod resource usage faster, mirror pods are created as soon as the node registers.
309 lines
12 KiB
Go
309 lines
12 KiB
Go
/*
|
|
Copyright 2015 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package pod
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
|
)
|
|
|
|
var (
|
|
mirrorPodUID = "mirror-pod-uid"
|
|
staticPodUID = "static-pod-uid"
|
|
normalPodUID = "normal-pod-uid"
|
|
mirrorPodName = "mirror-static-pod-name"
|
|
staticPodName = "mirror-static-pod-name"
|
|
normalPodName = "normal-pod-name"
|
|
|
|
mirrorPod = &v1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
UID: types.UID(mirrorPodUID),
|
|
Name: mirrorPodName,
|
|
Namespace: metav1.NamespaceDefault,
|
|
Annotations: map[string]string{
|
|
kubetypes.ConfigSourceAnnotationKey: "api",
|
|
kubetypes.ConfigMirrorAnnotationKey: "mirror",
|
|
kubetypes.ConfigHashAnnotationKey: "mirror",
|
|
},
|
|
},
|
|
}
|
|
staticPod = &v1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
UID: types.UID(staticPodUID),
|
|
Name: staticPodName,
|
|
Namespace: metav1.NamespaceDefault,
|
|
Annotations: map[string]string{kubetypes.ConfigSourceAnnotationKey: "file"},
|
|
},
|
|
}
|
|
normalPod = &v1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
UID: types.UID(normalPodUID),
|
|
Name: normalPodName,
|
|
Namespace: metav1.NamespaceDefault,
|
|
Annotations: map[string]string{},
|
|
},
|
|
}
|
|
sortOpt = cmpopts.SortSlices(func(a, b *v1.Pod) bool { return a.Name < b.Name })
|
|
)
|
|
|
|
func TestAddOrUpdatePod(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
isMirror bool
|
|
pod *v1.Pod
|
|
}{
|
|
{
|
|
name: "mirror pod",
|
|
pod: mirrorPod,
|
|
isMirror: true,
|
|
},
|
|
{
|
|
name: "static pod",
|
|
pod: staticPod,
|
|
isMirror: false,
|
|
},
|
|
{
|
|
name: "normal pod",
|
|
pod: normalPod,
|
|
isMirror: false,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
pm := &basicManager{
|
|
podByUID: make(map[kubetypes.ResolvedPodUID]*v1.Pod),
|
|
mirrorPodByUID: make(map[kubetypes.MirrorPodUID]*v1.Pod),
|
|
podByFullName: make(map[string]*v1.Pod),
|
|
mirrorPodByFullName: make(map[string]*v1.Pod),
|
|
translationByUID: make(map[kubetypes.MirrorPodUID]kubetypes.ResolvedPodUID),
|
|
}
|
|
pm.AddPod(test.pod)
|
|
verify(t, pm, test.isMirror, test.pod)
|
|
test.pod.Annotations["testLabel"] = "updated"
|
|
pm.UpdatePod(test.pod)
|
|
verify(t, pm, test.isMirror, test.pod)
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func verify(t *testing.T, pm *basicManager, isMirror bool, expectedPod *v1.Pod) {
|
|
fullName := fmt.Sprintf("%s_%s", expectedPod.Name, expectedPod.Namespace)
|
|
if isMirror {
|
|
inputPod, ok := pm.mirrorPodByUID[kubetypes.MirrorPodUID(expectedPod.UID)]
|
|
assert.True(t, ok)
|
|
assert.Empty(t, cmp.Diff(expectedPod, inputPod), "MirrorPodByUID map verification failed.")
|
|
inputPod, ok = pm.mirrorPodByFullName[fullName]
|
|
assert.True(t, ok)
|
|
assert.Empty(t, cmp.Diff(expectedPod, inputPod), "MirrorPodByFullName map verification failed.")
|
|
} else {
|
|
inputPod, ok := pm.podByUID[kubetypes.ResolvedPodUID(expectedPod.UID)]
|
|
assert.True(t, ok)
|
|
assert.Empty(t, cmp.Diff(expectedPod, inputPod), "PodByUID map verification failed.")
|
|
inputPod, ok = pm.podByFullName[fullName]
|
|
assert.True(t, ok)
|
|
assert.Empty(t, cmp.Diff(expectedPod, inputPod), "PodByFullName map verification failed.")
|
|
}
|
|
}
|
|
|
|
// Tests that pods/maps are properly set after the pod update, and the basic
|
|
// methods work correctly.
|
|
func TestGetSetPods(t *testing.T) {
|
|
testCase := []struct {
|
|
name string
|
|
podList []*v1.Pod
|
|
wantUID types.UID
|
|
wantPod *v1.Pod
|
|
isMirrorPod bool
|
|
expectPods []*v1.Pod
|
|
expectPod *v1.Pod
|
|
expectedMirrorPod *v1.Pod
|
|
expectUID types.UID
|
|
expectPodToMirrorMap map[kubetypes.ResolvedPodUID]kubetypes.MirrorPodUID
|
|
expectMirrorToPodMap map[kubetypes.MirrorPodUID]kubetypes.ResolvedPodUID
|
|
}{
|
|
{
|
|
name: "Get normal pod",
|
|
podList: []*v1.Pod{mirrorPod, staticPod, normalPod},
|
|
wantUID: types.UID("normal-pod-uid"),
|
|
wantPod: normalPod,
|
|
isMirrorPod: false,
|
|
expectedMirrorPod: nil,
|
|
expectPods: []*v1.Pod{staticPod, normalPod},
|
|
expectPod: normalPod,
|
|
expectUID: types.UID("normal-pod-uid"),
|
|
expectPodToMirrorMap: map[kubetypes.ResolvedPodUID]kubetypes.MirrorPodUID{
|
|
kubetypes.ResolvedPodUID("static-pod-uid"): kubetypes.MirrorPodUID("mirror-pod-uid"),
|
|
},
|
|
expectMirrorToPodMap: map[kubetypes.MirrorPodUID]kubetypes.ResolvedPodUID{
|
|
kubetypes.MirrorPodUID("mirror-pod-uid"): kubetypes.ResolvedPodUID("static-pod-uid"),
|
|
},
|
|
},
|
|
{
|
|
name: "Get static pod",
|
|
podList: []*v1.Pod{mirrorPod, staticPod, normalPod},
|
|
wantUID: types.UID("static-pod-uid"),
|
|
wantPod: staticPod,
|
|
isMirrorPod: false,
|
|
expectPods: []*v1.Pod{staticPod, normalPod},
|
|
expectPod: staticPod,
|
|
expectedMirrorPod: mirrorPod,
|
|
expectUID: types.UID("static-pod-uid"),
|
|
expectPodToMirrorMap: map[kubetypes.ResolvedPodUID]kubetypes.MirrorPodUID{
|
|
kubetypes.ResolvedPodUID("static-pod-uid"): kubetypes.MirrorPodUID("mirror-pod-uid"),
|
|
},
|
|
expectMirrorToPodMap: map[kubetypes.MirrorPodUID]kubetypes.ResolvedPodUID{
|
|
kubetypes.MirrorPodUID("mirror-pod-uid"): kubetypes.ResolvedPodUID("static-pod-uid"),
|
|
},
|
|
},
|
|
{
|
|
name: "Get mirror pod",
|
|
podList: []*v1.Pod{mirrorPod, staticPod, normalPod},
|
|
wantUID: types.UID("static-pod-uid"),
|
|
wantPod: mirrorPod,
|
|
isMirrorPod: true,
|
|
expectPods: []*v1.Pod{staticPod, normalPod},
|
|
expectPod: staticPod,
|
|
expectedMirrorPod: mirrorPod,
|
|
expectUID: types.UID("static-pod-uid"),
|
|
expectPodToMirrorMap: map[kubetypes.ResolvedPodUID]kubetypes.MirrorPodUID{
|
|
kubetypes.ResolvedPodUID("static-pod-uid"): kubetypes.MirrorPodUID("mirror-pod-uid"),
|
|
},
|
|
expectMirrorToPodMap: map[kubetypes.MirrorPodUID]kubetypes.ResolvedPodUID{
|
|
kubetypes.MirrorPodUID("mirror-pod-uid"): kubetypes.ResolvedPodUID("static-pod-uid"),
|
|
},
|
|
},
|
|
}
|
|
for _, test := range testCase {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
podManager := NewBasicPodManager()
|
|
podManager.SetPods(test.podList)
|
|
actualPods := podManager.GetPods()
|
|
assert.Empty(t, cmp.Diff(test.expectPods, actualPods, sortOpt), "actualPods and expectPods differ")
|
|
|
|
uid := podManager.TranslatePodUID(test.wantPod.UID)
|
|
assert.Equal(t, kubetypes.ResolvedPodUID(test.expectUID), uid, "unable to translate pod UID")
|
|
|
|
fullName := fmt.Sprintf("%s_%s", test.wantPod.Name, test.wantPod.Namespace)
|
|
actualPod, ok := podManager.GetPodByFullName(fullName)
|
|
assert.True(t, ok)
|
|
assert.Empty(t, cmp.Diff(test.expectPod, actualPod), "actualPod by full name and expectGetPod differ")
|
|
|
|
actualPod, ok = podManager.GetPodByName(test.wantPod.Namespace, test.wantPod.Name)
|
|
assert.True(t, ok)
|
|
assert.Empty(t, cmp.Diff(test.expectPod, actualPod), "actualPod by name and expectGetPod differ")
|
|
|
|
actualPod, ok = podManager.GetPodByUID(test.wantUID)
|
|
assert.True(t, ok)
|
|
assert.Empty(t, cmp.Diff(test.expectPod, actualPod), "actualPod and expectGetPod differ")
|
|
|
|
podToMirror, mirrorToPod := podManager.GetUIDTranslations()
|
|
assert.Empty(t, cmp.Diff(test.expectPodToMirrorMap, podToMirror), "podToMirror and expectPodToMirror differ")
|
|
assert.Empty(t, cmp.Diff(test.expectMirrorToPodMap, mirrorToPod), "mirrorToPod and expectMirrorToPod differ")
|
|
|
|
actualPod, ok = podManager.GetMirrorPodByPod(test.wantPod)
|
|
assert.Equal(t, test.expectedMirrorPod != nil, ok)
|
|
assert.Empty(t, cmp.Diff(test.expectedMirrorPod, actualPod), "actualPod and expectShouldBeMirrorPod differ")
|
|
|
|
actualPod, actualMirrorPod, isMirrorPod := podManager.GetPodAndMirrorPod(test.wantPod)
|
|
assert.Equal(t, test.isMirrorPod, isMirrorPod)
|
|
assert.Empty(t, cmp.Diff(test.expectPod, actualPod), "actualPod and expectGetPod differ")
|
|
assert.Empty(t, cmp.Diff(test.expectedMirrorPod, actualMirrorPod), "actualMirrorPod and shouldBeMirrorPod differ")
|
|
|
|
isMirrorPod = IsMirrorPodOf(test.wantPod, mirrorPod)
|
|
assert.Equal(t, test.isMirrorPod, isMirrorPod)
|
|
|
|
if test.isMirrorPod {
|
|
actualPod, ok = podManager.GetPodByMirrorPod(test.wantPod)
|
|
assert.Equal(t, test.expectedMirrorPod != nil, ok)
|
|
assert.Empty(t, cmp.Diff(test.expectPod, actualPod), "actualPod by name and expectGetPod differ")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRemovePods(t *testing.T) {
|
|
testCase := []struct {
|
|
name string
|
|
podList []*v1.Pod
|
|
needToRemovePod *v1.Pod
|
|
expectPods []*v1.Pod
|
|
expectMirrorPods []*v1.Pod
|
|
expectOrphanedMirrorPodFullnames []string
|
|
}{
|
|
{
|
|
name: "Remove mirror pod",
|
|
podList: []*v1.Pod{mirrorPod, staticPod, normalPod},
|
|
needToRemovePod: mirrorPod,
|
|
expectPods: []*v1.Pod{normalPod, staticPod},
|
|
expectMirrorPods: []*v1.Pod{},
|
|
},
|
|
{
|
|
name: "Remove static pod",
|
|
podList: []*v1.Pod{mirrorPod, staticPod, normalPod},
|
|
needToRemovePod: staticPod,
|
|
expectPods: []*v1.Pod{normalPod},
|
|
expectMirrorPods: []*v1.Pod{mirrorPod},
|
|
expectOrphanedMirrorPodFullnames: []string{"mirror-static-pod-name_default"},
|
|
},
|
|
{
|
|
name: "Remove normal pod",
|
|
podList: []*v1.Pod{mirrorPod, staticPod, normalPod},
|
|
needToRemovePod: normalPod,
|
|
expectPods: []*v1.Pod{staticPod},
|
|
expectMirrorPods: []*v1.Pod{mirrorPod},
|
|
},
|
|
}
|
|
|
|
for _, test := range testCase {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
podManager := NewBasicPodManager()
|
|
podManager.SetPods(test.podList)
|
|
podManager.RemovePod(test.needToRemovePod)
|
|
actualPods1 := podManager.GetPods()
|
|
actualPods2, actualMirrorPods, orphanedMirrorPodFullnames := podManager.GetPodsAndMirrorPods()
|
|
// Check if the actual pods and mirror pods match the expected ones.
|
|
assert.Empty(t, cmp.Diff(actualPods1, actualPods2, sortOpt), "actualPods by GetPods() and GetPodsAndMirrorPods() differ")
|
|
assert.Empty(t, cmp.Diff(test.expectPods, actualPods1, sortOpt), "actualPods and expectPods differ")
|
|
assert.Empty(t, cmp.Diff(test.expectMirrorPods, actualMirrorPods, sortOpt), "actualMirrorPods and expectMirrorPods differ")
|
|
assert.Empty(t, cmp.Diff(test.expectOrphanedMirrorPodFullnames, orphanedMirrorPodFullnames), "orphanedMirrorPodFullnames and expectOrphanedMirrorPodFullnames differ")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetStaticPodToMirrorPodMap(t *testing.T) {
|
|
podManager := NewBasicPodManager()
|
|
podManager.SetPods([]*v1.Pod{mirrorPod, staticPod, normalPod})
|
|
m := podManager.GetStaticPodToMirrorPodMap()
|
|
if len(m) != 1 {
|
|
t.Fatalf("GetStaticPodToMirrorPodMap(): got %d static pods, wanted 1 static pod", len(m))
|
|
}
|
|
if gotMirrorPod, ok := m[staticPod]; !ok || gotMirrorPod.UID != mirrorPod.UID {
|
|
t.Fatalf("GetStaticPodToMirrorPodMap() did not return the correct mirror pod UID %s, wanted mirror pod UID %s", gotMirrorPod.UID, mirrorPod.UID)
|
|
}
|
|
}
|