added podgc orphaned pod unit tests
This commit is contained in:
		| @@ -18,14 +18,18 @@ package podgc | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"encoding/json" | ||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/google/go-cmp/cmp" | 	"github.com/google/go-cmp/cmp" | ||||||
|  | 	"github.com/google/go-cmp/cmp/cmpopts" | ||||||
|  |  | ||||||
| 	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/labels" | 	"k8s.io/apimachinery/pkg/labels" | ||||||
| 	"k8s.io/apimachinery/pkg/util/sets" | 	"k8s.io/apimachinery/pkg/util/sets" | ||||||
|  | 	"k8s.io/apimachinery/pkg/util/strategicpatch" | ||||||
| 	"k8s.io/apimachinery/pkg/util/wait" | 	"k8s.io/apimachinery/pkg/util/wait" | ||||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||||
| 	"k8s.io/client-go/informers" | 	"k8s.io/client-go/informers" | ||||||
| @@ -43,6 +47,7 @@ import ( | |||||||
| 	"k8s.io/kubernetes/pkg/features" | 	"k8s.io/kubernetes/pkg/features" | ||||||
| 	"k8s.io/kubernetes/pkg/kubelet/eviction" | 	"k8s.io/kubernetes/pkg/kubelet/eviction" | ||||||
| 	testingclock "k8s.io/utils/clock/testing" | 	testingclock "k8s.io/utils/clock/testing" | ||||||
|  | 	"k8s.io/utils/pointer" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func alwaysReady() bool { return true } | func alwaysReady() bool { return true } | ||||||
| @@ -671,6 +676,128 @@ func TestGCTerminating(t *testing.T) { | |||||||
| 	testDeletingPodsMetrics(t, 7, metrics.PodGCReasonTerminatingOutOfService) | 	testDeletingPodsMetrics(t, 7, metrics.PodGCReasonTerminatingOutOfService) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestGCInspectingPatchedPodBeforeDeletion(t *testing.T) { | ||||||
|  | 	testCases := []struct { | ||||||
|  | 		name                 string | ||||||
|  | 		pod                  *v1.Pod | ||||||
|  | 		expectedPatchedPod   *v1.Pod | ||||||
|  | 		expectedDeleteAction *clienttesting.DeleteActionImpl | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			name: "orphaned pod should have DisruptionTarget condition added before deletion", | ||||||
|  | 			pod: &v1.Pod{ | ||||||
|  | 				ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 					Namespace: "default", | ||||||
|  | 					Name:      "testPod", | ||||||
|  | 				}, | ||||||
|  | 				Spec: v1.PodSpec{ | ||||||
|  | 					NodeName: "deletedNode", | ||||||
|  | 				}, | ||||||
|  | 				Status: v1.PodStatus{ | ||||||
|  | 					Phase: v1.PodRunning, | ||||||
|  | 					Conditions: []v1.PodCondition{ | ||||||
|  | 						{ | ||||||
|  | 							Type:   v1.PodReady, | ||||||
|  | 							Status: v1.ConditionTrue, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expectedPatchedPod: &v1.Pod{ | ||||||
|  | 				ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 					Namespace: "default", | ||||||
|  | 					Name:      "testPod", | ||||||
|  | 				}, | ||||||
|  | 				Spec: v1.PodSpec{ | ||||||
|  | 					NodeName: "deletedNode", | ||||||
|  | 				}, | ||||||
|  | 				Status: v1.PodStatus{ | ||||||
|  | 					Phase: v1.PodFailed, | ||||||
|  | 					Conditions: []v1.PodCondition{ | ||||||
|  | 						{ | ||||||
|  | 							Type:    v1.DisruptionTarget, | ||||||
|  | 							Status:  v1.ConditionTrue, | ||||||
|  | 							Reason:  "DeletionByPodGC", | ||||||
|  | 							Message: "PodGC: node no longer exists", | ||||||
|  | 						}, | ||||||
|  | 						{ | ||||||
|  | 							Type:   v1.PodReady, | ||||||
|  | 							Status: v1.ConditionTrue, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expectedDeleteAction: &clienttesting.DeleteActionImpl{ | ||||||
|  | 				Name:          "testPod", | ||||||
|  | 				DeleteOptions: metav1.DeleteOptions{GracePeriodSeconds: pointer.Int64(0)}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, test := range testCases { | ||||||
|  | 		t.Run(test.name, func(t *testing.T) { | ||||||
|  | 			_, ctx := ktesting.NewTestContext(t) | ||||||
|  | 			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, true)() | ||||||
|  |  | ||||||
|  | 			pods := []*v1.Pod{test.pod} | ||||||
|  |  | ||||||
|  | 			client := setupNewSimpleClient(nil, pods) | ||||||
|  | 			gcc, podInformer, _ := NewFromClient(ctx, client, -1) | ||||||
|  | 			gcc.quarantineTime = time.Duration(-1) | ||||||
|  | 			podInformer.Informer().GetStore().Add(test.pod) | ||||||
|  | 			gcc.gc(ctx) | ||||||
|  |  | ||||||
|  | 			actions := client.Actions() | ||||||
|  |  | ||||||
|  | 			var patchAction clienttesting.PatchAction | ||||||
|  | 			var deleteAction clienttesting.DeleteAction | ||||||
|  |  | ||||||
|  | 			for _, action := range actions { | ||||||
|  | 				if action.GetVerb() == "patch" { | ||||||
|  | 					patchAction = action.(clienttesting.PatchAction) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				if action.GetVerb() == "delete" { | ||||||
|  | 					deleteAction = action.(clienttesting.DeleteAction) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if patchAction != nil && test.expectedPatchedPod == nil { | ||||||
|  | 				t.Fatalf("Pod was pactched but expectedPatchedPod is nil") | ||||||
|  | 			} | ||||||
|  | 			if test.expectedPatchedPod != nil { | ||||||
|  | 				patchedPodBytes := patchAction.GetPatch() | ||||||
|  | 				originalPod, err := json.Marshal(test.pod) | ||||||
|  | 				if err != nil { | ||||||
|  | 					t.Fatalf("Failed to marshal original pod %#v: %v", originalPod, err) | ||||||
|  | 				} | ||||||
|  | 				updated, err := strategicpatch.StrategicMergePatch(originalPod, patchedPodBytes, v1.Pod{}) | ||||||
|  | 				if err != nil { | ||||||
|  | 					t.Fatalf("Failed to apply strategic merge patch %q on pod %#v: %v", patchedPodBytes, originalPod, err) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				updatedPod := &v1.Pod{} | ||||||
|  | 				if err := json.Unmarshal(updated, updatedPod); err != nil { | ||||||
|  | 					t.Fatalf("Failed to unmarshal updated pod %q: %v", updated, err) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				if diff := cmp.Diff(test.expectedPatchedPod, updatedPod, cmpopts.IgnoreFields(v1.Pod{}, "TypeMeta"), cmpopts.IgnoreFields(v1.PodCondition{}, "LastTransitionTime")); diff != "" { | ||||||
|  | 					t.Fatalf("Unexpected diff on pod (-want,+got):\n%s", diff) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if deleteAction != nil && test.expectedDeleteAction == nil { | ||||||
|  | 				t.Fatalf("Pod was deleted but expectedDeleteAction is nil") | ||||||
|  | 			} | ||||||
|  | 			if test.expectedDeleteAction != nil { | ||||||
|  | 				if diff := cmp.Diff(*test.expectedDeleteAction, deleteAction, cmpopts.IgnoreFields(clienttesting.DeleteActionImpl{}, "ActionImpl")); diff != "" { | ||||||
|  | 					t.Fatalf("Unexpected diff on deleteAction (-want,+got):\n%s", diff) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func verifyDeletedAndPatchedPods(t *testing.T, client *fake.Clientset, wantDeletedPodNames, wantPatchedPodNames sets.String) { | func verifyDeletedAndPatchedPods(t *testing.T, client *fake.Clientset, wantDeletedPodNames, wantPatchedPodNames sets.String) { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 	deletedPodNames := getDeletedPodNames(client) | 	deletedPodNames := getDeletedPodNames(client) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Chok Yip Lau
					Chok Yip Lau