...

Source file src/sigs.k8s.io/cli-utils/test/e2e/skip_invalid_test.go

Documentation: sigs.k8s.io/cli-utils/test/e2e

     1  // Copyright 2022 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package e2e
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	. "github.com/onsi/ginkgo/v2"
    11  	. "github.com/onsi/gomega"
    12  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    13  	"k8s.io/apimachinery/pkg/runtime/schema"
    14  	"k8s.io/apimachinery/pkg/util/validation/field"
    15  	"sigs.k8s.io/cli-utils/pkg/apply"
    16  	"sigs.k8s.io/cli-utils/pkg/apply/event"
    17  	"sigs.k8s.io/cli-utils/pkg/inventory"
    18  	"sigs.k8s.io/cli-utils/pkg/object"
    19  	"sigs.k8s.io/cli-utils/pkg/object/dependson"
    20  	"sigs.k8s.io/cli-utils/pkg/object/graph"
    21  	"sigs.k8s.io/cli-utils/pkg/object/mutation"
    22  	"sigs.k8s.io/cli-utils/pkg/object/validation"
    23  	"sigs.k8s.io/cli-utils/pkg/testutil"
    24  	"sigs.k8s.io/cli-utils/test/e2e/e2eutil"
    25  	"sigs.k8s.io/cli-utils/test/e2e/invconfig"
    26  	"sigs.k8s.io/controller-runtime/pkg/client"
    27  )
    28  
    29  func skipInvalidTest(ctx context.Context, c client.Client, invConfig invconfig.InventoryConfig, inventoryName, namespaceName string) {
    30  	By("apply valid objects and skip invalid objects")
    31  	applier := invConfig.ApplierFactoryFunc()
    32  
    33  	inv := invConfig.InvWrapperFunc(invConfig.FactoryFunc(inventoryName, namespaceName, "test"))
    34  
    35  	fields := struct{ Namespace string }{Namespace: namespaceName}
    36  	// valid pod
    37  	pod1Obj := e2eutil.WithNamespace(e2eutil.ManifestToUnstructured(pod1), namespaceName)
    38  	// valid deployment with dependency
    39  	deployment1Obj := e2eutil.WithDependsOn(e2eutil.WithNamespace(e2eutil.ManifestToUnstructured(deployment1), namespaceName),
    40  		fmt.Sprintf("/namespaces/%s/Pod/%s", namespaceName, pod1Obj.GetName()))
    41  	// external/missing dependency
    42  	pod3Obj := e2eutil.WithDependsOn(e2eutil.WithNamespace(e2eutil.ManifestToUnstructured(pod3), namespaceName),
    43  		fmt.Sprintf("/namespaces/%s/Pod/pod0", namespaceName))
    44  	// cyclic dependency (podB)
    45  	podAObj := e2eutil.TemplateToUnstructured(podATemplate, fields)
    46  	// cyclic dependency (podA) & invalid source reference (dependency not in object set)
    47  	podBObj := e2eutil.TemplateToUnstructured(invalidMutationPodBTemplate, fields)
    48  	// missing name
    49  	invalidPodObj := e2eutil.TemplateToUnstructured(invalidPodTemplate, fields)
    50  
    51  	resources := []*unstructured.Unstructured{
    52  		pod1Obj,
    53  		deployment1Obj,
    54  		pod3Obj,
    55  		podAObj,
    56  		podBObj,
    57  		invalidPodObj,
    58  	}
    59  
    60  	applierEvents := e2eutil.RunCollect(applier.Run(ctx, inv, resources, apply.ApplierOptions{
    61  		EmitStatusEvents: false,
    62  		ValidationPolicy: validation.SkipInvalid,
    63  	}))
    64  
    65  	expEvents := []testutil.ExpEvent{
    66  		{
    67  			// invalid pod validation error
    68  			EventType: event.ValidationType,
    69  			ValidationEvent: &testutil.ExpValidationEvent{
    70  				Identifiers: object.ObjMetadataSet{
    71  					object.UnstructuredToObjMetadata(invalidPodObj),
    72  				},
    73  				Error: testutil.EqualError(
    74  					validation.NewError(
    75  						field.Required(field.NewPath("metadata", "name"), "name is required"),
    76  						object.UnstructuredToObjMetadata(invalidPodObj),
    77  					),
    78  				),
    79  			},
    80  		},
    81  		{
    82  			// Pod3 validation error
    83  			EventType: event.ValidationType,
    84  			ValidationEvent: &testutil.ExpValidationEvent{
    85  				Identifiers: object.ObjMetadataSet{
    86  					object.UnstructuredToObjMetadata(pod3Obj),
    87  				},
    88  				Error: testutil.EqualError(
    89  					validation.NewError(
    90  						object.InvalidAnnotationError{
    91  							Annotation: dependson.Annotation,
    92  							Cause: graph.ExternalDependencyError{
    93  								Edge: graph.Edge{
    94  									From: object.UnstructuredToObjMetadata(pod3Obj),
    95  									To: object.ObjMetadata{
    96  										GroupKind: schema.GroupKind{Kind: "Pod"},
    97  										Name:      "pod0",
    98  										Namespace: namespaceName,
    99  									},
   100  								},
   101  							},
   102  						},
   103  						object.UnstructuredToObjMetadata(pod3Obj),
   104  					),
   105  				),
   106  			},
   107  		},
   108  		{
   109  			// PodB validation error
   110  			EventType: event.ValidationType,
   111  			ValidationEvent: &testutil.ExpValidationEvent{
   112  				Identifiers: object.ObjMetadataSet{
   113  					object.UnstructuredToObjMetadata(podBObj),
   114  				},
   115  				Error: testutil.EqualError(
   116  					validation.NewError(
   117  						object.InvalidAnnotationError{
   118  							Annotation: mutation.Annotation,
   119  							Cause: graph.ExternalDependencyError{
   120  								Edge: graph.Edge{
   121  									From: object.UnstructuredToObjMetadata(podBObj),
   122  									To: object.ObjMetadata{
   123  										GroupKind: schema.GroupKind{Kind: "Pod"},
   124  										Name:      "pod-a",
   125  									},
   126  								},
   127  							},
   128  						},
   129  						object.UnstructuredToObjMetadata(podBObj),
   130  					),
   131  				),
   132  			},
   133  		},
   134  		{
   135  			// Cyclic Dependency validation error
   136  			EventType: event.ValidationType,
   137  			ValidationEvent: &testutil.ExpValidationEvent{
   138  				Identifiers: object.ObjMetadataSet{
   139  					object.UnstructuredToObjMetadata(podAObj),
   140  					object.UnstructuredToObjMetadata(podBObj),
   141  				},
   142  				Error: testutil.EqualError(
   143  					validation.NewError(
   144  						graph.CyclicDependencyError{
   145  							Edges: []graph.Edge{
   146  								{
   147  									From: object.UnstructuredToObjMetadata(podAObj),
   148  									To:   object.UnstructuredToObjMetadata(podBObj),
   149  								},
   150  								{
   151  									From: object.UnstructuredToObjMetadata(podBObj),
   152  									To:   object.UnstructuredToObjMetadata(podAObj),
   153  								},
   154  							},
   155  						},
   156  						object.UnstructuredToObjMetadata(podAObj),
   157  						object.UnstructuredToObjMetadata(podBObj),
   158  					),
   159  				),
   160  			},
   161  		},
   162  		{
   163  			// InitTask
   164  			EventType: event.InitType,
   165  			InitEvent: &testutil.ExpInitEvent{},
   166  		},
   167  		{
   168  			// InvAddTask start
   169  			EventType: event.ActionGroupType,
   170  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   171  				Action:    event.InventoryAction,
   172  				GroupName: "inventory-add-0",
   173  				Type:      event.Started,
   174  			},
   175  		},
   176  		{
   177  			// InvAddTask finished
   178  			EventType: event.ActionGroupType,
   179  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   180  				Action:    event.InventoryAction,
   181  				GroupName: "inventory-add-0",
   182  				Type:      event.Finished,
   183  			},
   184  		},
   185  		{
   186  			// ApplyTask start
   187  			EventType: event.ActionGroupType,
   188  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   189  				Action:    event.ApplyAction,
   190  				GroupName: "apply-0",
   191  				Type:      event.Started,
   192  			},
   193  		},
   194  		{
   195  			// Apply Pod1
   196  			EventType: event.ApplyType,
   197  			ApplyEvent: &testutil.ExpApplyEvent{
   198  				GroupName:  "apply-0",
   199  				Status:     event.ApplySuccessful,
   200  				Identifier: object.UnstructuredToObjMetadata(pod1Obj),
   201  			},
   202  		},
   203  		{
   204  			// ApplyTask finished
   205  			EventType: event.ActionGroupType,
   206  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   207  				Action:    event.ApplyAction,
   208  				GroupName: "apply-0",
   209  				Type:      event.Finished,
   210  			},
   211  		},
   212  		{
   213  			// WaitTask start
   214  			EventType: event.ActionGroupType,
   215  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   216  				Action:    event.WaitAction,
   217  				GroupName: "wait-0",
   218  				Type:      event.Started,
   219  			},
   220  		},
   221  		{
   222  			// Pod1 reconcile Pending.
   223  			EventType: event.WaitType,
   224  			WaitEvent: &testutil.ExpWaitEvent{
   225  				GroupName:  "wait-0",
   226  				Status:     event.ReconcilePending,
   227  				Identifier: object.UnstructuredToObjMetadata(pod1Obj),
   228  			},
   229  		},
   230  		{
   231  			// Pod1 confirmed Current.
   232  			EventType: event.WaitType,
   233  			WaitEvent: &testutil.ExpWaitEvent{
   234  				GroupName:  "wait-0",
   235  				Status:     event.ReconcileSuccessful,
   236  				Identifier: object.UnstructuredToObjMetadata(pod1Obj),
   237  			},
   238  		},
   239  		{
   240  			// WaitTask finished
   241  			EventType: event.ActionGroupType,
   242  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   243  				Action:    event.WaitAction,
   244  				GroupName: "wait-0",
   245  				Type:      event.Finished,
   246  			},
   247  		},
   248  		{
   249  			// ApplyTask start
   250  			EventType: event.ActionGroupType,
   251  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   252  				Action:    event.ApplyAction,
   253  				GroupName: "apply-1",
   254  				Type:      event.Started,
   255  			},
   256  		},
   257  		{
   258  			// Apply Deployment1
   259  			EventType: event.ApplyType,
   260  			ApplyEvent: &testutil.ExpApplyEvent{
   261  				GroupName:  "apply-1",
   262  				Status:     event.ApplySuccessful,
   263  				Identifier: object.UnstructuredToObjMetadata(deployment1Obj),
   264  			},
   265  		},
   266  		{
   267  			// ApplyTask finished
   268  			EventType: event.ActionGroupType,
   269  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   270  				Action:    event.ApplyAction,
   271  				GroupName: "apply-1",
   272  				Type:      event.Finished,
   273  			},
   274  		},
   275  		{
   276  			// WaitTask start
   277  			EventType: event.ActionGroupType,
   278  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   279  				Action:    event.WaitAction,
   280  				GroupName: "wait-1",
   281  				Type:      event.Started,
   282  			},
   283  		},
   284  		{
   285  			// Deployment1 reconcile Pending.
   286  			EventType: event.WaitType,
   287  			WaitEvent: &testutil.ExpWaitEvent{
   288  				GroupName:  "wait-1",
   289  				Status:     event.ReconcilePending,
   290  				Identifier: object.UnstructuredToObjMetadata(deployment1Obj),
   291  			},
   292  		},
   293  		{
   294  			// Deployment1 confirmed Current.
   295  			EventType: event.WaitType,
   296  			WaitEvent: &testutil.ExpWaitEvent{
   297  				GroupName:  "wait-1",
   298  				Status:     event.ReconcileSuccessful,
   299  				Identifier: object.UnstructuredToObjMetadata(deployment1Obj),
   300  			},
   301  		},
   302  		{
   303  			// WaitTask finished
   304  			EventType: event.ActionGroupType,
   305  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   306  				Action:    event.WaitAction,
   307  				GroupName: "wait-1",
   308  				Type:      event.Finished,
   309  			},
   310  		},
   311  		{
   312  			// InvSetTask start
   313  			EventType: event.ActionGroupType,
   314  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   315  				Action:    event.InventoryAction,
   316  				GroupName: "inventory-set-0",
   317  				Type:      event.Started,
   318  			},
   319  		},
   320  		{
   321  			// InvSetTask finished
   322  			EventType: event.ActionGroupType,
   323  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   324  				Action:    event.InventoryAction,
   325  				GroupName: "inventory-set-0",
   326  				Type:      event.Finished,
   327  			},
   328  		},
   329  	}
   330  	Expect(testutil.EventsToExpEvents(applierEvents)).To(testutil.Equal(expEvents))
   331  
   332  	By("verify pod1 created and ready")
   333  	result := e2eutil.AssertUnstructuredExists(ctx, c, pod1Obj)
   334  	podIP, found, err := object.NestedField(result.Object, "status", "podIP")
   335  	Expect(err).NotTo(HaveOccurred())
   336  	Expect(found).To(BeTrue())
   337  	Expect(podIP).NotTo(BeEmpty()) // use podIP as proxy for readiness
   338  
   339  	By("verify deployment1 created and ready")
   340  	result = e2eutil.AssertUnstructuredExists(ctx, c, deployment1Obj)
   341  	e2eutil.AssertUnstructuredAvailable(result)
   342  
   343  	By("verify pod3 not found")
   344  	e2eutil.AssertUnstructuredDoesNotExist(ctx, c, pod3Obj)
   345  
   346  	By("verify podA not found")
   347  	e2eutil.AssertUnstructuredDoesNotExist(ctx, c, podAObj)
   348  
   349  	By("verify podB not found")
   350  	e2eutil.AssertUnstructuredDoesNotExist(ctx, c, podBObj)
   351  
   352  	By("modify deployment1 depends-on annotation to be invalid")
   353  	e2eutil.ApplyUnstructured(ctx, c, e2eutil.WithDependsOn(deployment1Obj, "invalid"))
   354  
   355  	By("destroy valid objects and skip invalid objects")
   356  	destroyer := invConfig.DestroyerFactoryFunc()
   357  	destroyerEvents := e2eutil.RunCollect(destroyer.Run(ctx, inv, apply.DestroyerOptions{
   358  		InventoryPolicy:  inventory.PolicyAdoptIfNoInventory,
   359  		ValidationPolicy: validation.SkipInvalid,
   360  	}))
   361  
   362  	expEvents = []testutil.ExpEvent{
   363  		{
   364  			// Deployment1 validation error
   365  			EventType: event.ValidationType,
   366  			ValidationEvent: &testutil.ExpValidationEvent{
   367  				Identifiers: object.ObjMetadataSet{
   368  					object.UnstructuredToObjMetadata(deployment1Obj),
   369  				},
   370  				Error: testutil.EqualError(
   371  					validation.NewError(
   372  						object.InvalidAnnotationError{
   373  							Annotation: dependson.Annotation,
   374  							Cause: fmt.Errorf("failed to parse object reference (index: 0): %w",
   375  								fmt.Errorf("expected 3 or 5 fields, found 1: %q", "invalid")),
   376  						},
   377  						object.UnstructuredToObjMetadata(deployment1Obj),
   378  					),
   379  				),
   380  			},
   381  		},
   382  		{
   383  			// InitTask
   384  			EventType: event.InitType,
   385  			InitEvent: &testutil.ExpInitEvent{},
   386  		},
   387  		{
   388  			// PruneTask start
   389  			EventType: event.ActionGroupType,
   390  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   391  				Action:    event.DeleteAction,
   392  				GroupName: "prune-0",
   393  				Type:      event.Started,
   394  			},
   395  		},
   396  		// TODO: Filter deletes so dependencies don't get deleted when the objects that used to depend on them are invalid?
   397  		{
   398  			// Delete pod1
   399  			EventType: event.DeleteType,
   400  			DeleteEvent: &testutil.ExpDeleteEvent{
   401  				GroupName:  "prune-0",
   402  				Status:     event.DeleteSuccessful,
   403  				Identifier: object.UnstructuredToObjMetadata(pod1Obj),
   404  			},
   405  		},
   406  		{
   407  			// PruneTask finished
   408  			EventType: event.ActionGroupType,
   409  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   410  				Action:    event.DeleteAction,
   411  				GroupName: "prune-0",
   412  				Type:      event.Finished,
   413  			},
   414  		},
   415  		{
   416  			// WaitTask start
   417  			EventType: event.ActionGroupType,
   418  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   419  				Action:    event.WaitAction,
   420  				GroupName: "wait-0",
   421  				Type:      event.Started,
   422  			},
   423  		},
   424  		{
   425  			// Pod1 reconcile Pending.
   426  			EventType: event.WaitType,
   427  			WaitEvent: &testutil.ExpWaitEvent{
   428  				GroupName:  "wait-0",
   429  				Status:     event.ReconcilePending,
   430  				Identifier: object.UnstructuredToObjMetadata(pod1Obj),
   431  			},
   432  		},
   433  		{
   434  			// Pod1 confirmed NotFound.
   435  			EventType: event.WaitType,
   436  			WaitEvent: &testutil.ExpWaitEvent{
   437  				GroupName:  "wait-0",
   438  				Status:     event.ReconcileSuccessful,
   439  				Identifier: object.UnstructuredToObjMetadata(pod1Obj),
   440  			},
   441  		},
   442  		{
   443  			// WaitTask finished
   444  			EventType: event.ActionGroupType,
   445  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   446  				Action:    event.WaitAction,
   447  				GroupName: "wait-0",
   448  				Type:      event.Finished,
   449  			},
   450  		},
   451  		{
   452  			// DeleteInvTask start
   453  			EventType: event.ActionGroupType,
   454  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   455  				Action:    event.InventoryAction,
   456  				GroupName: "delete-inventory-0",
   457  				Type:      event.Started,
   458  			},
   459  		},
   460  		{
   461  			// DeleteInvTask finished
   462  			EventType: event.ActionGroupType,
   463  			ActionGroupEvent: &testutil.ExpActionGroupEvent{
   464  				Action:    event.InventoryAction,
   465  				GroupName: "delete-inventory-0",
   466  				Type:      event.Finished,
   467  			},
   468  		},
   469  	}
   470  	Expect(testutil.EventsToExpEvents(destroyerEvents)).To(testutil.Equal(expEvents))
   471  
   472  	By("verify pod1 deleted")
   473  	e2eutil.AssertUnstructuredDoesNotExist(ctx, c, pod1Obj)
   474  
   475  	By("verify deployment1 not deleted")
   476  	e2eutil.AssertUnstructuredExists(ctx, c, deployment1Obj)
   477  	e2eutil.DeleteUnstructuredIfExists(ctx, c, deployment1Obj)
   478  
   479  	By("verify pod3 not found")
   480  	e2eutil.AssertUnstructuredDoesNotExist(ctx, c, pod3Obj)
   481  
   482  	By("verify podA not found")
   483  	e2eutil.AssertUnstructuredDoesNotExist(ctx, c, podAObj)
   484  
   485  	By("verify podB not found")
   486  	e2eutil.AssertUnstructuredDoesNotExist(ctx, c, podBObj)
   487  }
   488  

View as plain text