Merge pull request #126895 from googs1025/refactor/kubelet_podmanager_ut

refactor(kubelet): implement a table-driven test for pod_manager
This commit is contained in:
Kubernetes Prow Robot 2024-09-20 04:12:07 +01:00 committed by GitHub
commit a45742b271
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 249 additions and 121 deletions

View File

@ -123,7 +123,7 @@ func NewBasicPodManager() Manager {
return pm return pm
} }
// Set the internal pods based on the new pods. // SetPods set the internal pods based on the new pods.
func (pm *basicManager) SetPods(newPods []*v1.Pod) { func (pm *basicManager) SetPods(newPods []*v1.Pod) {
pm.lock.Lock() pm.lock.Lock()
defer pm.lock.Unlock() defer pm.lock.Unlock()
@ -336,5 +336,4 @@ func (pm *basicManager) GetPodAndMirrorPod(aPod *v1.Pod) (pod, mirrorPod *v1.Pod
return pm.podByFullName[fullName], aPod, true return pm.podByFullName[fullName], aPod, true
} }
return aPod, pm.mirrorPodByFullName[fullName], false return aPod, pm.mirrorPodByFullName[fullName], false
} }

View File

@ -17,151 +17,280 @@ limitations under the License.
package pod package pod
import ( import (
"reflect" "fmt"
"testing" "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" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
podtest "k8s.io/kubernetes/pkg/kubelet/pod/testing"
kubetypes "k8s.io/kubernetes/pkg/kubelet/types" kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
) )
// Stub out mirror client for testing purpose. var (
func newTestManager() (*basicManager, *podtest.FakeMirrorClient) { mirrorPodUID = "mirror-pod-uid"
fakeMirrorClient := podtest.NewFakeMirrorClient() staticPodUID = "static-pod-uid"
manager := NewBasicPodManager().(*basicManager) normalPodUID = "normal-pod-uid"
return manager, fakeMirrorClient 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 // Tests that pods/maps are properly set after the pod update, and the basic
// methods work correctly. // methods work correctly.
func TestGetSetPods(t *testing.T) { func TestGetSetPods(t *testing.T) {
mirrorPod := &v1.Pod{ testCase := []struct {
ObjectMeta: metav1.ObjectMeta{ name string
UID: "987654321", podList []*v1.Pod
Name: "bar", wantUID types.UID
Namespace: "default", wantPod *v1.Pod
Annotations: map[string]string{ isMirrorPod bool
kubetypes.ConfigSourceAnnotationKey: "api", expectPods []*v1.Pod
kubetypes.ConfigMirrorAnnotationKey: "mirror", expectPod *v1.Pod
}, expectedMirrorPod *v1.Pod
}, expectUID types.UID
} expectPodToMirrorMap map[kubetypes.ResolvedPodUID]kubetypes.MirrorPodUID
staticPod := &v1.Pod{ expectMirrorToPodMap map[kubetypes.MirrorPodUID]kubetypes.ResolvedPodUID
ObjectMeta: metav1.ObjectMeta{ }{
UID: "123456789",
Name: "bar",
Namespace: "default",
Annotations: map[string]string{kubetypes.ConfigSourceAnnotationKey: "file"},
},
}
expectedPods := []*v1.Pod{
{ {
ObjectMeta: metav1.ObjectMeta{ name: "Get normal pod",
UID: "999999999", podList: []*v1.Pod{mirrorPod, staticPod, normalPod},
Name: "taco", wantUID: types.UID("normal-pod-uid"),
Namespace: "default", wantPod: normalPod,
Annotations: map[string]string{kubetypes.ConfigSourceAnnotationKey: "api"}, 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"),
}, },
}, },
staticPod,
} }
updates := append(expectedPods, mirrorPod) for _, test := range testCase {
podManager, _ := newTestManager() t.Run(test.name, func(t *testing.T) {
podManager.SetPods(updates) podManager := NewBasicPodManager()
podManager.SetPods(test.podList)
actualPods := podManager.GetPods()
assert.Empty(t, cmp.Diff(test.expectPods, actualPods, sortOpt), "actualPods and expectPods differ")
// Tests that all regular pods are recorded correctly. uid := podManager.TranslatePodUID(test.wantPod.UID)
actualPods := podManager.GetPods() assert.Equal(t, kubetypes.ResolvedPodUID(test.expectUID), uid, "unable to translate pod UID")
if len(actualPods) != len(expectedPods) {
t.Errorf("expected %d pods, got %d pods; expected pods %#v, got pods %#v", len(expectedPods), len(actualPods), fullName := fmt.Sprintf("%s_%s", test.wantPod.Name, test.wantPod.Namespace)
expectedPods, actualPods) actualPod, ok := podManager.GetPodByFullName(fullName)
} assert.True(t, ok)
for _, expected := range expectedPods { assert.Empty(t, cmp.Diff(test.expectPod, actualPod), "actualPod by full name and expectGetPod differ")
found := false
for _, actual := range actualPods { actualPod, ok = podManager.GetPodByName(test.wantPod.Namespace, test.wantPod.Name)
if actual.UID == expected.UID { assert.True(t, ok)
if !reflect.DeepEqual(&expected, &actual) { assert.Empty(t, cmp.Diff(test.expectPod, actualPod), "actualPod by name and expectGetPod differ")
t.Errorf("pod was recorded incorrectly. expect: %#v, got: %#v", expected, actual)
} actualPod, ok = podManager.GetPodByUID(test.wantUID)
found = true assert.True(t, ok)
break 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")
} }
} })
if !found {
t.Errorf("pod %q was not found in %#v", expected.UID, actualPods)
}
} }
// Tests UID translation works as expected. Convert static pod UID for comparison only.
if uid := podManager.TranslatePodUID(mirrorPod.UID); uid != kubetypes.ResolvedPodUID(staticPod.UID) {
t.Errorf("unable to translate UID %q to the static POD's UID %q; %#v",
mirrorPod.UID, staticPod.UID, podManager.mirrorPodByUID)
}
// Test the basic Get methods.
actualPod, ok := podManager.GetPodByFullName("bar_default")
if !ok || !reflect.DeepEqual(actualPod, staticPod) {
t.Errorf("unable to get pod by full name; expected: %#v, got: %#v", staticPod, actualPod)
}
actualPod, ok = podManager.GetPodByName("default", "bar")
if !ok || !reflect.DeepEqual(actualPod, staticPod) {
t.Errorf("unable to get pod by name; expected: %#v, got: %#v", staticPod, actualPod)
}
} }
func TestRemovePods(t *testing.T) { func TestRemovePods(t *testing.T) {
mirrorPod := &v1.Pod{ testCase := []struct {
ObjectMeta: metav1.ObjectMeta{ name string
UID: types.UID("mirror-pod-uid"), podList []*v1.Pod
Name: "mirror-static-pod-name", needToRemovePod *v1.Pod
Namespace: metav1.NamespaceDefault, expectPods []*v1.Pod
Annotations: map[string]string{ expectMirrorPods []*v1.Pod
kubetypes.ConfigSourceAnnotationKey: "api", expectOrphanedMirrorPodFullnames []string
kubetypes.ConfigMirrorAnnotationKey: "mirror", }{
},
},
}
staticPod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: types.UID("static-pod-uid"),
Name: "mirror-static-pod-name",
Namespace: metav1.NamespaceDefault,
Annotations: map[string]string{kubetypes.ConfigSourceAnnotationKey: "file"},
},
}
expectedPods := []*v1.Pod{
{ {
ObjectMeta: metav1.ObjectMeta{ name: "Remove mirror pod",
UID: types.UID("extra-pod-uid"), podList: []*v1.Pod{mirrorPod, staticPod, normalPod},
Name: "extra-pod-name", needToRemovePod: mirrorPod,
Namespace: metav1.NamespaceDefault, expectPods: []*v1.Pod{normalPod, staticPod},
Annotations: map[string]string{kubetypes.ConfigSourceAnnotationKey: "api"}, 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},
}, },
staticPod,
}
updates := append(expectedPods, mirrorPod)
podManager, _ := newTestManager()
podManager.SetPods(updates)
podManager.RemovePod(staticPod)
actualPods := podManager.GetPods()
if len(actualPods) == len(expectedPods) {
t.Fatalf("Run RemovePod() error, expected %d pods, got %d pods; ", len(expectedPods)-1, len(actualPods))
} }
_, _, orphanedMirrorPodNames := podManager.GetPodsAndMirrorPods() for _, test := range testCase {
expectedOrphanedMirrorPodNameNum := 1 t.Run(test.name, func(t *testing.T) {
if len(orphanedMirrorPodNames) != expectedOrphanedMirrorPodNameNum { podManager := NewBasicPodManager()
t.Fatalf("Run getOrphanedMirrorPodNames() error, expected %d orphaned mirror pods, got %d orphaned mirror pods; ", expectedOrphanedMirrorPodNameNum, len(orphanedMirrorPodNames)) podManager.SetPods(test.podList)
} podManager.RemovePod(test.needToRemovePod)
actualPods1 := podManager.GetPods()
expectedOrphanedMirrorPodName := mirrorPod.Name + "_" + mirrorPod.Namespace actualPods2, actualMirrorPods, orphanedMirrorPodFullnames := podManager.GetPodsAndMirrorPods()
if orphanedMirrorPodNames[0] != expectedOrphanedMirrorPodName { // Check if the actual pods and mirror pods match the expected ones.
t.Fatalf("Run getOrphanedMirrorPodNames() error, expected orphaned mirror pod name : %s, got orphaned mirror pod name %s; ", expectedOrphanedMirrorPodName, orphanedMirrorPodNames[0]) 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")
})
} }
} }