diff --git a/go.mod b/go.mod index d4b8683d6..2b7281160 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,13 @@ module github.com/codeready-toolchain/toolchain-e2e require ( - github.com/codeready-toolchain/api v0.0.0-20241119094246-f6581d52dc80 + github.com/codeready-toolchain/api v0.0.0-20241202100321-545317813297 github.com/codeready-toolchain/toolchain-common v0.0.0-20241114215157-a6a85252b2f5 github.com/davecgh/go-spew v1.1.1 github.com/fatih/color v1.15.0 github.com/ghodss/yaml v1.0.0 github.com/gofrs/uuid v3.3.0+incompatible - github.com/google/go-cmp v0.5.9 + github.com/google/go-cmp v0.6.0 github.com/gorilla/websocket v1.4.2 github.com/gosuri/uiprogress v0.0.1 github.com/gosuri/uitable v0.0.4 @@ -102,12 +102,13 @@ require ( github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect golang.org/x/crypto v0.31.0 // indirect - golang.org/x/net v0.23.0 // indirect + golang.org/x/net v0.31.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.27.0 // indirect gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/go.sum b/go.sum index 9da586d6d..266c4c322 100644 --- a/go.sum +++ b/go.sum @@ -119,8 +119,8 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/codeready-toolchain/api v0.0.0-20241119094246-f6581d52dc80 h1:OpZkP3OGAdrDHOb1TtHVnLSVuevEiQhOH//plnpVL/c= -github.com/codeready-toolchain/api v0.0.0-20241119094246-f6581d52dc80/go.mod h1:DUq1ffy9Mbersdgji48i/cm9Y+6NMwAdAQJNlfOrPRo= +github.com/codeready-toolchain/api v0.0.0-20241202100321-545317813297 h1:YsZesoSkP4sxGL7d1DtPHbqLGUe2A9srXVyW2IERKrw= +github.com/codeready-toolchain/api v0.0.0-20241202100321-545317813297/go.mod h1:DUq1ffy9Mbersdgji48i/cm9Y+6NMwAdAQJNlfOrPRo= github.com/codeready-toolchain/toolchain-common v0.0.0-20241114215157-a6a85252b2f5 h1:vW0C32c6sI9ZUGcUw3e9ftE9hqJ/bMo+TtRHp84Hung= github.com/codeready-toolchain/toolchain-common v0.0.0-20241114215157-a6a85252b2f5/go.mod h1:wx/d4HVbDPOadwpbxn28ZGClC5OmzelIK8p4wupDJVI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -301,8 +301,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v52 v52.0.0 h1:uyGWOY+jMQ8GVGSX8dkSwCzlehU3WfdxQ7GweO/JP7M= github.com/google/go-github/v52 v52.0.0/go.mod h1:WJV6VEEUPuMo5pXqqa2ZCZEdbQqua4zAk2MZTIo+m+4= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -831,8 +832,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1045,7 +1046,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= +golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/test/e2e/parallel/nstemplatetier_test.go b/test/e2e/parallel/nstemplatetier_test.go index 6f2e440ae..3319efb76 100644 --- a/test/e2e/parallel/nstemplatetier_test.go +++ b/test/e2e/parallel/nstemplatetier_test.go @@ -9,6 +9,7 @@ import ( "time" v1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "github.com/gofrs/uuid" @@ -20,6 +21,7 @@ import ( . "github.com/codeready-toolchain/toolchain-e2e/testsupport/space" "github.com/codeready-toolchain/toolchain-e2e/testsupport/tiers" "github.com/codeready-toolchain/toolchain-e2e/testsupport/wait" + apiwait "k8s.io/apimachinery/pkg/util/wait" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -140,7 +142,6 @@ func TestUpdateNSTemplateTier(t *testing.T) { verifyResourceUpdatesForUserSignups(t, hostAwait, memberAwait, cheesecakeUsers, cheesecakeTier) verifyResourceUpdatesForUserSignups(t, hostAwait, memberAwait, cookieUsers, cookieTier) verifyResourceUpdatesForSpaces(t, hostAwait, memberAwait, spaces, chocolateTier) - } func TestResetDeactivatingStateWhenPromotingUser(t *testing.T) { @@ -366,6 +367,89 @@ func TestFeatureToggles(t *testing.T) { }) } +func TestTierTemplateRevision(t *testing.T) { + t.Parallel() + + // given + awaitilities := WaitForDeployments(t) + hostAwait := awaitilities.Host() + // we create new NSTemplateTiers (derived from `base`) + baseTier, err := hostAwait.WaitForNSTemplateTier(t, "base1ns") + require.NoError(t, err) + // for the tiertemplaterevisions to be created the tiertemplates need to have template objects populated + // we add the RawExtension objects to the TemplateObjects field + crq := unstructured.Unstructured{Object: map[string]interface{}{ + "kind": "ClusterResourceQuota", + "metadata": map[string]interface{}{ + "name": "for-{{.SPACE_NAME}}-deployments", + }, + "spec": map[string]interface{}{ + "quota": map[string]interface{}{ + "hard": map[string]string{ + "count/deploymentconfigs.apps": "{{.DEPLOYMENT_QUOTA}}", + "count/deployments.apps": "{{.DEPLOYMENT_QUOTA}}", + "count/pods": "600", + }, + }, + "selector": map[string]interface{}{ + "annotations": map[string]string{}, + "labels": map[string]interface{}{ + "matchLabels": map[string]string{ + "toolchain.dev.openshift.com/space": "{{.SPACE_NAME}}", + }, + }, + }, + }, + }} + rawTemplateObjects := []runtime.RawExtension{{Object: &crq}} + updateTierTemplateObjects := func(template *toolchainv1alpha1.TierTemplate) error { + template.Spec.TemplateObjects = rawTemplateObjects + return nil + } + // for simplicity, we add the CRQ to all types of templates (both cluster scope and namespace scoped), + // even if the CRQ is cluster scoped. + // WARNING: thus THIS NSTemplateTier should NOT be sued to provision a user!!! + tiers.CreateCustomNSTemplateTier(t, hostAwait, "ttr", baseTier, + tiers.WithNamespaceResources(t, baseTier, updateTierTemplateObjects), + tiers.WithClusterResources(t, baseTier, updateTierTemplateObjects), + tiers.WithSpaceRoles(t, baseTier, updateTierTemplateObjects), + tiers.WithParameter("DEPLOYMENT_QUOTA", "60")) + // when + // we verify the counters in the status.history for 'tierUsingTierTemplateRevisions' tier + // and verify that TierTemplateRevision CRs were created, since all the tiertemplates now have templateObjects field populated + customTier, err := hostAwait.WaitForNSTemplateTierAndCheckTemplates(t, "ttr", + wait.HasStatusTierTemplateRevisions(tiers.GetTemplateRefs(t, hostAwait, "ttr").Flatten())) + require.NoError(t, err) + + // then + // check the expected total number of ttr matches + err = apiwait.PollUntilContextTimeout(context.TODO(), hostAwait.RetryInterval, hostAwait.Timeout, true, func(ctx context.Context) (done bool, err error) { + objs := &toolchainv1alpha1.TierTemplateRevisionList{} + if err := hostAwait.Client.List(ctx, objs, client.InNamespace(hostAwait.Namespace)); err != nil { + return false, err + } + // we IDEALLY expect one TTR per each tiertemplate to be created (clusterresource, namespace and spacerole), thus a total of 3 TTRs ideally. + // But since the creation of a TTR could be very quick and could trigger another reconcile of the NSTemplateTier before the status is actually updated with the reference, + // this might generate some copies of the TTRs. This is not a problem in production since the cleanup mechanism of TTRs will remove the extra ones but could cause some flakiness with the test, + // thus we assert the number of TTRs doesn't exceed the double of the expected number. + assert.LessOrEqual(t, len(objs.Items), len(tiers.GetTemplateRefs(t, hostAwait, "ttr").Flatten())*2) + // we check that the TTR content has the parameters replaced with values from the NSTemplateTier + for _, obj := range objs.Items { + // the object should have all the variables still there since this one will be replaced when provisioning the Space + assert.Contains(t, string(obj.Spec.TemplateObjects[0].Raw), ".SPACE_NAME") + assert.Contains(t, string(obj.Spec.TemplateObjects[0].Raw), ".DEPLOYMENT_QUOTA") + // the parameter is copied from the NSTemplateTier + assert.NotNil(t, obj.Spec.Parameters) + assert.NotNil(t, customTier.Spec.Parameters) + // we only expect the static parameter DEPLOYMENT_QUOTA to be copied from the tier to the TTR. + // the SPACE_NAME is not a parameter, but a dynamic variable which will be evaluated when provisioning a namespace for the user. + assert.ElementsMatch(t, customTier.Spec.Parameters, obj.Spec.Parameters) + } + return true, nil + }) + require.NoError(t, err) +} + func withClusterRoleBindings(t *testing.T, otherTier *toolchainv1alpha1.NSTemplateTier, feature string) tiers.CustomNSTemplateTierModifier { clusterRB := getCRBforFeature(t, feature) // This is the ClusterRoleBinding for the desired feature noiseCRB := getCRBforFeature(t, unknownFeature) // This is a noise CRB for unknown/disabled feature. To be used to check that this CRB is never created. diff --git a/testsupport/cleanup/clean.go b/testsupport/cleanup/clean.go index 967f1df09..ec754ca28 100644 --- a/testsupport/cleanup/clean.go +++ b/testsupport/cleanup/clean.go @@ -106,6 +106,7 @@ func (c *cleanTask) cleanObject() { objToClean, ok := c.objToClean.DeepCopyObject().(client.Object) require.True(c.t, ok) userSignup, isUserSignup := c.objToClean.(*toolchainv1alpha1.UserSignup) + nsTemplateTier, isNsTemplateTier := c.objToClean.(*toolchainv1alpha1.NSTemplateTier) kind := objToClean.GetObjectKind().GroupVersionKind().Kind if kind == "" { kind = reflect.TypeOf(c.objToClean).Elem().Name() @@ -126,10 +127,13 @@ func (c *cleanTask) cleanObject() { } } } + // if the object was NSTemplateTier, then let's check that the TierTemplateRevisions were deleted as well + _, err := c.verifyTierTemplateRevisionsDeleted(isNsTemplateTier, nsTemplateTier, true) + require.NoError(c.t, err) // wait until deletion is done c.t.Logf("waiting until %s: %s is completely deleted", kind, objToClean.GetName()) - err := wait.PollUntilContextTimeout(context.TODO(), defaultRetryInterval, defaultTimeout, true, func(ctx context.Context) (done bool, err error) { + err = wait.PollUntilContextTimeout(context.TODO(), defaultRetryInterval, defaultTimeout, true, func(ctx context.Context) (done bool, err error) { if err := c.client.Get(context.TODO(), test.NamespacedName(objToClean.GetNamespace(), objToClean.GetName()), objToClean); err != nil { if errors.IsNotFound(err) { // if the object was UserSignup, then let's check that the MUR is deleted as well @@ -140,6 +144,10 @@ func (c *cleanTask) cleanObject() { if spaceDeleted, err := c.verifySpaceDeleted(isUserSignup, userSignup, false); !spaceDeleted || err != nil { return false, err } + // if the object was NSTemplateTier, then let's check that the TTRs were deleted as well + if ttrsDeleted, err := c.verifyTierTemplateRevisionsDeleted(isNsTemplateTier, nsTemplateTier, false); !ttrsDeleted || err != nil { + return false, err + } return true, nil } c.t.Logf("problem with getting the related %s '%s': %s", kind, objToClean.GetName(), err) @@ -240,3 +248,27 @@ func (c *cleanTask) verifySpaceDeleted(isUserSignup bool, userSignup *toolchainv } return true, nil } + +func (c *cleanTask) verifyTierTemplateRevisionsDeleted(isNsTemplateTier bool, nsTemplateTier *toolchainv1alpha1.NSTemplateTier, delete bool) (bool, error) { + if !isNsTemplateTier { + return true, nil + } + ttrs := &toolchainv1alpha1.TierTemplateRevisionList{} + if err := c.client.List(context.TODO(), ttrs, + client.InNamespace(nsTemplateTier.GetNamespace()), + client.MatchingLabels{toolchainv1alpha1.TierLabelKey: nsTemplateTier.GetName()}); err != nil { + c.t.Logf("problem with getting the ttrs for tier %s: %s", nsTemplateTier.GetName(), err) + return false, err + } + if len(ttrs.Items) == 0 { + c.t.Logf("the NSTemplateTier %s doesn't have TTRs", nsTemplateTier.GetName()) + return true, nil + } + if delete { + return false, c.client.DeleteAllOf(context.TODO(), &toolchainv1alpha1.TierTemplateRevision{}, + client.InNamespace(nsTemplateTier.GetNamespace()), + client.MatchingLabels{toolchainv1alpha1.TierLabelKey: nsTemplateTier.GetName()}) + } + // ttrs are still there + return false, nil +} diff --git a/testsupport/tiers/tier_setup.go b/testsupport/tiers/tier_setup.go index 68a7c35b3..a1fe4a34a 100644 --- a/testsupport/tiers/tier_setup.go +++ b/testsupport/tiers/tier_setup.go @@ -47,13 +47,13 @@ func WithClusterResources(t *testing.T, otherTier *toolchainv1alpha1.NSTemplateT } } -func WithNamespaceResources(t *testing.T, otherTier *toolchainv1alpha1.NSTemplateTier) CustomNSTemplateTierModifier { +func WithNamespaceResources(t *testing.T, otherTier *toolchainv1alpha1.NSTemplateTier, modifiers ...TierTemplateModifier) CustomNSTemplateTierModifier { return func(hostAwait *wait.HostAwaitility, tier *CustomNSTemplateTier) error { tier.NamespaceResourcesTier = otherTier // configure the "wrapped" NSTemplateTier tier.Spec.Namespaces = make([]toolchainv1alpha1.NSTemplateTierNamespace, len(otherTier.Spec.Namespaces)) for i, def := range otherTier.Spec.Namespaces { - tmplRef, err := duplicateTierTemplate(t, hostAwait, otherTier.Namespace, tier.Name, def.TemplateRef) + tmplRef, err := duplicateTierTemplate(t, hostAwait, otherTier.Namespace, tier.Name, def.TemplateRef, modifiers...) if err != nil { return err } @@ -63,13 +63,13 @@ func WithNamespaceResources(t *testing.T, otherTier *toolchainv1alpha1.NSTemplat } } -func WithSpaceRoles(t *testing.T, otherTier *toolchainv1alpha1.NSTemplateTier) CustomNSTemplateTierModifier { +func WithSpaceRoles(t *testing.T, otherTier *toolchainv1alpha1.NSTemplateTier, modifiers ...TierTemplateModifier) CustomNSTemplateTierModifier { return func(hostAwait *wait.HostAwaitility, tier *CustomNSTemplateTier) error { tier.SpaceRolesTier = otherTier // configure the "wrapped" NSTemplateTier tier.Spec.SpaceRoles = make(map[string]toolchainv1alpha1.NSTemplateTierSpaceRole, len(otherTier.Spec.SpaceRoles)) for name, def := range otherTier.Spec.SpaceRoles { - tmplRef, err := duplicateTierTemplate(t, hostAwait, otherTier.Namespace, tier.Name, def.TemplateRef) + tmplRef, err := duplicateTierTemplate(t, hostAwait, otherTier.Namespace, tier.Name, def.TemplateRef, modifiers...) if err != nil { return err } @@ -81,6 +81,21 @@ func WithSpaceRoles(t *testing.T, otherTier *toolchainv1alpha1.NSTemplateTier) C } } +func WithParameter(name, value string) CustomNSTemplateTierModifier { + return func(hostAwait *wait.HostAwaitility, tier *CustomNSTemplateTier) error { + if tier.Spec.Parameters == nil { + tier.Spec.Parameters = []toolchainv1alpha1.Parameter{} + } + tier.Spec.Parameters = append(tier.Spec.Parameters, + toolchainv1alpha1.Parameter{ + Name: name, + Value: value, + }, + ) + return nil + } +} + // CreateCustomNSTemplateTier creates a custom tier. // If no modifiers provided then the new tier will use copies of the baseTier cluster, namespace and space roles templates // without any modifications. diff --git a/testsupport/wait/host.go b/testsupport/wait/host.go index 4cad9684e..83f125fef 100644 --- a/testsupport/wait/host.go +++ b/testsupport/wait/host.go @@ -919,7 +919,11 @@ func (a *HostAwaitility) WaitForNSTemplateTierAndCheckTemplates(t *testing.T, na if ns.TemplateRef == "" { return nil, fmt.Errorf("missing 'templateRef' in namespace #%d in NSTemplateTier '%s'", i, tier.Name) } - if _, err := a.WaitForTierTemplate(t, ns.TemplateRef); err != nil { + tierTemplateNamespaces, err := a.WaitForTierTemplate(t, ns.TemplateRef) + if err != nil { + return nil, err + } + if err := a.checkTTR(t, tier, tierTemplateNamespaces); err != nil { return nil, err } } @@ -927,13 +931,46 @@ func (a *HostAwaitility) WaitForNSTemplateTierAndCheckTemplates(t *testing.T, na if tier.Spec.ClusterResources.TemplateRef == "" { return nil, fmt.Errorf("missing 'templateRef' for the cluster resources in NSTemplateTier '%s'", tier.Name) } - if _, err := a.WaitForTierTemplate(t, tier.Spec.ClusterResources.TemplateRef); err != nil { + tierTemplateClusterResources, err := a.WaitForTierTemplate(t, tier.Spec.ClusterResources.TemplateRef) + if err != nil { + return nil, err + } + if err := a.checkTTR(t, tier, tierTemplateClusterResources); err != nil { + return nil, err + } + } + + for _, r := range tier.Spec.SpaceRoles { + if r.TemplateRef == "" { + return nil, fmt.Errorf("missing 'templateRef' in spaceRole %s in NSTemplateTier '%s'", r.TemplateRef, tier.Name) + } + tierTemplateSpaceRoles, err := a.WaitForTierTemplate(t, r.TemplateRef) + if err != nil { + return nil, err + } + if err := a.checkTTR(t, tier, tierTemplateSpaceRoles); err != nil { return nil, err } } + return tier, err } +func (a *HostAwaitility) checkTTR(t *testing.T, tier *toolchainv1alpha1.NSTemplateTier, tierTemplate *toolchainv1alpha1.TierTemplate) error { + // if the tier template supports Tier Template Revisions then let's check those + if tierTemplate.Spec.TemplateObjects != nil { + // TODO improve this since now it requires that WaitForNSTemplateTierAndCheckTemplates should be called with HasStatusTierTemplateRevisions, + ttrName, found := tier.Status.Revisions[tierTemplate.GetName()] + if !found { + return fmt.Errorf("missing revision for TierTemplate %s in NSTemplateTier '%s'", tierTemplate.GetName(), tier.Name) + } + if _, err := For(t, a.Awaitility, &toolchainv1alpha1.TierTemplateRevision{}).WithNameThat(ttrName); err != nil { + return err + } + } + return nil +} + // WaitForTierTemplate waits until a TierTemplate with the given name exists // Returns an error if the resource did not exist (or something wrong happened) func (a *HostAwaitility) WaitForTierTemplate(t *testing.T, name string) (*toolchainv1alpha1.TierTemplate, error) { // nolint:unparam @@ -1015,6 +1052,26 @@ func UntilNSTemplateTierSpec(matcher NSTemplateTierSpecMatcher) NSTemplateTierWa } } +// HasStatusTierTemplateRevisions verifies revisions for the given TierTemplates are set in the `NSTemplateTier.Status.Revisions` +func HasStatusTierTemplateRevisions(revisions []string) NSTemplateTierWaitCriterion { + return NSTemplateTierWaitCriterion{ + Match: func(actual *toolchainv1alpha1.NSTemplateTier) bool { + if len(actual.Status.Revisions) != len(revisions) { + return false + } + for _, tierTemplateRef := range revisions { + if _, found := actual.Status.Revisions[tierTemplateRef]; !found { + return false + } + } + return true + }, + Diff: func(actual *toolchainv1alpha1.NSTemplateTier) string { + return fmt.Sprintf("expected revision keys %v not found in: %v", revisions, actual.Status.Revisions) + }, + } +} + // HasNoTemplateRefWithSuffix checks that ALL namespaces' `TemplateRef` doesn't have the suffix func HasNoTemplateRefWithSuffix(suffix string) NSTemplateTierSpecMatcher { return NSTemplateTierSpecMatcher{