...

Source file src/edge-infra.dev/pkg/f8n/warehouse/k8s/controllers/lumperctl/integration/shipment_controller_test.go

Documentation: edge-infra.dev/pkg/f8n/warehouse/k8s/controllers/lumperctl/integration

     1  package integration
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/davecgh/go-spew/spew"
     8  	"gotest.tools/v3/assert"
     9  	"gotest.tools/v3/assert/cmp"
    10  	"gotest.tools/v3/poll"
    11  	"k8s.io/apimachinery/pkg/labels"
    12  	"k8s.io/apimachinery/pkg/selection"
    13  	"k8s.io/apimachinery/pkg/types"
    14  	"sigs.k8s.io/controller-runtime/pkg/client"
    15  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    16  
    17  	"edge-infra.dev/pkg/f8n/warehouse/capability"
    18  	"edge-infra.dev/pkg/f8n/warehouse/cluster"
    19  	whv1 "edge-infra.dev/pkg/f8n/warehouse/k8s/apis/v1alpha2"
    20  	"edge-infra.dev/pkg/f8n/warehouse/oci/layer"
    21  	"edge-infra.dev/pkg/f8n/warehouse/pallet"
    22  	"edge-infra.dev/pkg/k8s/object"
    23  	"edge-infra.dev/pkg/k8s/runtime/conditions"
    24  	"edge-infra.dev/test/f2"
    25  	"edge-infra.dev/test/f2/x/ktest"
    26  	"edge-infra.dev/test/f2/x/warehouse"
    27  )
    28  
    29  func TestShipmentController_Finalizers(t *testing.T) {
    30  	var (
    31  		id       string
    32  		p1       testPallet
    33  		p2       testPallet
    34  		s        = &whv1.Shipment{}
    35  		children []client.Object
    36  	)
    37  
    38  	fin := f2.NewFeature("Finalizers").
    39  		Setup("Create and push test packages", func(ctx f2.Context, t *testing.T) f2.Context {
    40  			k := ktest.FromContextT(ctx, t)
    41  
    42  			id = fmt.Sprintf("sfin-%s", ctx.RunID)
    43  			p1 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1",
    44  				createLayer(t, layer.Runtime, minimalv1))
    45  			p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v1",
    46  				createLayer(t, layer.Runtime, minimalv2))
    47  			s = shipment(ctx, t, id, []whv1.BaseArtifact{
    48  				{Digest: p1.digest, Name: p1.name},
    49  				{Digest: p2.digest, Name: p2.name},
    50  			})
    51  
    52  			assert.NilError(t, k.Client.Create(ctx, s))
    53  			k.WaitOn(t, k.Check(s, shipmentReady))
    54  
    55  			return ctx
    56  		}).
    57  		Test("Finalizer is added", func(ctx f2.Context, t *testing.T) f2.Context {
    58  			if !controllerutil.ContainsFinalizer(s, whv1.WarehouseFinalizer) {
    59  				t.Error("finalizer not added to unpackedpallet", spew.Sprintln(s))
    60  			}
    61  
    62  			return ctx
    63  		}).
    64  		Test("Removes finalizer on delete", func(ctx f2.Context, t *testing.T) f2.Context {
    65  			k := ktest.FromContextT(ctx, t)
    66  
    67  			children = make([]client.Object, 2)
    68  			for i, v := range []testPallet{p1, p2} {
    69  				u := &whv1.UnpackedPallet{}
    70  				assert.NilError(t, k.Client.Get(
    71  					ctx, types.NamespacedName{Name: s.ChildResourceName(v.name)}, u,
    72  				), "%s never created by shipment", s.ChildResourceName(v.name))
    73  				children[i] = u
    74  			}
    75  
    76  			assert.NilError(t, k.Client.Delete(ctx, s))
    77  			// Object being deleted means that finalizer was successfully removed.
    78  			k.WaitOn(t, k.ObjDeleted(s))
    79  
    80  			return ctx
    81  		}).
    82  		Test("Respects spec.prune", func(ctx f2.Context, t *testing.T) f2.Context {
    83  			k := ktest.FromContextT(ctx, t)
    84  
    85  			// Make sure objects weren't pruned previously
    86  			k.WaitOn(t, k.ObjsExist(children))
    87  
    88  			s = shipment(ctx, t, fmt.Sprintf("%s-2", id), []whv1.BaseArtifact{
    89  				{Digest: p1.digest, Name: p1.name},
    90  				{Digest: p2.digest, Name: p2.name},
    91  			}, whv1.WithPrune())
    92  			assert.NilError(t, k.Client.Create(ctx, s))
    93  			k.WaitOn(t, k.Check(s, shipmentReady))
    94  
    95  			// Re-evaluate children with new name
    96  			for i, v := range []testPallet{p1, p2} {
    97  				u := &whv1.UnpackedPallet{}
    98  				assert.NilError(t, k.Client.Get(
    99  					ctx, types.NamespacedName{Name: s.ChildResourceName(v.name)}, u,
   100  				), "%s never created by shipment", s.ChildResourceName(v.name))
   101  				children[i] = u
   102  			}
   103  
   104  			assert.NilError(t, k.Client.Delete(ctx, s))
   105  			k.WaitOn(t, k.ObjsDeleted(children), poll.WithTimeout(k.Timeout*2))
   106  
   107  			return ctx
   108  		}).Feature()
   109  
   110  	f.Test(t, fin)
   111  }
   112  
   113  func TestShipmentController_Capability(t *testing.T) {
   114  	var (
   115  		p1 testPallet
   116  		p2 testPallet
   117  	)
   118  
   119  	capability := f2.NewFeature("Runtime Capabilities").
   120  		Test("Capability not present in spec.pallets or spec.pallets's children", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl
   121  			k := ktest.FromContextT(ctx, t)
   122  
   123  			id := fmt.Sprintf("not-present-in-spec-pallets-%s", ctx.RunID)
   124  			p1 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1",
   125  				createLayer(t, layer.Runtime, minimalv1))
   126  			p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2",
   127  				createLayer(t, layer.Runtime, minimalv2))
   128  			s := shipment(ctx, t, id, []whv1.BaseArtifact{
   129  				{Digest: p1.digest, Name: p1.name},
   130  				{Digest: p2.digest, Name: p2.name},
   131  			}, whv1.WithCapabilities("linkerd"))
   132  
   133  			assert.NilError(t, k.Client.Create(ctx, s))
   134  			k.WaitOn(t, k.Check(s, shipmentStalled))
   135  
   136  			return ctx
   137  		}).Test("Capability present in spec.pallets", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl
   138  		k := ktest.FromContextT(ctx, t)
   139  
   140  		id := fmt.Sprintf("present-in-spec-pallets-%s", ctx.RunID)
   141  		l5did := fmt.Sprintf("%s-linkerd", id)
   142  		l5d := createAndPushPallet(ctx, t, l5did, "latest",
   143  			createLayer(t, layer.Runtime, linkerdCapablityProvider))
   144  		p1 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1",
   145  			createLayer(t, layer.Runtime, minimalv1))
   146  		p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v1",
   147  			createLayer(t, layer.Runtime, meshConfig, layer.ForCapability(capability.Capability(l5did))))
   148  		s := shipment(ctx, t, id, []whv1.BaseArtifact{
   149  			{Digest: p1.digest, Name: p1.name},
   150  			{Digest: p2.digest, Name: p2.name},
   151  			{Digest: l5d.digest, Name: l5d.name},
   152  		}, whv1.WithCapabilities(l5did))
   153  
   154  		assert.NilError(t, k.Client.Create(ctx, s))
   155  		k.WaitOn(t, k.Check(s, shipmentReady))
   156  
   157  		return ctx
   158  	}).Test("Capability present in spec.pallets's children", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl
   159  		k := ktest.FromContextT(ctx, t)
   160  		reg := warehouse.FromContextT(ctx, t)
   161  
   162  		id := fmt.Sprintf("present-in-spec-pallets-children-%s", ctx.RunID)
   163  		l5did := fmt.Sprintf("%s-linkerd", id)
   164  		l5d := createAndPushPallet(ctx, t, l5did, "latest",
   165  			createLayer(t, layer.Runtime, linkerdCapablityProvider))
   166  
   167  		p1img := createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1",
   168  			createLayer(t, layer.Runtime, minimalv1))
   169  		p1, err := pallet.ImageIndex(pallet.Options{
   170  			Metadata:         p1img.pallet.Metadata(),
   171  			Capabilities:     p1img.pallet.Capabilities(),
   172  			ClusterProviders: cluster.Providers{cluster.Generic, cluster.GKE},
   173  		}, p1img.pallet, l5d.pallet)
   174  		assert.NilError(t, err)
   175  		assert.NilError(t, reg.Push(p1, p1.Name(), "v1"))
   176  
   177  		p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v1",
   178  			createLayer(t, layer.Runtime, minimalv2))
   179  
   180  		p1digest, err := p1.Digest()
   181  		assert.NilError(t, err)
   182  
   183  		s := shipment(ctx, t, id, []whv1.BaseArtifact{
   184  			{Digest: p1digest.String(), Name: p1.Name()},
   185  			{Digest: p2.digest, Name: p2.name},
   186  		}, whv1.WithCapabilities(l5did))
   187  
   188  		assert.NilError(t, k.Client.Create(ctx, s))
   189  		k.WaitOn(t, k.Check(s, shipmentReady))
   190  
   191  		return ctx
   192  	}).Feature()
   193  
   194  	f.Test(t, capability)
   195  }
   196  
   197  func TestShipmentController_PalletRepos(t *testing.T) {
   198  	feat := f2.NewFeature("Pallet Repositories").
   199  		Test("Pallets are fetched from a single repository", func(ctx f2.Context, t *testing.T) f2.Context {
   200  			k := ktest.FromContextT(ctx, t)
   201  
   202  			// create somes pallets and include them as deps
   203  			id := fmt.Sprintf("unpackedpallet-repo-%s", ctx.RunID)
   204  			p10 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-10", id), "v1",
   205  				createLayer(t, layer.Runtime, minimalv1))
   206  			p11 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-11", id), "v1",
   207  				createLayer(t, layer.Runtime, minimalv1))
   208  			p1 := createAndPushPalletWithDeps(ctx, t, fmt.Sprintf("%s-1", id), "", p10.pallet, p11.pallet)
   209  			p2 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2",
   210  				createLayer(t, layer.Runtime, minimalv2))
   211  			p0 := createAndPushPalletWithDeps(ctx, t, id, "", p1.pallet, p2.pallet)
   212  
   213  			// shipment with single pallet
   214  			s := shipment(ctx, t, id, []whv1.BaseArtifact{{Digest: p0.digest, Name: p0.name}})
   215  			s.Labels = map[string]string{"test-id": id}
   216  			assert.NilError(t, k.Client.Create(ctx, s))
   217  			k.WaitOn(t, k.Check(s, shipmentReady))
   218  
   219  			req, err := labels.NewRequirement("test-id", selection.Equals, []string{id})
   220  			assert.NilError(t, err)
   221  
   222  			// TODO(aw185176): Should be generic helper
   223  			plts := &whv1.UnpackedPalletList{}
   224  			assert.NilError(t, k.Client.List(ctx, plts, &client.ListOptions{
   225  				LabelSelector: labels.NewSelector().Add(*req),
   226  			}))
   227  			assert.Assert(t, len(plts.Items) == 3, "expected 3 unpackedpallets, got %d", len(plts.Items))
   228  			sameRepo := plts.Items[0].Spec.Repository
   229  			for _, p := range plts.Items {
   230  				assert.Equal(t, sameRepo, p.Spec.Repository,
   231  					"expected Pallet's dependencies to be pulled from same repository",
   232  				)
   233  			}
   234  
   235  			return ctx
   236  		}).Feature()
   237  
   238  	f.Test(t, feat)
   239  }
   240  
   241  // nolint:dupl
   242  func TestShipmentController_Status(t *testing.T) {
   243  	fetchCondFailed := func(o client.Object) cmp.Result {
   244  		s := o.(*whv1.Shipment)
   245  		if conditions.HasReason(s, whv1.FetchedArtifactCondition, whv1.FetchFailedReason) {
   246  			return cmp.ResultSuccess
   247  		}
   248  		return cmp.ResultFailure(fmt.Sprintf("%s not stalled", object.FmtObject(s)))
   249  	}
   250  
   251  	hasResolvedArtifacts := func(pins []whv1.ResolvedArtifact) func(client.Object) cmp.Result {
   252  		return func(o client.Object) cmp.Result {
   253  			s := o.(*whv1.Shipment)
   254  			if !cmp.DeepEqual(s.Status.LastApplied, pins)().Success() {
   255  				return cmp.ResultFailure(fmt.Sprintf("lastApplied does not match. actual: %+v, expected: %+v", s.Status.LastApplied, pins))
   256  			}
   257  			if !cmp.DeepEqual(s.Status.LastAttempted, pins)().Success() {
   258  				return cmp.ResultFailure(fmt.Sprintf("lastAttempted does not match. actual: %+v, expected: %+v", s.Status.LastAttempted, pins))
   259  			}
   260  
   261  			return cmp.ResultSuccess
   262  		}
   263  	}
   264  
   265  	feat := f2.NewFeature("Shipment Status").
   266  		Test("FetchedArtifactCondition is set correctly for missing artifacts", func(ctx f2.Context, t *testing.T) f2.Context {
   267  			k := ktest.FromContextT(ctx, t)
   268  
   269  			// Shipment controller should report correct status condition for failed fetch
   270  			id := fmt.Sprintf("testshipmentcontroller-status-%s", ctx.RunID)
   271  			s := shipment(ctx, t, id, []whv1.BaseArtifact{{Tag: "latest", Name: "does-not-exist"}})
   272  			s.Labels = map[string]string{"test-id": id}
   273  			assert.NilError(t, k.Client.Create(ctx, s))
   274  			k.WaitOn(t, k.Check(s, fetchCondFailed))
   275  
   276  			return ctx
   277  		}).
   278  		Test("Status contains information about composite pallet artifacts", func(ctx f2.Context, t *testing.T) f2.Context {
   279  			k := ktest.FromContextT(ctx, t)
   280  
   281  			// create somes pallets and include them as deps
   282  			id := fmt.Sprintf("pinned-composite-status-%s", ctx.RunID)
   283  			imgDep0 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-10", id), "v1",
   284  				createLayer(t, layer.Runtime, minimalv1))
   285  			imgDep1 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-11", id), "v1",
   286  				createLayer(t, layer.Runtime, minimalv1))
   287  			idxDep := createAndPushPalletWithDeps(ctx, t, fmt.Sprintf("%s-1", id), "", imgDep0.pallet, imgDep1.pallet)
   288  			imgDep := createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2",
   289  				createLayer(t, layer.Runtime, minimalv2))
   290  			compIdx := createAndPushPalletWithDeps(ctx, t, id, "0.1.2", idxDep.pallet, imgDep.pallet)
   291  
   292  			artifacts := []whv1.ResolvedArtifact{
   293  				{Name: imgDep0.name, Digest: imgDep0.digest, ResolvedDigest: imgDep0.digest, Version: fixtureSemVer},
   294  				{Name: imgDep1.name, Digest: imgDep1.digest, ResolvedDigest: imgDep1.digest, Version: fixtureSemVer},
   295  				{Name: idxDep.name, Digest: idxDep.digest, ResolvedDigest: idxDep.digest, Version: fixtureSemVer},
   296  				{Name: imgDep.name, Digest: imgDep.digest, ResolvedDigest: imgDep.digest, Version: fixtureSemVer},
   297  				{Name: compIdx.name, Digest: compIdx.digest, ResolvedDigest: compIdx.digest, Version: fixtureSemVer},
   298  			}
   299  
   300  			// Shipment status should show digests for composite pallets
   301  			s := shipment(ctx, t, id, []whv1.BaseArtifact{{Digest: compIdx.digest, Name: compIdx.name}})
   302  			s.Labels = map[string]string{"test-id": id}
   303  			assert.NilError(t, k.Client.Create(ctx, s))
   304  			k.WaitOn(t, k.Check(s, shipmentReady))
   305  			k.WaitOn(t, k.Check(s, hasResolvedArtifacts(artifacts)))
   306  
   307  			return ctx
   308  		}).
   309  		Test("Status contains version metadata about included pallets", func(ctx f2.Context, t *testing.T) f2.Context {
   310  			k := ktest.FromContextT(ctx, t)
   311  
   312  			// create somes pallets and include them as deps
   313  			id := fmt.Sprintf("version-meta-status-%s", ctx.RunID)
   314  			imgDep0 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-10", id), "v1",
   315  				createLayer(t, layer.Runtime, minimalv1))
   316  			imgDep1 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-11", id), "v1",
   317  				createLayer(t, layer.Runtime, minimalv1))
   318  			idxDep := createAndPushPalletWithDeps(ctx, t, fmt.Sprintf("%s-1", id), "", imgDep0.pallet, imgDep1.pallet)
   319  			imgDep := createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2",
   320  				createLayer(t, layer.Runtime, minimalv2))
   321  			compIdx := createAndPushPalletWithDeps(ctx, t, id, "0.1.2", idxDep.pallet, imgDep.pallet)
   322  
   323  			artifacts := []whv1.ResolvedArtifact{
   324  				{Name: imgDep0.name, Digest: imgDep0.digest, ResolvedDigest: imgDep0.digest, Version: fixtureSemVer},
   325  				{Name: imgDep1.name, Digest: imgDep1.digest, ResolvedDigest: imgDep1.digest, Version: fixtureSemVer},
   326  				{Name: idxDep.name, Digest: idxDep.digest, ResolvedDigest: idxDep.digest, Version: fixtureSemVer},
   327  				{Name: imgDep.name, Digest: imgDep.digest, ResolvedDigest: imgDep.digest, Version: fixtureSemVer},
   328  				{Name: compIdx.name, Digest: compIdx.digest, ResolvedDigest: compIdx.digest, Version: fixtureSemVer},
   329  			}
   330  
   331  			// Shipment status should show digests for composite pallets
   332  			s := shipment(ctx, t, id, []whv1.BaseArtifact{{Digest: compIdx.digest, Name: compIdx.name}})
   333  			s.Labels = map[string]string{"test-id": id}
   334  			assert.NilError(t, k.Client.Create(ctx, s))
   335  			k.WaitOn(t, k.Check(s, shipmentReady))
   336  			k.WaitOn(t, k.Check(s, hasResolvedArtifacts(artifacts)))
   337  
   338  			return ctx
   339  		}).Feature()
   340  
   341  	f.Test(t, feat)
   342  }
   343  

View as plain text