From b79dfd09b0b9a8173ec23e5a72b3a0c444863dee Mon Sep 17 00:00:00 2001 From: Austin Abro Date: Tue, 25 Feb 2025 03:51:38 +0000 Subject: [PATCH] refactor Signed-off-by: Austin Abro --- pkg/kube/statuswait.go | 9 +++-- pkg/kube/statuswait_test.go | 70 +++++++++++++++++++++++++++---------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/pkg/kube/statuswait.go b/pkg/kube/statuswait.go index 3c1e90a36..baf5814b1 100644 --- a/pkg/kube/statuswait.go +++ b/pkg/kube/statuswait.go @@ -187,6 +187,11 @@ func statusObserver(cancel context.CancelFunc, desired status.Status, logFn func if rs == nil { continue } + // If a resource is already deleted before waiting has started, it will show as unknown + // this check ensures we don't wait forever for a resource that is already deleted + if rs.Status == status.UnknownStatus && desired == status.NotFoundStatus { + continue + } rss = append(rss, rs) if rs.Status != desired { nonDesiredResources = append(nonDesiredResources, rs) @@ -199,12 +204,12 @@ func statusObserver(cancel context.CancelFunc, desired status.Status, logFn func } if len(nonDesiredResources) > 0 { - // Log only the first resource so the user knows what they're waiting for without being overwhelmed + // Log a single resource so the user knows what they're waiting for without an overwhelming amount of output sort.Slice(nonDesiredResources, func(i, j int) bool { return nonDesiredResources[i].Identifier.Name < nonDesiredResources[j].Identifier.Name }) first := nonDesiredResources[0] - logFn("waiting for resource: name: %s, kind: %s, desired status: %s, actual status: %s", + logFn("waiting for resource: name: %s, kind: %s, desired status: %s, actual status: %s \n", first.Identifier.Name, first.Identifier.GroupKind.Kind, desired, first.Status) } } diff --git a/pkg/kube/statuswait_test.go b/pkg/kube/statuswait_test.go index df16bf7e9..2b10dfef1 100644 --- a/pkg/kube/statuswait_test.go +++ b/pkg/kube/statuswait_test.go @@ -160,6 +160,18 @@ func getGVR(t *testing.T, mapper meta.RESTMapper, obj *unstructured.Unstructured return mapping.Resource } +func getUnstructuredObjsFromManifests(t *testing.T, manifests []string) []runtime.Object { + objects := []runtime.Object{} + for _, manifest := range manifests { + m := make(map[string]interface{}) + err := yaml.Unmarshal([]byte(manifest), &m) + assert.NoError(t, err) + resource := &unstructured.Unstructured{Object: m} + objects = append(objects, resource) + } + return objects +} + func TestStatusWaitForDelete(t *testing.T) { t.Parallel() tests := []struct { @@ -190,7 +202,6 @@ func TestStatusWaitForDelete(t *testing.T) { fakeClient := dynamicfake.NewSimpleDynamicClient(scheme.Scheme) fakeMapper := testutil.NewFakeRESTMapper( v1.SchemeGroupVersion.WithKind("Pod"), - appsv1.SchemeGroupVersion.WithKind("Deployment"), batchv1.SchemeGroupVersion.WithKind("Job"), ) statusWaiter := statusWaiter{ @@ -198,31 +209,25 @@ func TestStatusWaitForDelete(t *testing.T) { client: fakeClient, log: t.Logf, } - createdObjs := []runtime.Object{} - for _, manifest := range tt.manifestsToCreate { - m := make(map[string]interface{}) - err := yaml.Unmarshal([]byte(manifest), &m) - assert.NoError(t, err) - resource := &unstructured.Unstructured{Object: m} - createdObjs = append(createdObjs, resource) - gvr := getGVR(t, fakeMapper, resource) - err = fakeClient.Tracker().Create(gvr, resource, resource.GetNamespace()) + objsToCreate := getUnstructuredObjsFromManifests(t, tt.manifestsToCreate) + for _, objToCreate := range objsToCreate { + u := objToCreate.(*unstructured.Unstructured) + gvr := getGVR(t, fakeMapper, u) + err := fakeClient.Tracker().Create(gvr, u, u.GetNamespace()) assert.NoError(t, err) } - for _, manifest := range tt.manifestsToDelete { - m := make(map[string]interface{}) - err := yaml.Unmarshal([]byte(manifest), &m) - assert.NoError(t, err) - resource := &unstructured.Unstructured{Object: m} - gvr := getGVR(t, fakeMapper, resource) + objsToDelete := getUnstructuredObjsFromManifests(t, tt.manifestsToDelete) + for _, objToDelete := range objsToDelete { + u := objToDelete.(*unstructured.Unstructured) + gvr := getGVR(t, fakeMapper, u) go func() { time.Sleep(timeUntilPodDelete) - err = fakeClient.Tracker().Delete(gvr, resource.GetNamespace(), resource.GetName()) + err := fakeClient.Tracker().Delete(gvr, u.GetNamespace(), u.GetName()) assert.NoError(t, err) }() } resourceList := ResourceList{} - for _, obj := range createdObjs { + for _, obj := range objsToCreate { list, err := c.Build(objBody(obj), false) assert.NoError(t, err) resourceList = append(resourceList, list...) @@ -237,6 +242,35 @@ func TestStatusWaitForDelete(t *testing.T) { } } +func TestStatusWaitForDeleteNonExistentObject(t *testing.T) { + t.Parallel() + c := newTestClient(t) + timeout := time.Second + fakeClient := dynamicfake.NewSimpleDynamicClient(scheme.Scheme) + fakeMapper := testutil.NewFakeRESTMapper( + v1.SchemeGroupVersion.WithKind("Pod"), + ) + statusWaiter := statusWaiter{ + restMapper: fakeMapper, + client: fakeClient, + log: t.Logf, + } + createdObjs := []runtime.Object{} + m := make(map[string]interface{}) + err := yaml.Unmarshal([]byte(podCurrentManifest), &m) + assert.NoError(t, err) + resource := &unstructured.Unstructured{Object: m} + createdObjs = append(createdObjs, resource) + resourceList := ResourceList{} + for _, obj := range createdObjs { + list, err := c.Build(objBody(obj), false) + assert.NoError(t, err) + resourceList = append(resourceList, list...) + } + err = statusWaiter.WaitForDelete(resourceList, timeout) + assert.NoError(t, err) +} + func TestStatusWait(t *testing.T) { t.Parallel() tests := []struct {