From b005cd7d9fb0c98d4209d5003f0e6357263b922f Mon Sep 17 00:00:00 2001 From: Matous Jobanek Date: Wed, 3 Apr 2024 11:16:34 +0200 Subject: [PATCH 1/5] make NSTemplateTier generator generic & move to common --- .../usersignup/usersignup_controller.go | 1 + go.mod | 3 +- go.sum | 4 +- make/go.mk | 4 - .../nstemplatetier_generator.go | 472 +---------- .../nstemplatetier_generator_test.go | 459 ++++------- .../nstemplatetier_generator_whitebox_test.go | 752 ------------------ test/nstemplatetier/nstemplatetier.go | 6 +- .../advanced/based_on_tier.yaml | 5 - .../nstemplatetiers/appstudio/cluster.yaml | 35 - .../nstemplatetiers/appstudio/ns_tenant.yaml | 22 - .../appstudio/spacerole_admin.yaml | 159 ---- .../appstudio/spacerole_contributor.yaml | 171 ---- .../appstudio/spacerole_maintainer.yaml | 177 ----- .../nstemplatetiers/appstudio/tier.yaml | 29 - .../nstemplatetiers/base/cluster.yaml | 28 - .../nstemplatetiers/base/ns_dev.yaml | 21 - .../nstemplatetiers/base/ns_stage.yaml | 21 - .../nstemplatetiers/base/spacerole_admin.yaml | 25 - test/templates/nstemplatetiers/base/tier.yaml | 25 - test/templates/nstemplatetiers/doc.go | 4 - test/templates/nstemplatetiers/metadata.yaml | 16 - .../nstemplatetiers/nocluster/ns_dev.yaml | 21 - .../nstemplatetiers/nocluster/ns_stage.yaml | 21 - .../nocluster/spacerole_admin.yaml | 37 - .../nstemplatetiers/nocluster/tier.yaml | 22 - 26 files changed, 175 insertions(+), 2365 deletions(-) delete mode 100644 pkg/templates/nstemplatetiers/nstemplatetier_generator_whitebox_test.go delete mode 100644 test/templates/nstemplatetiers/advanced/based_on_tier.yaml delete mode 100644 test/templates/nstemplatetiers/appstudio/cluster.yaml delete mode 100644 test/templates/nstemplatetiers/appstudio/ns_tenant.yaml delete mode 100644 test/templates/nstemplatetiers/appstudio/spacerole_admin.yaml delete mode 100644 test/templates/nstemplatetiers/appstudio/spacerole_contributor.yaml delete mode 100644 test/templates/nstemplatetiers/appstudio/spacerole_maintainer.yaml delete mode 100644 test/templates/nstemplatetiers/appstudio/tier.yaml delete mode 100644 test/templates/nstemplatetiers/base/cluster.yaml delete mode 100644 test/templates/nstemplatetiers/base/ns_dev.yaml delete mode 100644 test/templates/nstemplatetiers/base/ns_stage.yaml delete mode 100644 test/templates/nstemplatetiers/base/spacerole_admin.yaml delete mode 100644 test/templates/nstemplatetiers/base/tier.yaml delete mode 100644 test/templates/nstemplatetiers/doc.go delete mode 100644 test/templates/nstemplatetiers/metadata.yaml delete mode 100644 test/templates/nstemplatetiers/nocluster/ns_dev.yaml delete mode 100644 test/templates/nstemplatetiers/nocluster/ns_stage.yaml delete mode 100644 test/templates/nstemplatetiers/nocluster/spacerole_admin.yaml delete mode 100644 test/templates/nstemplatetiers/nocluster/tier.yaml diff --git a/controllers/usersignup/usersignup_controller.go b/controllers/usersignup/usersignup_controller.go index 5ab4624cb..8ab9dac89 100644 --- a/controllers/usersignup/usersignup_controller.go +++ b/controllers/usersignup/usersignup_controller.go @@ -380,6 +380,7 @@ func (r *Reconciler) checkIfMurAlreadyExists( } logger.Info("Setting UserSignup status to 'Complete'") + return true, r.updateStatus(ctx, userSignup, r.updateCompleteStatus(mur.Name)) } diff --git a/go.mod b/go.mod index 2b1284fb1..60eaf6e7b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ require ( github.com/codeready-toolchain/api v0.0.0-20240322110702-5ab3840476e9 github.com/codeready-toolchain/toolchain-common v0.0.0-20240403070054-183e7d407080 github.com/davecgh/go-spew v1.1.1 // indirect - github.com/ghodss/yaml v1.0.0 github.com/go-bindata/go-bindata v3.1.2+incompatible github.com/go-logr/logr v1.2.3 github.com/gofrs/uuid v4.4.0+incompatible @@ -33,6 +32,8 @@ require ( require github.com/google/uuid v1.3.0 // indirect +replace github.com/codeready-toolchain/toolchain-common => github.com/matousjobanek/toolchain-common v0.0.0-20240403092205-8439bbc15fa2 + require ( cloud.google.com/go v0.97.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect diff --git a/go.sum b/go.sum index 98ce59e69..fc2be32c8 100644 --- a/go.sum +++ b/go.sum @@ -138,8 +138,6 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/codeready-toolchain/api v0.0.0-20240322110702-5ab3840476e9 h1:Lm7bFLrzfJzrUiRGVqtsSaZMpj+akLiR/fvAFjjE9gM= github.com/codeready-toolchain/api v0.0.0-20240322110702-5ab3840476e9/go.mod h1:cfNN6YPX4TORvhhZXMSjSPesqAHlB3nD/WAfGe4WLKQ= -github.com/codeready-toolchain/toolchain-common v0.0.0-20240403070054-183e7d407080 h1:2ZzjVjiEbQmJoLrGfY4A4VH4MEkYj030aZ1gnDUhLgs= -github.com/codeready-toolchain/toolchain-common v0.0.0-20240403070054-183e7d407080/go.mod h1:OJ3L9aaTRMGjxr2WeH/9l6m5OjExwEK3Bp/+P+efoGg= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -432,6 +430,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matousjobanek/toolchain-common v0.0.0-20240403092205-8439bbc15fa2 h1:x1PVCNIzpE/iDB4ED/1yAQbXZck0L9uoh9RgTh/nBCM= +github.com/matousjobanek/toolchain-common v0.0.0-20240403092205-8439bbc15fa2/go.mod h1:OJ3L9aaTRMGjxr2WeH/9l6m5OjExwEK3Bp/+P+efoGg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= diff --git a/make/go.mk b/make/go.mk index f2a38058d..7ced72ea9 100644 --- a/make/go.mk +++ b/make/go.mk @@ -26,7 +26,6 @@ vendor: NSTEMPLATES_BASEDIR = deploy/templates/nstemplatetiers NSTEMPLATES_FILES = $(wildcard $(NSTEMPLATES_BASEDIR)/**/*.yaml) -NSTEMPLATES_TEST_BASEDIR = test/templates/nstemplatetiers USERTEMPLATES_BASEDIR = deploy/templates/usertiers USERTEMPLATES_TEST_BASEDIR = test/templates/usertiers @@ -57,9 +56,6 @@ generate-assets: go-bindata @echo "generating bindata for files in $(NSTEMPLATES_BASEDIR) ..." @rm ./pkg/templates/nstemplatetiers/nstemplatetier_assets.go 2>/dev/null || true @$(GO_BINDATA) -pkg nstemplatetiers -o ./pkg/templates/nstemplatetiers/nstemplatetier_assets.go -nometadata -nocompress -prefix $(NSTEMPLATES_BASEDIR) $(NSTEMPLATES_BASEDIR)/... - @echo "generating bindata for files in $(NSTEMPLATES_TEST_BASEDIR) ..." - @rm ./test/templates/nstemplatetiers/nstemplatetier_assets.go 2>/dev/null || true - @$(GO_BINDATA) -pkg nstemplatetiers_test -o ./test/templates/nstemplatetiers/nstemplatetier_assets.go -nometadata -nocompress -prefix $(NSTEMPLATES_TEST_BASEDIR) -ignore doc.go $(NSTEMPLATES_TEST_BASEDIR)/... @echo "generating bindata for files in $(USERTEMPLATES_BASEDIR) ..." @rm ./pkg/templates/usertiers/usertier_assets.go 2>/dev/null || true diff --git a/pkg/templates/nstemplatetiers/nstemplatetier_generator.go b/pkg/templates/nstemplatetiers/nstemplatetier_generator.go index 636e20755..bb8500281 100644 --- a/pkg/templates/nstemplatetiers/nstemplatetier_generator.go +++ b/pkg/templates/nstemplatetiers/nstemplatetier_generator.go @@ -2,490 +2,50 @@ package nstemplatetiers import ( "context" - "fmt" - "reflect" - "sort" - "strings" - toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1" "github.com/codeready-toolchain/host-operator/pkg/templates/assets" commonclient "github.com/codeready-toolchain/toolchain-common/pkg/client" - commonTemplate "github.com/codeready-toolchain/toolchain-common/pkg/template" + "github.com/codeready-toolchain/toolchain-common/pkg/template/nstemplatetiers" + apierrors "k8s.io/apimachinery/pkg/api/errors" - templatev1 "github.com/openshift/api/template/v1" "github.com/pkg/errors" "gopkg.in/yaml.v2" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/client-go/kubernetes/scheme" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" - logf "sigs.k8s.io/controller-runtime/pkg/log" ) -var log = logf.Log.WithName("templates") - // CreateOrUpdateResources generates the NSTemplateTier resources from the cluster resource template and namespace templates, // then uses the manager's client to create or update the resources on the cluster. func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runtimeclient.Client, namespace string, assets assets.Assets) error { - // initialize tier generator, loads templates from assets - generator, err := newNSTemplateTierGenerator(s, client, namespace, assets) - if err != nil { - return errors.Wrap(err, "unable to init NSTemplateTier generator") - } - - // create the TierTemplate resources - err = generator.createTierTemplates(ctx) - if err != nil { - return errors.Wrap(err, "unable to create TierTemplates") - } - - // create the NSTemplateTier resources - err = generator.createNSTemplateTiers(ctx) - if err != nil { - return errors.Wrap(err, "unable to create NSTemplateTiers") - } - - return nil -} - -type tierGenerator struct { - client runtimeclient.Client - namespace string - scheme *runtime.Scheme - templatesByTier map[string]*tierData -} - -type tierData struct { - name string - rawTemplates *templates - tierTemplates []*toolchainv1alpha1.TierTemplate - objects []runtimeclient.Object - basedOnTier *BasedOnTier -} - -// templates: namespaces and other cluster-scoped resources belonging to a given tier ("advanced", "base", "team", etc.) and the NSTemplateTier that combines them -type templates struct { - nsTemplateTier *template // NSTemplateTier resource with tier-scoped configuration and references to namespace and cluster templates in its spec, in a single template file - clusterTemplate *template // other cluster-scoped resources, in a single template file - namespaceTemplates map[string]template // namespace templates (including roles, limits, etc.) indexed by type ("dev", "stage") - spaceroleTemplates map[string]template // spacerole templates (including rolebindings, etc.) indexed by role ("admin", "viewer", etc.) - basedOnTier *template // a special config defining which tier should be reused and which parameters should be overridden -} - -// template: a template's content and its latest git revision -type template struct { - revision string - content []byte -} - -// newNSTemplateTierGenerator loads templates from the provided assets and processes the tierTemplates and NSTemplateTiers -func newNSTemplateTierGenerator(s *runtime.Scheme, client runtimeclient.Client, namespace string, assets assets.Assets) (*tierGenerator, error) { // load templates from assets - templatesByTier, err := loadTemplatesByTiers(assets) - if err != nil { - return nil, err - } - - c := &tierGenerator{ - client: client, - namespace: namespace, - scheme: s, - templatesByTier: templatesByTier, - } - - // process tierTemplates - if err := c.initTierTemplates(); err != nil { - return nil, err - } - - // process NSTemplateTiers - if err := c.initNSTemplateTiers(); err != nil { - return nil, err - } - - return c, nil -} - -// BasedOnTier defines which tier is supposed to be reused and which parameters should be modified -// An example: -// -// from: base -// parameters: -// - name: IDLER_TIMEOUT_SECONDS -// value: 43200 -// -// Which defines that for creating baseextendedidling tier the base tier should be used and -// the parameter IDLER_TIMEOUT_SECONDS should be set to 43200 -type BasedOnTier struct { - Revision string - From string `json:"from"` - Parameters []templatev1.Parameter `json:"parameters,omitempty" protobuf:"bytes,4,rep,name=parameters"` -} - -// loadTemplatesByTiers loads the assets and dispatches them by tiers, assuming the given `assets` has the following structure: -// -// metadata.yaml -// advanced/ -// -// based_on_tier.yaml -// -// base1ns/ -// -// cluster.yaml -// ns_dev.yaml -// ns_stage.yaml -// spacerole_admin.yaml -// tier.yaml -// -// team/ -// -// based_on_tier.yaml -// -// The output is a map of `tierData` indexed by tier. -// Each `tierData` object contains itself a map of `template` objects indexed by the namespace type (`namespaceTemplates`); -// an optional `template` for the cluster resources (`clusterTemplate`) and the NSTemplateTier resource object. -// Each `template` object contains a `revision` (`string`) and the `content` of the template to apply (`[]byte`) -func loadTemplatesByTiers(assets assets.Assets) (map[string]*tierData, error) { metadataContent, err := assets.Asset("metadata.yaml") if err != nil { - return nil, errors.Wrapf(err, "unable to load templates") + return errors.Wrapf(err, "unable to load templates") } metadata := make(map[string]string) err = yaml.Unmarshal([]byte(metadataContent), &metadata) if err != nil { - return nil, errors.Wrapf(err, "unable to load templates") + return errors.Wrapf(err, "unable to load templates") } - - results := make(map[string]*tierData) + files := map[string][]byte{} for _, name := range assets.Names() { - if name == "metadata.yaml" { - continue - } - // split the name using the `/` separator - parts := strings.Split(name, "/") - // skip any name that does not have 2 parts - if len(parts) != 2 { - return nil, fmt.Errorf("unable to load templates: invalid name format for file '%s'", name) - } - tier := parts[0] - filename := parts[1] - if _, exists := results[tier]; !exists { - results[tier] = &tierData{ - name: tier, - rawTemplates: &templates{ - namespaceTemplates: map[string]template{}, - spaceroleTemplates: map[string]template{}, - }, - } - } content, err := assets.Asset(name) if err != nil { - return nil, errors.Wrapf(err, "unable to load templates") - } - tmpl := template{ - revision: metadata[strings.TrimSuffix(name, ".yaml")], - content: content, - } - switch { - case filename == "tier.yaml": - results[tier].rawTemplates.nsTemplateTier = &tmpl - case filename == "cluster.yaml": - results[tier].rawTemplates.clusterTemplate = &tmpl - case strings.HasPrefix(filename, "ns_"): - kind := strings.TrimSuffix(strings.TrimPrefix(filename, "ns_"), ".yaml") - results[tier].rawTemplates.namespaceTemplates[kind] = tmpl - case strings.HasPrefix(filename, "spacerole_"): - role := strings.TrimSuffix(strings.TrimPrefix(filename, "spacerole_"), ".yaml") - results[tier].rawTemplates.spaceroleTemplates[role] = tmpl - case filename == "based_on_tier.yaml": - basedOnTier := &BasedOnTier{} - if err := yaml.Unmarshal(content, basedOnTier); err != nil { - return nil, errors.Wrapf(err, "unable to unmarshal '%s'", name) - } - results[tier].rawTemplates.basedOnTier = &tmpl - results[tier].basedOnTier = basedOnTier - default: - return nil, errors.Errorf("unable to load templates: unknown scope for file '%s'", name) + return errors.Wrapf(err, "unable to load templates") } + files[name] = content } - // check that none of the tiers uses combination of based_on_tier.yaml file together with any template file - for tier, tierData := range results { - if tierData.rawTemplates.basedOnTier != nil && - (tierData.rawTemplates.clusterTemplate != nil || - len(tierData.rawTemplates.namespaceTemplates) > 0 || - tierData.rawTemplates.nsTemplateTier != nil) { - return nil, fmt.Errorf("the tier %s contains a mix of based_on_tier.yaml file together with a regular template file", tier) - } - } - return results, nil -} - -// initTierTemplates generates all TierTemplate resources, and adds them to the tier map indexed by tier name -func (t *tierGenerator) initTierTemplates() error { - - // process tiers in alphabetical order - tiers := make([]string, 0, len(t.templatesByTier)) - for tier := range t.templatesByTier { - tiers = append(tiers, tier) - } - sort.Strings(tiers) - for _, tier := range tiers { - tierData := t.templatesByTier[tier] - basedOnTierFileRevision := "" - var parameters []templatev1.Parameter - if tierData.basedOnTier != nil { - parameters = tierData.basedOnTier.Parameters - basedOnTierFileRevision = tierData.rawTemplates.basedOnTier.revision - tierData = t.templatesByTier[tierData.basedOnTier.From] - } - tierTemplates, err := t.newTierTemplates(basedOnTierFileRevision, tierData, tier, parameters) - if err != nil { - return err - } - t.templatesByTier[tier].tierTemplates = tierTemplates - } - - return nil -} - -func (t *tierGenerator) newTierTemplates(basedOnTierFileRevision string, tierData *tierData, tier string, parameters []templatev1.Parameter) ([]*toolchainv1alpha1.TierTemplate, error) { - decoder := serializer.NewCodecFactory(t.scheme).UniversalDeserializer() - - // namespace templates - kinds := make([]string, 0, len(tierData.rawTemplates.namespaceTemplates)) - for kind := range tierData.rawTemplates.namespaceTemplates { - kinds = append(kinds, kind) - } - tierTmpls := []*toolchainv1alpha1.TierTemplate{} - sort.Strings(kinds) - for _, kind := range kinds { - tmpl := tierData.rawTemplates.namespaceTemplates[kind] - tierTmpl, err := t.newTierTemplate(decoder, basedOnTierFileRevision, tier, kind, tmpl, parameters) - if err != nil { - return nil, err - } - tierTmpls = append(tierTmpls, tierTmpl) - } - // space roles templates - roles := make([]string, 0, len(tierData.rawTemplates.spaceroleTemplates)) - for role := range tierData.rawTemplates.spaceroleTemplates { - roles = append(roles, role) - } - sort.Strings(roles) - for _, role := range roles { - tmpl := tierData.rawTemplates.spaceroleTemplates[role] - tierTmpl, err := t.newTierTemplate(decoder, basedOnTierFileRevision, tier, role, tmpl, parameters) - if err != nil { - return nil, err - } - tierTmpls = append(tierTmpls, tierTmpl) - } - // cluster resources templates - if tierData.rawTemplates.clusterTemplate != nil { - tierTmpl, err := t.newTierTemplate(decoder, basedOnTierFileRevision, tier, toolchainv1alpha1.ClusterResourcesTemplateType, *tierData.rawTemplates.clusterTemplate, parameters) - if err != nil { - return nil, err - } - tierTmpls = append(tierTmpls, tierTmpl) - } - return tierTmpls, nil -} - -// createTierTemplates creates all TierTemplate resources from the tier map -func (t *tierGenerator) createTierTemplates(ctx context.Context) error { - // create the templates - for _, tierTmpls := range t.templatesByTier { - for _, tierTmpl := range tierTmpls.tierTemplates { - log.Info("creating TierTemplate", "namespace", tierTmpl.Namespace, "name", tierTmpl.Name) - // using the "standard" client since we don't need to support updates on such resources, they should be immutable - if err := t.client.Create(ctx, tierTmpl); err != nil && !apierrors.IsAlreadyExists(err) { - return errors.Wrapf(err, "unable to create the '%s' TierTemplate in namespace '%s'", tierTmpl.Name, tierTmpl.Namespace) - } - log.Info("TierTemplate resource created", "namespace", tierTmpl.Namespace, "name", tierTmpl.Name) - } - } - return nil -} - -// newTierTemplate generates a TierTemplate resource for a given tier and kind -func (t *tierGenerator) newTierTemplate(decoder runtime.Decoder, basedOnTierFileRevision, tier, kind string, tmpl template, parameters []templatev1.Parameter) (*toolchainv1alpha1.TierTemplate, error) { - if basedOnTierFileRevision == "" { - basedOnTierFileRevision = tmpl.revision - } - revision := fmt.Sprintf("%s-%s", basedOnTierFileRevision, tmpl.revision) - name := NewTierTemplateName(tier, kind, revision) - tmplObj := &templatev1.Template{} - _, _, err := decoder.Decode(tmpl.content, nil, tmplObj) - if err != nil { - return nil, errors.Wrapf(err, "unable to generate '%s' TierTemplate manifest", name) - } - setParams(parameters, tmplObj) - - return &toolchainv1alpha1.TierTemplate{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, - Name: name, // link to the TierTemplate resource, whose name is: `--`, - }, - Spec: toolchainv1alpha1.TierTemplateSpec{ - Revision: revision, - TierName: tier, - Type: kind, - Template: *tmplObj, - }, - }, nil -} - -// setParams sets the value for each of the keys in the given parameter set to the template, but only if the key exists there -func setParams(parametersToSet []templatev1.Parameter, tmpl *templatev1.Template) { - for _, paramToSet := range parametersToSet { - for i, param := range tmpl.Parameters { - if param.Name == paramToSet.Name { - tmpl.Parameters[i].Value = paramToSet.Value - break + // initialize tier generator, loads templates from assets + return nstemplatetiers.GenerateTiers(s, func(toEnsure runtimeclient.Object, canUpdate bool) (bool, error) { + if !canUpdate { + if err := client.Create(ctx, toEnsure); err != nil && !apierrors.IsAlreadyExists(err) { + return false, err } + return true, nil } - } -} - -// NewTierTemplateName a utility func to generate a TierTemplate name, based on the given tier, kind and revision. -// note: the resource name must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character -func NewTierTemplateName(tier, kind, revision string) string { - return strings.ToLower(fmt.Sprintf("%s-%s-%s", tier, kind, revision)) -} - -// newNSTemplateTiers generates all NSTemplateTier resources and adds them to the tier map -func (t *tierGenerator) initNSTemplateTiers() error { - - for tierName, tierData := range t.templatesByTier { - nsTemplateTier := tierData.rawTemplates.nsTemplateTier - tierTemplates := tierData.tierTemplates - sourceTierName := tierName - var parameters []templatev1.Parameter - if tierData.basedOnTier != nil { - parameters = tierData.basedOnTier.Parameters - fromData := t.templatesByTier[tierData.basedOnTier.From] - nsTemplateTier = fromData.rawTemplates.nsTemplateTier - sourceTierName = fromData.name - } - objs, err := t.newNSTemplateTier(sourceTierName, tierName, *nsTemplateTier, tierTemplates, parameters) - if err != nil { - return err - } - t.templatesByTier[tierName].objects = objs - } - - return nil -} - -// createNSTemplateTiers creates the NSTemplateTier resources from the tier map -func (t *tierGenerator) createNSTemplateTiers(ctx context.Context) error { - applyCl := commonclient.NewApplyClient(t.client) - - for tierName, tierData := range t.templatesByTier { - if len(tierData.objects) != 1 { - return fmt.Errorf("there is an unexpected number of NSTemplateTier object to be applied for tier name '%s'; expected: 1; actual: %d", tierName, len(tierData.objects)) - } - - unstructuredObj, ok := tierData.objects[0].(*unstructured.Unstructured) - if !ok { - return fmt.Errorf("unable to cast NSTemplateTier '%s' to Unstructured object '%+v'", tierName, tierData.objects[0]) - } - tier := &toolchainv1alpha1.NSTemplateTier{} - if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredObj.Object, tier); err != nil { - return err - } - - labels := tier.GetLabels() - if labels == nil { - labels = make(map[string]string) - } - labels[toolchainv1alpha1.ProviderLabelKey] = toolchainv1alpha1.ProviderLabelValue - updated, err := applyCl.ApplyObject(ctx, tier, commonclient.ForceUpdate(true)) - if err != nil { - return errors.Wrapf(err, "unable to create or update the '%s' NSTemplateTier", tierName) - } - tierLog := log.WithValues("name", tierName) - if tier.Spec.ClusterResources != nil { - tierLog = tierLog.WithValues("clusterResourcesTemplate", tier.Spec.ClusterResources.TemplateRef) - } - for i, nsTemplate := range tier.Spec.Namespaces { - tierLog = tierLog.WithValues(fmt.Sprintf("namespaceTemplate-%d", i), nsTemplate.TemplateRef) - } - for role, nsTemplate := range tier.Spec.SpaceRoles { - tierLog = tierLog.WithValues(fmt.Sprintf("spaceRoleTemplate-%s", role), nsTemplate.TemplateRef) - } - if updated { - tierLog.Info("NSTemplateTier was either updated or created") - } else { - tierLog.Info("NSTemplateTier wasn't updated nor created: the spec was already set as expected") - } - } - return nil -} - -// NewNSTemplateTier generates a complete NSTemplateTier object via Openshift Template based on the contents of tier.yaml and -// by embedding the `-code.yaml`, `-dev.yaml` and `-stage.yaml` and cluster.yaml references. -// -// After processing the Openshift Template the NSTemplateTier should look something like: -// ------ -// kind: NSTemplateTier -// -// metadata: -// name: appstudio -// spec: -// deactivationTimeoutDays: 30 -// clusterResources: -// templateRef: appstudio-clusterresources-07cac69-07cac69 -// namespaces: -// - templateRef: appstudio-code-cb6fbd2-cb6fbd2 -// - templateRef: appstudio-dev-4d49fe0-4d49fe0 -// - templateRef: appstudio-stage-4d49fe0-4d49fe0 -// spaceRoles: -// admin: -// templateRef: appstudio-admin-ab12cd34-ab12cd34 -// viewer: -// templateRef: appstudio-admin-ab12cd34-ab12cd34 -// -// ------ -func (t *tierGenerator) newNSTemplateTier(sourceTierName, tierName string, nsTemplateTier template, tierTemplates []*toolchainv1alpha1.TierTemplate, parameters []templatev1.Parameter) ([]runtimeclient.Object, error) { - decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer() - if reflect.DeepEqual(nsTemplateTier, template{}) { - return nil, fmt.Errorf("tier %s is missing a tier.yaml file", tierName) - } - - tmplObj := &templatev1.Template{} - _, _, err := decoder.Decode(nsTemplateTier.content, nil, tmplObj) - if err != nil { - return nil, errors.Wrapf(err, "unable to generate '%s' NSTemplateTier manifest", tierName) - } - - tmplProcessor := commonTemplate.NewProcessor(t.scheme) - params := map[string]string{"NAMESPACE": t.namespace} - - for _, tierTmpl := range tierTemplates { - switch tierTmpl.Spec.Type { - // ClusterResources - case toolchainv1alpha1.ClusterResourcesTemplateType: - params["CLUSTER_TEMPL_REF"] = tierTmpl.Name - // Namespaces and Space Roles - default: - tmplType := strings.ToUpper(tierTmpl.Spec.Type) // code, dev, stage - key := tmplType + "_TEMPL_REF" // eg. CODE_TEMPL_REF - params[key] = tierTmpl.Name - } - } - setParams(parameters, tmplObj) - toolchainObjects, err := tmplProcessor.Process(tmplObj.DeepCopy(), params) - if err != nil { - return nil, err - } - for i := range toolchainObjects { - toolchainObjects[i].SetName(strings.Replace(toolchainObjects[i].GetName(), sourceTierName, tierName, 1)) - } - return toolchainObjects, nil + applyCl := commonclient.NewApplyClient(client) + return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true)) + }, namespace, metadata, files) } diff --git a/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go b/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go index 9122fae65..eb2e1de9f 100644 --- a/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go +++ b/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go @@ -3,6 +3,7 @@ package nstemplatetiers_test import ( "context" "fmt" + "strings" "testing" "github.com/gofrs/uuid" @@ -11,335 +12,120 @@ import ( "github.com/codeready-toolchain/host-operator/pkg/apis" "github.com/codeready-toolchain/host-operator/pkg/templates/assets" "github.com/codeready-toolchain/host-operator/pkg/templates/nstemplatetiers" - testnstemplatetiers "github.com/codeready-toolchain/host-operator/test/templates/nstemplatetiers" commontest "github.com/codeready-toolchain/toolchain-common/pkg/test" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/scheme" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" ) -func TestCreateOrUpdateResources(t *testing.T) { +var expectedProdTiers = []string{ + "advanced", + "base", + "base1ns", + "base1nsnoidling", + "base1ns6didler", + "baselarge", + "baseextendedidling", + "test", + "appstudio", + "appstudiolarge", + "appstudio-env", +} + +func nsTypes(tier string) []string { + switch tier { + case "appstudio", "appstudiolarge": + return []string{"tenant"} + case "appstudio-env": + return []string{"env"} + case "base1ns", "base1nsnoidling", "base1ns6didler", "test": + return []string{"dev"} + default: + return []string{"dev", "stage"} + } +} + +func roles(tier string) []string { + switch tier { + case "appstudio", "appstudio-env", "appstudiolarge": + return []string{"admin", "maintainer", "contributor"} + default: + return []string{"admin"} + } +} + +func TestCreateOrUpdateResourcesWitProdAssets(t *testing.T) { s := scheme.Scheme err := apis.AddToScheme(s) require.NoError(t, err) logf.SetLogger(zap.New(zap.UseDevMode(true))) + namespace := "host-operator-" + uuid.Must(uuid.NewV4()).String()[:7] + cl := commontest.NewFakeClient(t) + templateAssets := assets.NewAssets(nstemplatetiers.AssetNames, nstemplatetiers.Asset) - testassets := assets.NewAssets(testnstemplatetiers.AssetNames, testnstemplatetiers.Asset) - - t.Run("ok", func(t *testing.T) { - - expectedTemplateRefs := map[string]map[string]interface{}{ - "advanced": { - "clusterresources": "advanced-clusterresources-abcd123-654321a", - "namespaces": []string{ - "advanced-dev-abcd123-123456b", - "advanced-stage-abcd123-123456c", - }, - "spaceRoles": map[string]string{ - "admin": "advanced-admin-abcd123-123456d", - }, - }, - "base": { - "clusterresources": "base-clusterresources-654321a-654321a", - "namespaces": []string{ - "base-dev-123456b-123456b", - "base-stage-123456c-123456c", - }, - "spaceRoles": map[string]string{ - "admin": "base-admin-123456d-123456d", - }, - }, - "nocluster": { - "namespaces": []string{ - "nocluster-dev-123456j-123456j", - "nocluster-stage-1234567-1234567", - }, - "spaceRoles": map[string]string{ - "admin": "nocluster-admin-123456k-123456k", - }, - }, - "appstudio": { - "clusterresources": "appstudio-clusterresources-654321a-654321a", - "namespaces": []string{ - "appstudio-tenant-123456b-123456b", - }, - "spaceRoles": map[string]string{ - "admin": "appstudio-admin-123456c-123456c", - "maintainer": "appstudio-maintainer-123456d-123456d", - "contributor": "appstudio-contributor-123456e-123456e", - }, - }, - } + // when + err = nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, cl, namespace, templateAssets) - t.Run("create only", func(t *testing.T) { - // given - namespace := "host-operator" + uuid.Must(uuid.NewV4()).String()[:7] - clt := commontest.NewFakeClient(t) - // verify that no NSTemplateTier resources exist prior to creation - nsTmplTiers := toolchainv1alpha1.NSTemplateTierList{} - err = clt.List(context.TODO(), &nsTmplTiers, runtimeclient.InNamespace(namespace)) - require.NoError(t, err) - require.Empty(t, nsTmplTiers.Items) - // verify that no TierTemplate resources exist prior to creation - tierTmpls := toolchainv1alpha1.TierTemplateList{} - err = clt.List(context.TODO(), &tierTmpls, runtimeclient.InNamespace(namespace)) - require.NoError(t, err) - require.Empty(t, tierTmpls.Items) - - assets := assets.NewAssets(testnstemplatetiers.AssetNames, testnstemplatetiers.Asset) - - // when - err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, assets) - - // then - require.NoError(t, err) - - // verify that TierTemplates were created - tierTmpls = toolchainv1alpha1.TierTemplateList{} - err = clt.List(context.TODO(), &tierTmpls, runtimeclient.InNamespace(namespace)) - require.NoError(t, err) - require.Len(t, tierTmpls.Items, 16) // 4 items for advanced and base tiers + 3 for nocluster tier + 5 for appstudio - names := []string{} - for _, tierTmpl := range tierTmpls.Items { - names = append(names, tierTmpl.Name) - } - require.ElementsMatch(t, []string{ - "advanced-clusterresources-abcd123-654321a", - "advanced-dev-abcd123-123456b", - "advanced-stage-abcd123-123456c", - "advanced-admin-abcd123-123456d", - "base-clusterresources-654321a-654321a", - "base-dev-123456b-123456b", - "base-stage-123456c-123456c", - "base-admin-123456d-123456d", - "nocluster-dev-123456j-123456j", - "nocluster-stage-1234567-1234567", - "nocluster-admin-123456k-123456k", - "appstudio-clusterresources-654321a-654321a", - "appstudio-tenant-123456b-123456b", - "appstudio-admin-123456c-123456c", - "appstudio-maintainer-123456d-123456d", - "appstudio-contributor-123456e-123456e", - }, names) - - // verify that 4 NSTemplateTier CRs were created: - for _, tierName := range []string{"advanced", "base", "nocluster", "appstudio"} { - tier := toolchainv1alpha1.NSTemplateTier{} - err = clt.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: tierName}, &tier) - require.NoError(t, err) - assert.Equal(t, int64(1), tier.ObjectMeta.Generation) - - // check the `clusterresources` templateRef - if tier.Name == "nocluster" { - assert.Nil(t, tier.Spec.ClusterResources) // "nocluster" tier should not have cluster resources set - } else { - require.NotNil(t, tier.Spec.ClusterResources) - assert.Equal(t, expectedTemplateRefs[tierName]["clusterresources"], tier.Spec.ClusterResources.TemplateRef) - } - - // check the `namespaces` templateRefs - actualNamespaceTmplRefs := make([]string, len(tier.Spec.Namespaces)) - for i, ns := range tier.Spec.Namespaces { - actualNamespaceTmplRefs[i] = ns.TemplateRef - } - assert.ElementsMatch(t, expectedTemplateRefs[tierName]["namespaces"], actualNamespaceTmplRefs) - - // check the `spaceRoles` templateRefs - actualSpaceRoleTmplRefs := make(map[string]string, len(tier.Spec.SpaceRoles)) - for i, r := range tier.Spec.SpaceRoles { - actualSpaceRoleTmplRefs[i] = r.TemplateRef - } - for role, tmpl := range expectedTemplateRefs[tierName]["spaceRoles"].(map[string]string) { - assert.Equal(t, tmpl, actualSpaceRoleTmplRefs[role]) - } - } - }) - - t.Run("create then update with same tier templates", func(t *testing.T) { - // given - namespace := "host-operator" + uuid.Must(uuid.NewV4()).String()[:7] - clt := commontest.NewFakeClient(t) - - // when - err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, testassets) - require.NoError(t, err) - - // when calling CreateOrUpdateResources a second time - err = nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, testassets) - - // then - require.NoError(t, err) - // verify that all TierTemplate CRs were updated - tierTmpls := toolchainv1alpha1.TierTemplateList{} - err = clt.List(context.TODO(), &tierTmpls, runtimeclient.InNamespace(namespace)) - require.NoError(t, err) - require.Len(t, tierTmpls.Items, 16) // 4 items for advanced and base tiers + 3 for nocluster tier + 4 for appstudio - for _, tierTmpl := range tierTmpls.Items { - assert.Equal(t, int64(1), tierTmpl.ObjectMeta.Generation) // unchanged + // then + require.NoError(t, err) + nstemplateTiers := &toolchainv1alpha1.NSTemplateTierList{} + err = cl.List(context.TODO(), nstemplateTiers, runtimeclient.InNamespace(namespace)) + require.NoError(t, err) + expectedClusterResourcesTmplRef := map[string]string{} + expectedNamespaceTmplRefs := map[string][]string{} + expectedSpaceRoleTmplRefs := map[string]map[string]string{} + require.Len(t, nstemplateTiers.Items, len(expectedProdTiers)) + for _, tier := range expectedProdTiers { + t.Run(tier, func(t *testing.T) { + for _, nsTypeName := range nsTypes(tier) { + t.Run("ns-"+nsTypeName, func(t *testing.T) { + templateName := verifyTierTemplate(t, cl, namespace, tier, nsTypeName) + expectedNamespaceTmplRefs[tier] = append(expectedNamespaceTmplRefs[tier], templateName) + }) } - - // verify that 4 NSTemplateTier CRs were created: - for _, tierName := range []string{"advanced", "base", "nocluster", "appstudio"} { - tier := toolchainv1alpha1.NSTemplateTier{} - err = clt.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: tierName}, &tier) - require.NoError(t, err) - assert.Equal(t, int64(1), tier.ObjectMeta.Generation) - - // check the `clusterresources` templateRef - if tier.Name == "nocluster" { - assert.Nil(t, tier.Spec.ClusterResources) - } else { - require.NotNil(t, tier.Spec.ClusterResources) - assert.Equal(t, expectedTemplateRefs[tierName]["clusterresources"], tier.Spec.ClusterResources.TemplateRef) - } - - // check the `namespaces` templateRefs - actualTemplateRefs := make([]string, len(tier.Spec.Namespaces)) - for i, ns := range tier.Spec.Namespaces { - actualTemplateRefs[i] = ns.TemplateRef - } - assert.ElementsMatch(t, expectedTemplateRefs[tierName]["namespaces"], actualTemplateRefs) - - // check the `spaceRoles` templateRefs - actualSpaceRoleTmplRefs := make(map[string]string, len(tier.Spec.SpaceRoles)) - for i, r := range tier.Spec.SpaceRoles { - actualSpaceRoleTmplRefs[i] = r.TemplateRef - } - for role, tmpl := range expectedTemplateRefs[tierName]["spaceRoles"].(map[string]string) { - assert.Equal(t, tmpl, actualSpaceRoleTmplRefs[role]) - } + for _, role := range roles(tier) { + t.Run("spacerole-"+role, func(t *testing.T) { + roleName := verifyTierTemplate(t, cl, namespace, tier, role) + if expectedSpaceRoleTmplRefs[tier] == nil { + expectedSpaceRoleTmplRefs[tier] = map[string]string{} + } + expectedSpaceRoleTmplRefs[tier][role] = roleName + }) } - }) - - t.Run("create then update with new tier templates", func(t *testing.T) { - // given - namespace := "host-operator" + uuid.Must(uuid.NewV4()).String()[:7] - clt := commontest.NewFakeClient(t) - - // when - err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, testassets) - require.NoError(t, err) - - // given a new set of tier templates (same content but new revisions, which is what we'll want to check here) - testassets = assets.NewAssets(testnstemplatetiers.AssetNames, func(name string) ([]byte, error) { - if name == "metadata.yaml" { - return []byte( - `advanced/based_on_tier: "111111a"` + "\n" + - `base/cluster: "222222a"` + "\n" + - `base/ns_dev: "222222b"` + "\n" + - `base/ns_stage: "222222c"` + "\n" + - `base/spacerole_admin: "222222d"` + "\n" + - `nocluster/ns_dev: "333333a"` + "\n" + - `nocluster/ns_stage: "333333b"` + "\n" + - `nocluster/spacerole_admin: "333333c"` + "\n" + - `appstudio/cluster: "444444a"` + "\n" + - `appstudio/ns_tenant: "444444b"` + "\n" + - `appstudio/spacerole_admin: "444444c"` + "\n" + - `appstudio/spacerole_maintainer: "444444d"` + "\n" + - `appstudio/spacerole_contributor: "444444e"` + "\n"), nil - } - // return default content for other assets - return testnstemplatetiers.Asset(name) + t.Run("clusterresources", func(t *testing.T) { + templateName := verifyTierTemplate(t, cl, namespace, tier, "clusterresources") + expectedClusterResourcesTmplRef[tier] = templateName }) + }) + } + // verify that each NSTemplateTier has the ClusterResources, Namespaces and SpaceRoles `TemplateRef` set as expected - // when calling CreateOrUpdateResources a second time - err = nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, testassets) + for _, nstmplTier := range nstemplateTiers.Items { + // verify tier configuration - // then - require.NoError(t, err) - // verify that all TierTemplate CRs for the new revisions were created - tierTmpls := toolchainv1alpha1.TierTemplateList{} - err = clt.List(context.TODO(), &tierTmpls, runtimeclient.InNamespace(namespace)) - require.NoError(t, err) - require.Len(t, tierTmpls.Items, 32) // two versions of: 4 items for advanced and base tiers + 3 for nocluster tier + 4 for appstudio - for _, tierTmpl := range tierTmpls.Items { - assert.Equal(t, int64(1), tierTmpl.ObjectMeta.Generation) // unchanged - } + require.Contains(t, expectedProdTiers, nstmplTier.Name) - expectedTemplateRefs := map[string]map[string]interface{}{ - "advanced": { - "clusterresources": "advanced-clusterresources-111111a-222222a", - "namespaces": []string{ - "advanced-dev-111111a-222222b", - "advanced-stage-111111a-222222c", - }, - "spaceRoles": map[string]string{ - "admin": "advanced-admin-111111a-222222d", - }, - }, - "base": { - "clusterresources": "base-clusterresources-222222a-222222a", - "namespaces": []string{ - "base-dev-222222b-222222b", - "base-stage-222222c-222222c", - }, - "spaceRoles": map[string]string{ - "admin": "base-admin-222222d-222222d", - }, - }, - "nocluster": { - "namespaces": []string{ - "nocluster-dev-333333a-333333a", - "nocluster-stage-333333b-333333b", - }, - "spaceRoles": map[string]string{ - "admin": "nocluster-admin-333333c-333333c", - }, - }, - "appstudio": { - "clusterresources": "appstudio-clusterresources-444444a-444444a", - "namespaces": []string{ - "appstudio-dev-444444a-444444a", - "appstudio-stage-444444b-444444b", - }, - "spaceRoles": map[string]string{ - "admin": "appstudio-admin-444444c-444444c", - "maintainer": "appstudio-maintainer-444444d-444444d", - "contributor": "appstudio-contributor-444444e-444444e", - }, - }, - } - // verify that the 3 NStemplateTier CRs were updated - for _, tierName := range []string{"advanced", "base", "nocluster"} { - tier := toolchainv1alpha1.NSTemplateTier{} - err = clt.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: tierName}, &tier) - require.NoError(t, err) - assert.Equal(t, int64(2), tier.ObjectMeta.Generation) - - // check the `clusteresources` templateRefs - if tier.Name == "nocluster" { - assert.Nil(t, tier.Spec.ClusterResources) - } else { - require.NotNil(t, tier.Spec.ClusterResources) - assert.Equal(t, expectedTemplateRefs[tierName]["clusterresources"], tier.Spec.ClusterResources.TemplateRef) - } + require.NotNil(t, nstmplTier.Spec.ClusterResources) + assert.Equal(t, expectedClusterResourcesTmplRef[nstmplTier.Name], nstmplTier.Spec.ClusterResources.TemplateRef) + actualNamespaceTmplRefs := []string{} + for _, ns := range nstmplTier.Spec.Namespaces { + actualNamespaceTmplRefs = append(actualNamespaceTmplRefs, ns.TemplateRef) + } + assert.ElementsMatch(t, expectedNamespaceTmplRefs[nstmplTier.Name], actualNamespaceTmplRefs) - // check the `namespaces` templateRefs - actualTemplateRefs := make([]string, len(tier.Spec.Namespaces)) - for i, ns := range tier.Spec.Namespaces { - actualTemplateRefs[i] = ns.TemplateRef - } - assert.ElementsMatch(t, expectedTemplateRefs[tierName]["namespaces"], actualTemplateRefs) + require.Len(t, nstmplTier.Spec.SpaceRoles, len(expectedSpaceRoleTmplRefs[nstmplTier.Name])) + for role, templateRef := range expectedSpaceRoleTmplRefs[nstmplTier.Name] { + assert.Equal(t, nstmplTier.Spec.SpaceRoles[role].TemplateRef, templateRef) + } - // check the `spaceRoles` templateRefs - actualSpaceRoleTmplRefs := make(map[string]string, len(tier.Spec.SpaceRoles)) - for i, r := range tier.Spec.SpaceRoles { - actualSpaceRoleTmplRefs[i] = r.TemplateRef - } - for role, tmpl := range expectedTemplateRefs[tierName]["spaceRoles"].(map[string]string) { - assert.Equal(t, tmpl, actualSpaceRoleTmplRefs[role]) - } - } - }) - }) + } t.Run("failures", func(t *testing.T) { @@ -347,7 +133,7 @@ func TestCreateOrUpdateResources(t *testing.T) { t.Run("failed to read assets", func(t *testing.T) { // given - fakeAssets := assets.NewAssets(testnstemplatetiers.AssetNames, func(name string) ([]byte, error) { + fakeAssets := assets.NewAssets(nstemplatetiers.AssetNames, func(name string) ([]byte, error) { if name == "metadata.yaml" { return []byte("advanced-code: abcdef"), nil } @@ -359,7 +145,7 @@ func TestCreateOrUpdateResources(t *testing.T) { err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, fakeAssets) // then require.Error(t, err) - assert.Equal(t, "unable to init NSTemplateTier generator: unable to load templates: an error", err.Error()) // error occurred while creating TierTemplate resources + assert.Equal(t, "unable to load templates: an error", err.Error()) // error occurred while creating TierTemplate resources }) t.Run("nstemplatetiers", func(t *testing.T) { @@ -374,12 +160,12 @@ func TestCreateOrUpdateResources(t *testing.T) { } return clt.Client.Create(ctx, obj, opts...) } - assets := assets.NewAssets(testnstemplatetiers.AssetNames, testnstemplatetiers.Asset) + assets := assets.NewAssets(nstemplatetiers.AssetNames, nstemplatetiers.Asset) // when err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, assets) // then require.Error(t, err) - assert.Regexp(t, "unable to create NSTemplateTiers: unable to create or update the '\\w+' NSTemplateTier: unable to create resource of kind: NSTemplateTier, version: v1alpha1: an error", err.Error()) + assert.Regexp(t, "unable to create or update the '\\w+' NSTemplateTier: unable to create resource of kind: NSTemplateTier, version: v1alpha1: an error", err.Error()) }) t.Run("failed to update nstemplatetiers", func(t *testing.T) { @@ -398,7 +184,7 @@ func TestCreateOrUpdateResources(t *testing.T) { } return clt.Client.Update(ctx, obj, opts...) } - testassets := assets.NewAssets(testnstemplatetiers.AssetNames, testnstemplatetiers.Asset) + testassets := assets.NewAssets(nstemplatetiers.AssetNames, nstemplatetiers.Asset) // when err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, testassets) // then @@ -419,7 +205,7 @@ func TestCreateOrUpdateResources(t *testing.T) { } return clt.Client.Create(ctx, obj, opts...) } - testassets := assets.NewAssets(testnstemplatetiers.AssetNames, testnstemplatetiers.Asset) + testassets := assets.NewAssets(nstemplatetiers.AssetNames, nstemplatetiers.Asset) // when err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, testassets) // then @@ -427,5 +213,62 @@ func TestCreateOrUpdateResources(t *testing.T) { assert.Regexp(t, fmt.Sprintf("unable to create the '\\w+-\\w+-\\w+-\\w+' TierTemplate in namespace '%s'", namespace), err.Error()) // we can't tell for sure which namespace will fail first, but the error should match the given regex }) }) + + t.Run("missing asset", func(t *testing.T) { + // given + fakeAssets := func(name string) ([]byte, error) { + if name == "metadata.yaml" { + return nstemplatetiers.Asset(name) + } + return nil, fmt.Errorf("an error occurred") + } + tmplAssets := assets.NewAssets(nstemplatetiers.AssetNames, fakeAssets) + clt := commontest.NewFakeClient(t) + + // when + err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, tmplAssets) + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "unable to load templates: an error occurred") + }) + + t.Run("missing metadata", func(t *testing.T) { + // given + fakeAssets := func(name string) ([]byte, error) { + return nil, fmt.Errorf("an error occurred") + } + tmplAssets := assets.NewAssets(nstemplatetiers.AssetNames, fakeAssets) + clt := commontest.NewFakeClient(t) + + // when + err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, tmplAssets) + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "unable to load templates: an error occurred") + }) }) } + +func verifyTierTemplate(t *testing.T, cl *commontest.FakeClient, namespace, tierName, typeName string) string { + tierTemplates := &toolchainv1alpha1.TierTemplateList{} + require.NoError(t, cl.List(context.TODO(), tierTemplates, runtimeclient.InNamespace(namespace))) + + for _, template := range tierTemplates.Items { + if template.Spec.TierName == tierName && template.Spec.Type == typeName { + splitName := strings.Split(template.Name[len(tierName)+1:], "-") + require.Len(t, splitName, 3) + assert.Equal(t, tierName, template.Name[:len(tierName)]) + assert.Equal(t, typeName, splitName[0]) + assert.Equal(t, fmt.Sprintf("%s-%s", splitName[1], splitName[2]), template.Spec.Revision) + assert.Equal(t, tierName, template.Spec.TierName) + assert.Equal(t, typeName, template.Spec.Type) + assert.NotEmpty(t, template.Spec.Template) + assert.NotEmpty(t, template.Spec.Template.Name) + return template.Name + } + } + require.Fail(t, fmt.Sprintf("the TierTemplate for NSTemplateTier '%s' and of the type '%s' wasn't found", tierName, typeName)) + return "" +} diff --git a/pkg/templates/nstemplatetiers/nstemplatetier_generator_whitebox_test.go b/pkg/templates/nstemplatetiers/nstemplatetier_generator_whitebox_test.go deleted file mode 100644 index c2777ecc7..000000000 --- a/pkg/templates/nstemplatetiers/nstemplatetier_generator_whitebox_test.go +++ /dev/null @@ -1,752 +0,0 @@ -package nstemplatetiers - -import ( - "bytes" - "fmt" - "regexp" - "strings" - "testing" - texttemplate "text/template" - - toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1" - "github.com/codeready-toolchain/host-operator/pkg/apis" - "github.com/codeready-toolchain/host-operator/pkg/templates/assets" - testnstemplatetiers "github.com/codeready-toolchain/host-operator/test/templates/nstemplatetiers" - "github.com/codeready-toolchain/toolchain-common/pkg/test" - - "github.com/ghodss/yaml" - "github.com/gofrs/uuid" - templatev1 "github.com/openshift/api/template/v1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/client-go/kubernetes/scheme" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" -) - -var expectedProdTiers = map[string]bool{ - "advanced": true, // tier_name: true/false (if based on the other tier) - "base": false, - "base1ns": false, - "base1nsnoidling": true, - "base1ns6didler": true, - "baselarge": true, - "baseextendedidling": true, - "test": false, - "appstudio": false, - "appstudiolarge": true, - "appstudio-env": false, -} - -var expectedTestTiers = map[string]bool{ - "advanced": true, // tier_name: true/false (if based on the other tier) - "base": false, - "nocluster": false, - "appstudio": false, -} - -func nsTypes(tier string) []string { - switch tier { - case "appstudio": - return []string{"tenant"} - case "appstudio-env": - return []string{"env"} - case "base1ns", "base1nsnoidling", "base1ns6didler", "test": - return []string{"dev"} - default: - return []string{"dev", "stage"} - } -} - -func roles(tier string) []string { - switch tier { - case "appstudio", "appstudio-env": - return []string{"admin", "maintainer", "contributor"} - default: - return []string{"admin"} - } -} - -func isNamespaceType(expectedTiers map[string]bool, typeName string) bool { - for _, tier := range tiers(expectedTiers) { - for _, t := range nsTypes(tier) { - if t == typeName { - return true - } - } - } - return false -} - -func isSpaceRole(expectedTiers map[string]bool, roleName string) bool { - for _, tier := range tiers(expectedTiers) { - for _, r := range roles(tier) { - if r == roleName { - return true - } - } - } - return false -} - -func tiers(expectedTiers map[string]bool) []string { - tt := make([]string, 0, len(expectedTiers)) - for tier := range expectedTiers { - tt = append(tt, tier) - } - return tt -} - -func assertKnownTier(t *testing.T, expectedTiers map[string]bool, tier string) { - assert.Contains(t, expectedTiers, tier, "encountered an unexpected tier: %s", tier) -} - -func basedOnOtherTier(expectedTiers map[string]bool, tier string) bool { - return expectedTiers[tier] -} - -func TestLoadTemplatesByTiers(t *testing.T) { - - logf.SetLogger(zap.New(zap.UseDevMode(true))) - - t.Run("ok", func(t *testing.T) { - - t.Run("with prod assets", func(t *testing.T) { - // given - assets := assets.NewAssets(AssetNames, Asset) - // when - tmpls, err := loadTemplatesByTiers(assets) - // then - require.NoError(t, err) - require.Len(t, tmpls, len(expectedProdTiers)) - require.NotContains(t, "foo", tmpls) // make sure that the `foo: bar` entry was ignored - for _, tier := range tiers(expectedProdTiers) { - t.Run(tier, func(t *testing.T) { - for _, nsTypeName := range nsTypes(tier) { - t.Run("ns-"+nsTypeName, func(t *testing.T) { - if basedOnOtherTier(expectedProdTiers, tier) { - assert.Empty(t, tmpls[tier].rawTemplates.namespaceTemplates[nsTypeName].revision) - assert.Empty(t, tmpls[tier].rawTemplates.namespaceTemplates[nsTypeName].content) - } else { - assert.NotEmpty(t, tmpls[tier].rawTemplates.namespaceTemplates[nsTypeName].revision) - assert.NotEmpty(t, tmpls[tier].rawTemplates.namespaceTemplates[nsTypeName].content) - } - }) - } - for _, role := range roles(tier) { - t.Run("spacerole-"+role, func(t *testing.T) { - if basedOnOtherTier(expectedProdTiers, tier) { - assert.Empty(t, tmpls[tier].rawTemplates.spaceroleTemplates[role].revision) - assert.Empty(t, tmpls[tier].rawTemplates.spaceroleTemplates[role].content) - } else { - assert.NotEmpty(t, tmpls[tier].rawTemplates.spaceroleTemplates[role].revision) - assert.NotEmpty(t, tmpls[tier].rawTemplates.spaceroleTemplates[role].content) - } - }) - } - t.Run("clusterresources", func(t *testing.T) { - if basedOnOtherTier(expectedProdTiers, tier) { - assert.Nil(t, tmpls[tier].rawTemplates.clusterTemplate) - } else { - require.NotNil(t, tmpls[tier].rawTemplates.clusterTemplate) - assert.NotEmpty(t, tmpls[tier].rawTemplates.clusterTemplate.revision) - assert.NotEmpty(t, tmpls[tier].rawTemplates.clusterTemplate.content) - } - }) - t.Run("based_on_tier", func(t *testing.T) { - if basedOnOtherTier(expectedProdTiers, tier) { - require.NotNil(t, tmpls[tier].basedOnTier) - } else { - require.Nil(t, tmpls[tier].basedOnTier) - } - }) - }) - } - }) - - t.Run("with test assets", func(t *testing.T) { - // given - assets := assets.NewAssets(testnstemplatetiers.AssetNames, testnstemplatetiers.Asset) - // when - tmpls, err := loadTemplatesByTiers(assets) - // then - require.NoError(t, err) - require.Len(t, tmpls, 4) - require.NotContains(t, "foo", tmpls) // make sure that the `foo: bar` entry was ignored - - for _, tier := range tiers(expectedTestTiers) { - t.Run(tier, func(t *testing.T) { - for _, typeName := range nsTypes(tier) { - t.Run("ns-"+typeName, func(t *testing.T) { - require.NotNil(t, tmpls[tier]) - if basedOnOtherTier(expectedTestTiers, tier) { - assert.Empty(t, tmpls[tier].rawTemplates.namespaceTemplates[typeName].revision) - assert.Empty(t, tmpls[tier].rawTemplates.namespaceTemplates[typeName].content) - } else { - assert.NotEmpty(t, tmpls[tier].rawTemplates.namespaceTemplates[typeName].revision) - assert.NotEmpty(t, tmpls[tier].rawTemplates.namespaceTemplates[typeName].content) - } - }) - } - for _, role := range roles(tier) { - t.Run("spacerole-"+role, func(t *testing.T) { - if basedOnOtherTier(expectedProdTiers, tier) { - assert.Empty(t, tmpls[tier].rawTemplates.spaceroleTemplates[role].revision) - assert.Empty(t, tmpls[tier].rawTemplates.spaceroleTemplates[role].content) - } else { - assert.NotEmpty(t, tmpls[tier].rawTemplates.spaceroleTemplates[role].revision) - assert.NotEmpty(t, tmpls[tier].rawTemplates.spaceroleTemplates[role].content) - } - }) - } - t.Run("cluster", func(t *testing.T) { - require.NotNil(t, tmpls[tier].rawTemplates) - if basedOnOtherTier(expectedTestTiers, tier) { - assert.Nil(t, tmpls[tier].rawTemplates.clusterTemplate) - } else if tier != "nocluster" { - require.NotNil(t, tmpls[tier].rawTemplates.clusterTemplate) - assert.NotEmpty(t, tmpls[tier].rawTemplates.clusterTemplate.revision) - assert.NotEmpty(t, tmpls[tier].rawTemplates.clusterTemplate.content) - } else { - require.Nil(t, tmpls[tier].rawTemplates.clusterTemplate) - } - }) - t.Run("based_on_tier", func(t *testing.T) { - if basedOnOtherTier(expectedTestTiers, tier) { - require.NotNil(t, tmpls[tier].basedOnTier) - } else { - require.Nil(t, tmpls[tier].basedOnTier) - } - }) - }) - } - }) - }) - - t.Run("failures", func(t *testing.T) { - - t.Run("unparseable content", func(t *testing.T) { - // given - fakeAssets := func(name string) ([]byte, error) { - return []byte("foo::bar"), nil - } - assets := assets.NewAssets(testnstemplatetiers.AssetNames, fakeAssets) - // when - _, err := loadTemplatesByTiers(assets) - // then - require.Error(t, err) - assert.Contains(t, err.Error(), "unable to load templates: yaml: unmarshal errors:") - }) - - t.Run("missing metadata", func(t *testing.T) { - // given - fakeAssets := func(name string) ([]byte, error) { - return nil, fmt.Errorf("an error occurred") - } - assets := assets.NewAssets(testnstemplatetiers.AssetNames, fakeAssets) - // when - _, err := loadTemplatesByTiers(assets) - // then - require.Error(t, err) - assert.Contains(t, err.Error(), "unable to load templates: an error occurred") - }) - - t.Run("missing asset", func(t *testing.T) { - // given - fakeAssets := func(name string) ([]byte, error) { - if name == "metadata.yaml" { - return testnstemplatetiers.Asset(name) - } - return nil, fmt.Errorf("an error occurred") - } - assets := assets.NewAssets(testnstemplatetiers.AssetNames, fakeAssets) - // when - _, err := loadTemplatesByTiers(assets) - // then - require.Error(t, err) - assert.Contains(t, err.Error(), "unable to load templates: an error occurred") - }) - - t.Run("invalid name format", func(t *testing.T) { - // given - fakeAssetNames := func() []string { - return []string{`.DS_Store`, `metadata.yaml`} // '/advanced/foo.yaml' is not a valid filename - } - fakeAssets := func(name string) ([]byte, error) { - switch name { - case "metadata.yaml": - return []byte(`advanced/foo.yaml: "123456a"`), nil // just make sure the asset exists - case ".DS_Store": - return []byte(`foo:bar`), nil // just make sure the asset exists - default: - return testnstemplatetiers.Asset(name) - } - } - assets := assets.NewAssets(fakeAssetNames, fakeAssets) - // when - _, err := loadTemplatesByTiers(assets) - // then - require.Error(t, err) - assert.EqualError(t, err, "unable to load templates: invalid name format for file '.DS_Store'") - }) - - t.Run("invalid filename scope", func(t *testing.T) { - // given - fakeAssetNames := func() []string { - return []string{`metadata.yaml`, `advanced/foo.yaml`} // '/advanced/foo.yaml' is not a valid filename - } - fakeAssets := func(name string) ([]byte, error) { - switch name { - case "metadata.yaml": - return []byte(`advanced/foo.yaml: "123456a"`), nil // just make sure the asset exists - case "advanced/foo.yaml": - return []byte(`foo:bar`), nil // just make sure the asset exists - default: - return testnstemplatetiers.Asset(name) - } - } - assets := assets.NewAssets(fakeAssetNames, fakeAssets) - // when - _, err := loadTemplatesByTiers(assets) - // then - require.Error(t, err) - assert.Contains(t, err.Error(), "unable to load templates: unknown scope for file 'advanced/foo.yaml'") - }) - - t.Run("should fail when tier contains a mix of based_on_tier.yaml file together with a regular template file", func(t *testing.T) { - // given - s := scheme.Scheme - err := apis.AddToScheme(s) - require.NoError(t, err) - clt := test.NewFakeClient(t) - - for _, tmplName := range []string{"cluster.yaml", "ns_dev.yaml", "ns_stage.yaml", "tier.yaml"} { - t.Run("for template name "+tmplName, func(t *testing.T) { - // given - testassets := assets.NewAssets(func() []string { - return append(testnstemplatetiers.AssetNames(), fmt.Sprintf("advanced/%s", tmplName)) - - }, func(path string) (bytes []byte, e error) { - if path == "advanced/"+tmplName { - return []byte(""), nil - } - return testnstemplatetiers.Asset(path) - }) - - // when - _, err := newNSTemplateTierGenerator(s, clt, test.HostOperatorNs, testassets) - - // then - require.EqualError(t, err, "the tier advanced contains a mix of based_on_tier.yaml file together with a regular template file") - }) - } - }) - }) -} - -func TestNewNSTemplateTier(t *testing.T) { - - s := scheme.Scheme - err := apis.AddToScheme(s) - require.NoError(t, err) - - t.Run("ok", func(t *testing.T) { - - t.Run("with prod assets", func(t *testing.T) { - // given - namespace := "host-operator-" + uuid.Must(uuid.NewV4()).String()[:7] - assets := assets.NewAssets(AssetNames, Asset) - - // when - // uses the `Asset` funcs generated in the `pkg/templates/nstemplatetiers/` subpackages - tc, err := newNSTemplateTierGenerator(s, nil, namespace, assets) - require.NoError(t, err) - // then - require.NotEmpty(t, tc.templatesByTier) - // verify that each NSTemplateTier has the ClusterResources, Namespaces and SpaceRoles `TemplateRef` set as expected - for _, tierData := range tc.templatesByTier { - - for _, nstmplTierObj := range tierData.objects { - // verify tier configuration - nstmplTier := runtimeObjectToNSTemplateTier(t, s, nstmplTierObj) - require.NotNil(t, nstmplTier) - assert.Equal(t, namespace, nstmplTier.Namespace) - - assertKnownTier(t, expectedProdTiers, nstmplTier.Name) - - // verify tier templates - var expectedClusterResourcesTmplRef string - expectedNamespaceTmplRefs := []string{} - expectedSpaceRoleTmplRefs := []string{} - for _, tierTmpl := range tierData.tierTemplates { - switch { - case strings.Contains(tierTmpl.Name, "clusterresources"): - expectedClusterResourcesTmplRef = tierTmpl.Name - case strings.Contains(tierTmpl.Name, "admin") || strings.Contains(tierTmpl.Name, "maintainer") || strings.Contains(tierTmpl.Name, "contributor"): - expectedSpaceRoleTmplRefs = append(expectedSpaceRoleTmplRefs, tierTmpl.Name) - default: - expectedNamespaceTmplRefs = append(expectedNamespaceTmplRefs, tierTmpl.Name) - } - } - require.NotNil(t, nstmplTier.Spec.ClusterResources) - assert.Equal(t, expectedClusterResourcesTmplRef, nstmplTier.Spec.ClusterResources.TemplateRef) - actualNamespaceTmplRefs := []string{} - for _, ns := range nstmplTier.Spec.Namespaces { - actualNamespaceTmplRefs = append(actualNamespaceTmplRefs, ns.TemplateRef) - } - assert.ElementsMatch(t, expectedNamespaceTmplRefs, actualNamespaceTmplRefs) - actualSpaceRoleTmplRefs := []string{} - for _, role := range nstmplTier.Spec.SpaceRoles { - actualSpaceRoleTmplRefs = append(actualSpaceRoleTmplRefs, role.TemplateRef) - } - assert.ElementsMatch(t, expectedSpaceRoleTmplRefs, actualSpaceRoleTmplRefs) - - } - } - }) - - t.Run("with test assets", func(t *testing.T) { - // given - namespace := "host-operator-" + uuid.Must(uuid.NewV4()).String()[:7] - assets := assets.NewAssets(testnstemplatetiers.AssetNames, testnstemplatetiers.Asset) - tc, err := newNSTemplateTierGenerator(s, nil, namespace, assets) - require.NoError(t, err) - clusterResourcesRevisions := map[string]string{ - "advanced": "abcd123-654321a", - "base": "654321a-654321a", - "appstudio": "654321a-654321a", - } - namespaceRevisions := map[string]map[string]string{ - "advanced": { - "dev": "abcd123-123456b", - "stage": "abcd123-123456c", - }, - "base": { - "dev": "123456b-123456b", - "stage": "123456c-123456c", - }, - "nocluster": { - "dev": "123456j-123456j", - "stage": "1234567-1234567", - }, - "appstudio": { - "tenant": "123456b-123456b", - }, - } - spaceRoleRevisions := map[string]map[string]string{ - "advanced": { - "admin": "abcd123-123456d", - }, - "base": { - "admin": "123456d-123456d", - }, - "nocluster": { - "admin": "123456k-123456k", - }, - "appstudio": { - "admin": "123456c-123456c", - "maintainer": "123456d-123456d", - "contributor": "123456e-123456e", - }, - } - for tier := range namespaceRevisions { - t.Run(tier, func(t *testing.T) { - // given - objects := tc.templatesByTier[tier].objects - require.Len(t, objects, 1, "expected only 1 NSTemplateTier toolchain object") - // when - actual := runtimeObjectToNSTemplateTier(t, s, objects[0]) - - // then - expected, err := newNSTemplateTierFromYAML(s, tier, namespace, clusterResourcesRevisions[tier], namespaceRevisions[tier], spaceRoleRevisions[tier]) - require.NoError(t, err) - // here we don't compare objects because the generated NSTemplateTier - // has no specific values for the `TypeMeta`: the `APIVersion: toolchain.dev.openshift.com/v1alpha1` - // and `Kind: NSTemplateTier` should be set by the client using the registered GVK - assert.Equal(t, expected.ObjectMeta, actual.ObjectMeta) - assert.Equal(t, expected.Spec, actual.Spec) - }) - } - }) - }) -} - -func TestNewTierTemplate(t *testing.T) { - - s := scheme.Scheme - err := apis.AddToScheme(s) - require.NoError(t, err) - namespace := "host-operator-" + uuid.Must(uuid.NewV4()).String()[:7] - - t.Run("ok", func(t *testing.T) { - - t.Run("with prod assets", func(t *testing.T) { - // given - assets := assets.NewAssets(AssetNames, Asset) - // uses the `Asset` funcs generated in the `pkg/templates/nstemplatetiers/` subpackages - // when - tc, err := newNSTemplateTierGenerator(s, nil, namespace, assets) - - // then - require.NoError(t, err) - decoder := serializer.NewCodecFactory(s).UniversalDeserializer() - - resourceNameRE, err := regexp.Compile(`[a-z0-9\.-]+`) - require.NoError(t, err) - for tier, tmpls := range tc.templatesByTier { - t.Run(tier, func(t *testing.T) { - for _, actual := range tmpls.tierTemplates { - t.Run(actual.Name, func(t *testing.T) { - assert.Equal(t, namespace, actual.Namespace) - assert.True(t, resourceNameRE.MatchString(actual.Name)) // verifies that the TierTemplate name complies with the DNS-1123 spec - assert.NotEmpty(t, actual.Spec.Revision) - assert.Equal(t, tier, actual.Spec.TierName) - assert.NotEmpty(t, actual.Spec.Type) - assert.NotEmpty(t, actual.Spec.Template) - assert.NotEmpty(t, actual.Spec.Template.Name) - if actual.Spec.Type == "clusterresources" { - assertClusterResourcesTemplate(t, decoder, actual.Spec.Template, assets, expectedProdTiers, tier) - } else if isNamespaceType(expectedProdTiers, actual.Spec.Type) { - assertNamespaceTemplate(t, decoder, actual.Spec.Template, assets, expectedProdTiers, tier, actual.Spec.Type) - } else if isSpaceRole(expectedProdTiers, actual.Spec.Type) { - assertSpaceRoleTemplate(t, decoder, actual.Spec.Template, assets, expectedProdTiers, tier, actual.Spec.Type) - } else { - t.Errorf("unexpected type of template: '%s'", actual.Spec.Type) - } - }) - } - }) - } - }) - - t.Run("with test assets", func(t *testing.T) { - // given - assets := assets.NewAssets(testnstemplatetiers.AssetNames, testnstemplatetiers.Asset) - // when - tc, err := newNSTemplateTierGenerator(s, nil, namespace, assets) - - // then - require.NoError(t, err) - decoder := serializer.NewCodecFactory(s).UniversalDeserializer() - - resourceNameRE, err := regexp.Compile(`[a-z0-9\.-]+`) - require.NoError(t, err) - for tier, tmpls := range tc.templatesByTier { - t.Run(tier, func(t *testing.T) { - for _, actual := range tmpls.tierTemplates { - assert.Equal(t, namespace, actual.Namespace) - assert.True(t, resourceNameRE.MatchString(actual.Name)) // verifies that the TierTemplate name complies with the DNS-1123 spec - assert.NotEmpty(t, actual.Spec.Revision) - assert.NotEmpty(t, actual.Spec.TierName) - assert.NotEmpty(t, actual.Spec.Type) - assert.NotEmpty(t, actual.Spec.Template) - assert.NotEmpty(t, actual.Spec.Template.Name) - - if actual.Spec.Type == "clusterresources" { - assertClusterResourcesTemplate(t, decoder, actual.Spec.Template, assets, expectedTestTiers, tier) - } else if isNamespaceType(expectedTestTiers, actual.Spec.Type) { - assertNamespaceTemplate(t, decoder, actual.Spec.Template, assets, expectedTestTiers, tier, actual.Spec.Type) - } else if isSpaceRole(expectedTestTiers, actual.Spec.Type) { - assertSpaceRoleTemplate(t, decoder, actual.Spec.Template, assets, expectedTestTiers, tier, actual.Spec.Type) - } else { - t.Errorf("unexpected type of template: '%s'", actual.Spec.Type) - } - } - }) - } - }) - }) - - t.Run("failures", func(t *testing.T) { - - t.Run("invalid template", func(t *testing.T) { - // given - fakeAssets := assets.NewAssets(testnstemplatetiers.AssetNames, func(name string) ([]byte, error) { - if name == "metadata.yaml" || strings.HasSuffix(name, "based_on_tier.yaml") { - return testnstemplatetiers.Asset(name) - } - // error occurs when fetching the content of the 'advanced-code.yaml' template - return []byte("invalid"), nil // return an invalid YAML representation of a Template - }) - // when - _, err := newNSTemplateTierGenerator(s, nil, namespace, fakeAssets) - - // then - require.Error(t, err) - assert.Contains(t, err.Error(), "unable to generate 'advanced-dev-abcd123-123456b' TierTemplate manifest: couldn't get version/kind; json parse error") - }) - }) -} - -func assertClusterResourcesTemplate(t *testing.T, decoder runtime.Decoder, actual templatev1.Template, assets assets.Assets, expectedTiers map[string]bool, tier string) { - if !basedOnOtherTier(expectedTiers, tier) { - expected := templatev1.Template{} - content, err := assets.Asset(fmt.Sprintf("%s/cluster.yaml", tier)) - require.NoError(t, err) - _, _, err = decoder.Decode(content, nil, &expected) - require.NoError(t, err) - assert.Equal(t, expected, actual) - assert.NotEmpty(t, actual.Objects) - } -} - -func assertNamespaceTemplate(t *testing.T, decoder runtime.Decoder, actual templatev1.Template, assets assets.Assets, expectedTiers map[string]bool, tier, typeName string) { - var templatePath string - if basedOnOtherTier(expectedTiers, tier) { - templatePath = expectedTemplateFromBasedOnTierConfig(t, assets, tier, fmt.Sprintf("ns_%s.yaml", typeName)) - } else { - templatePath = fmt.Sprintf("%s/ns_%s.yaml", tier, typeName) - } - content, err := assets.Asset(templatePath) - require.NoError(t, err) - expected := templatev1.Template{} - _, _, err = decoder.Decode(content, nil, &expected) - require.NoError(t, err) - assert.Equal(t, expected, actual) - assert.NotEmpty(t, actual.Objects) -} - -func assertSpaceRoleTemplate(t *testing.T, decoder runtime.Decoder, actual templatev1.Template, assets assets.Assets, expectedTiers map[string]bool, tier, roleName string) { - var templatePath string - if basedOnOtherTier(expectedTiers, tier) { - templatePath = expectedTemplateFromBasedOnTierConfig(t, assets, tier, fmt.Sprintf("spacerole_%s.yaml", roleName)) - } else { - templatePath = fmt.Sprintf("%s/spacerole_%s.yaml", tier, roleName) - } - content, err := assets.Asset(templatePath) - require.NoError(t, err) - expected := templatev1.Template{} - _, _, err = decoder.Decode(content, nil, &expected) - require.NoError(t, err) - assert.Equal(t, expected, actual) - // there are no space role permissions for appstudio-env because the user doesn't have any permissions in the namespace - if tier != "appstudio-env" { - assert.NotEmpty(t, actual.Objects) - } else { - assert.Empty(t, actual.Objects) - } -} - -func expectedTemplateFromBasedOnTierConfig(t *testing.T, assets assets.Assets, tier, templateFileName string) string { - basedOnTierContent, err := assets.Asset(fmt.Sprintf("%s/based_on_tier.yaml", tier)) - require.NoError(t, err) - basedOnTier := BasedOnTier{} - require.NoError(t, yaml.Unmarshal(basedOnTierContent, &basedOnTier)) - return fmt.Sprintf("%s/%s", basedOnTier.From, templateFileName) -} - -func TestNewNSTemplateTiers(t *testing.T) { - - // given - s := scheme.Scheme - err := apis.AddToScheme(s) - require.NoError(t, err) - - t.Run("ok", func(t *testing.T) { - // given - namespace := "host-operator-" + uuid.Must(uuid.NewV4()).String()[:7] - assets := assets.NewAssets(testnstemplatetiers.AssetNames, testnstemplatetiers.Asset) - // when - tc, err := newNSTemplateTierGenerator(s, nil, namespace, assets) - require.NoError(t, err) - // then - require.Len(t, tc.templatesByTier, 4) - for _, name := range []string{"advanced", "base", "nocluster", "appstudio"} { - tierData, found := tc.templatesByTier[name] - tierObjs := tierData.objects - require.Len(t, tierObjs, 1, "expected only 1 NSTemplateTier toolchain object") - tier := runtimeObjectToNSTemplateTier(t, s, tierObjs[0]) - - require.True(t, found) - assert.Equal(t, name, tier.ObjectMeta.Name) - assert.Equal(t, namespace, tier.ObjectMeta.Namespace) - for _, ns := range tier.Spec.Namespaces { - assert.NotEmpty(t, ns.TemplateRef, "expected namespace reference not empty for tier %v", name) - } - if name == "nocluster" { - assert.Nil(t, tier.Spec.ClusterResources) - } else { - require.NotNil(t, tier.Spec.ClusterResources) - assert.NotEmpty(t, tier.Spec.ClusterResources.TemplateRef) - } - } - }) -} - -var ExpectedRevisions = map[string]map[string]string{ - "advanced": { - "based_on_tier": "abcd123", - }, - "base": { - "dev": "123456b", - "stage": "123456c", - "cluster": "654321a", - }, - "nocluster": { - "dev": "123456j", - "stage": "1234567", - }, -} - -// newNSTemplateTierFromYAML generates toolchainv1alpha1.NSTemplateTier using a golang template which is applied to the given tier. -func newNSTemplateTierFromYAML(s *runtime.Scheme, tier, namespace string, clusterResourcesRevision string, namespaceRevisions map[string]string, spaceRoleRevisions map[string]string) (*toolchainv1alpha1.NSTemplateTier, error) { - expectedTmpl, err := texttemplate.New("template").Parse(` -{{ $tier := .Tier}} -kind: NSTemplateTier -apiVersion: toolchain.dev.openshift.com/v1alpha1 -metadata: - namespace: {{ .Namespace }} - name: {{ .Tier }} -spec: - deactivationTimeoutDays: {{ .DeactivationTimeout }} - {{ if .ClusterResourcesRevision }}clusterResources: - templateRef: {{ .Tier }}-clusterresources-{{ .ClusterResourcesRevision }} - {{ end }} - namespaces: -{{ range $type, $revision := .NamespaceRevisions }} - - templateRef: {{ $tier }}-{{ $type }}-{{ $revision }} -{{ end }} - spaceRoles: -{{ range $role, $revision := .SpaceRoleRevisions }} - {{ $role }}: - templateRef: {{ $tier }}-{{ $role }}-{{ $revision }} -{{ end }} -`) - if err != nil { - return nil, err - } - buf := bytes.NewBuffer(nil) - err = expectedTmpl.Execute(buf, struct { - Tier string - Namespace string - ClusterResourcesRevision string - NamespaceRevisions map[string]string - SpaceRoleRevisions map[string]string - DeactivationTimeout int - }{ - Tier: tier, - Namespace: namespace, - ClusterResourcesRevision: clusterResourcesRevision, - NamespaceRevisions: namespaceRevisions, - SpaceRoleRevisions: spaceRoleRevisions, - }) - if err != nil { - return nil, err - } - result := &toolchainv1alpha1.NSTemplateTier{} - codecFactory := serializer.NewCodecFactory(s) - _, _, err = codecFactory.UniversalDeserializer().Decode(buf.Bytes(), nil, result) - if err != nil { - return nil, err - } - return result, nil -} - -func runtimeObjectToNSTemplateTier(t *testing.T, s *runtime.Scheme, tierObj runtime.Object) *toolchainv1alpha1.NSTemplateTier { - tier := &toolchainv1alpha1.NSTemplateTier{} - err := s.Convert(tierObj, tier, nil) - require.NoError(t, err) - return tier -} diff --git a/test/nstemplatetier/nstemplatetier.go b/test/nstemplatetier/nstemplatetier.go index a5fdf4441..3bdc80cae 100644 --- a/test/nstemplatetier/nstemplatetier.go +++ b/test/nstemplatetier/nstemplatetier.go @@ -5,9 +5,9 @@ import ( "testing" toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1" - "github.com/codeready-toolchain/host-operator/pkg/templates/nstemplatetiers" "github.com/codeready-toolchain/toolchain-common/pkg/hash" "github.com/codeready-toolchain/toolchain-common/pkg/test" + "github.com/codeready-toolchain/toolchain-common/pkg/test/nstemplateset" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -18,7 +18,7 @@ func NewNSTemplateTier(tierName string, nsTypes ...string) *toolchainv1alpha1.NS for i, nsType := range nsTypes { revision := fmt.Sprintf("123abc%d", i+1) namespaces[i] = toolchainv1alpha1.NSTemplateTierNamespace{ - TemplateRef: nstemplatetiers.NewTierTemplateName(tierName, nsType, revision), + TemplateRef: nstemplateset.NewTierTemplateName(tierName, nsType, revision), } } @@ -30,7 +30,7 @@ func NewNSTemplateTier(tierName string, nsTypes ...string) *toolchainv1alpha1.NS Spec: toolchainv1alpha1.NSTemplateTierSpec{ Namespaces: namespaces, ClusterResources: &toolchainv1alpha1.NSTemplateTierClusterResources{ - TemplateRef: nstemplatetiers.NewTierTemplateName(tierName, "clusterresources", "654321b"), + TemplateRef: nstemplateset.NewTierTemplateName(tierName, "clusterresources", "654321b"), }, SpaceRoles: map[string]toolchainv1alpha1.NSTemplateTierSpaceRole{ "admin": { diff --git a/test/templates/nstemplatetiers/advanced/based_on_tier.yaml b/test/templates/nstemplatetiers/advanced/based_on_tier.yaml deleted file mode 100644 index 4504989ec..000000000 --- a/test/templates/nstemplatetiers/advanced/based_on_tier.yaml +++ /dev/null @@ -1,5 +0,0 @@ -from: base -parameters: -- name: IDLER_TIMEOUT_SECONDS - # 144 hours - value: "518400" diff --git a/test/templates/nstemplatetiers/appstudio/cluster.yaml b/test/templates/nstemplatetiers/appstudio/cluster.yaml deleted file mode 100644 index 35a655623..000000000 --- a/test/templates/nstemplatetiers/appstudio/cluster.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: appstudio-cluster-resources -objects: -- apiVersion: quota.openshift.io/v1 - kind: ClusterResourceQuota - metadata: - name: for-${SPACE_NAME}-compute - spec: - quota: - hard: - limits.cpu: 20000m - limits.memory: ${MEMORY_LIMIT} - limits.ephemeral-storage: 7Gi - requests.cpu: 1750m - requests.memory: ${MEMORY_REQUEST} - requests.storage: 15Gi - requests.ephemeral-storage: 7Gi - count/persistentvolumeclaims: "5" - selector: - annotations: null - labels: - matchLabels: - toolchain.dev.openshift.com/space: ${SPACE_NAME} -parameters: -- name: SPACE_NAME - required: true -- name: IDLER_TIMEOUT_SECONDS - # 12 hours - value: "43200" -- name: MEMORY_LIMIT - value: "7Gi" -- name: MEMORY_REQUEST - value: "7Gi" diff --git a/test/templates/nstemplatetiers/appstudio/ns_tenant.yaml b/test/templates/nstemplatetiers/appstudio/ns_tenant.yaml deleted file mode 100644 index b265e395f..000000000 --- a/test/templates/nstemplatetiers/appstudio/ns_tenant.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: appstudio-tenant -objects: -- apiVersion: v1 - kind: Namespace - metadata: - annotations: - openshift.io/description: ${SPACE_NAME} - openshift.io/display-name: ${SPACE_NAME} - openshift.io/requester: ${SPACE_NAME} - labels: - name: ${SPACE_NAME}-tenant - appstudio.redhat.com/workspace_name: ${SPACE_NAME} - name: ${SPACE_NAME}-tenant - -parameters: -- name: SPACE_NAME - required: true -- name: MEMBER_OPERATOR_NAMESPACE - value: toolchain-member-operator \ No newline at end of file diff --git a/test/templates/nstemplatetiers/appstudio/spacerole_admin.yaml b/test/templates/nstemplatetiers/appstudio/spacerole_admin.yaml deleted file mode 100644 index 7006ee6e1..000000000 --- a/test/templates/nstemplatetiers/appstudio/spacerole_admin.yaml +++ /dev/null @@ -1,159 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: appstudio-spacerole-admin -objects: - -# ServiceAccounts that represents the AppStudio user - the token of this SA is used by the proxy for forwarding the requests from UI (or any other client) -- apiVersion: v1 - kind: ServiceAccount - metadata: - namespace: ${NAMESPACE} - name: appstudio-${USERNAME} - -# RoleBinding that grants limited CRUD permissions on AppStudio components CRDs & secrets to the user's SA -# Role(s) and RoleBinding(s) that grant limited CRUD permissions on AppStudio components CRDs & secrets to the user's SA -- apiVersion: rbac.authorization.k8s.io/v1 - kind: Role - metadata: - namespace: ${NAMESPACE} - name: appstudio-admin-user-actions - rules: - - apiGroups: - - appstudio.redhat.com - resources: - - applications - - components - - componentdetectionqueries - verbs: - - "*" - - apiGroups: - - appstudio.redhat.com - resources: - - spiaccesstokenbindings - - spiaccesschecks - - spiaccesstokens - - spifilecontentrequests - - spiaccesstokendataupdates - verbs: - - '*' - - apiGroups: - - appstudio.redhat.com - resources: - - promotionruns - - snapshotenvironmentbindings - - snapshots - - environments - verbs: - - '*' - - apiGroups: - - appstudio.redhat.com - resources: - - deploymentttargets - - deploymenttargetclaims - verbs: - - '*' - - apiGroups: - - managed-gitops.redhat.com - resources: - - gitopsdeployments - - gitopsdeploymentmanagedenvironments - - gitopsdeploymentrepositorycredentials - - gitopsdeploymentsyncruns - verbs: - - get - - list - - watch - - apiGroups: - - tekton.dev - resources: - - pipelineruns - verbs: - - '*' - - apiGroups: - - results.tekton.dev - resources: - - results - - records - verbs: - - get - - list - - apiGroups: - - appstudio.redhat.com - resources: - - integrationtestscenarios - verbs: - - '*' - - apiGroups: - - appstudio.redhat.com - resources: - - enterprisecontractpolicies - verbs: - - '*' - - apiGroups: - - appstudio.redhat.com - resources: - - releases - - releasestrategies - - releaseplans - verbs: - - '*' - - apiGroups: - - appstudio.redhat.com - resources: - - releaseplanadmissions - verbs: - - '*' - - apiGroups: - - jvmbuildservice.io - resources: - - jbsconfigs - - artifactbuilds - verbs: - - '*' - - apiGroups: - - '' - resources: - - configmaps - verbs: - - '*' - - apiGroups: - - '' - resources: - - secrets - verbs: - - '*' -- apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - namespace: ${NAMESPACE} - name: appstudio-${USERNAME}-user-actions - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: appstudio-admin-user-actions - subjects: - - apiGroup: "" - kind: ServiceAccount - name: appstudio-${USERNAME} - -# Role & RoleBinding that grants view permissions to the user's SA -- apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - namespace: ${NAMESPACE} - name: appstudio-${USERNAME}-view - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: view - subjects: - - apiGroup: "" - kind: ServiceAccount - name: appstudio-${USERNAME} - -parameters: -- name: USERNAME - required: true -- name: NAMESPACE - required: true diff --git a/test/templates/nstemplatetiers/appstudio/spacerole_contributor.yaml b/test/templates/nstemplatetiers/appstudio/spacerole_contributor.yaml deleted file mode 100644 index 3045bbcf5..000000000 --- a/test/templates/nstemplatetiers/appstudio/spacerole_contributor.yaml +++ /dev/null @@ -1,171 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: appstudio-spacerole-contributor # name is used in e2e tests -objects: - # ServiceAccounts that represents the AppStudio user - the token of this SA is used by the proxy for forwarding the requests from UI (or any other client) - - apiVersion: v1 - kind: ServiceAccount - metadata: - namespace: ${NAMESPACE} - name: appstudio-${USERNAME} - - # RoleBinding that grants limited CRUD permissions on AppStudio components CRDs & secrets to the user's SA - # Role(s) and RoleBinding(s) that grant limited CRUD permissions on AppStudio components CRDs & secrets to the user's SA - - apiVersion: rbac.authorization.k8s.io/v1 - kind: Role - metadata: - namespace: ${NAMESPACE} - name: appstudio-contributor-user-actions - rules: - - apiGroups: - - appstudio.redhat.com - resources: - - applications - - components - - componentdetectionqueries - verbs: - - get - - list - - watch - - apiGroups: - - appstudio.redhat.com - resources: - - promotionruns - - snapshotenvironmentbindings - - snapshots - - environments - verbs: - - get - - list - - watch - - apiGroups: - - appstudio.redhat.com - resources: - - deploymentttargets - - deploymenttargetclaims - verbs: - - get - - list - - watch - - apiGroups: - - managed-gitops.redhat.com - resources: - - gitopsdeployments - - gitopsdeploymentmanagedenvironments - - gitopsdeploymentrepositorycredentials - - gitopsdeploymentsyncruns - verbs: - - get - - list - - watch - - apiGroups: - - tekton.dev - resources: - - pipelineruns - verbs: - - get - - list - - watch - - apiGroups: - - results.tekton.dev - resources: - - results - - records - verbs: - - get - - list - - apiGroups: - - appstudio.redhat.com - resources: - - integrationtestscenarios - verbs: - - get - - list - - watch - - apiGroups: - - appstudio.redhat.com - resources: - - enterprisecontractpolicies - verbs: - - get - - list - - watch - - apiGroups: - - appstudio.redhat.com - resources: - - releases - - releasestrategies - - releaseplans - verbs: - - get - - list - - watch - - apiGroups: - - appstudio.redhat.com - resources: - - releaseplanadmissions - verbs: - - get - - list - - watch - - apiGroups: - - jvmbuildservice.io - resources: - - jbsconfigs - - artifactbuilds - verbs: - - get - - list - - watch - - apiGroups: - - appstudio.redhat.com - resources: - - spiaccesstokenbindings - - spiaccesschecks - - spiaccesstokens - - spifilecontentrequest - verbs: - - get - - list - - watch - - apiGroups: - - '' - resources: - - configmaps - verbs: - - get - - list - - watch - - apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - namespace: ${NAMESPACE} - name: appstudio-${USERNAME}-user-actions - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: appstudio-contributor-user-actions - subjects: - - apiGroup: "" - kind: ServiceAccount - name: appstudio-${USERNAME} - # Role & RoleBinding that grants view permissions to the user's SA - - apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - namespace: ${NAMESPACE} - name: appstudio-${USERNAME}-view-user - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: view - subjects: - - kind: User - name: ${USERNAME} - -parameters: - - name: NAMESPACE - required: true - - name: USERNAME - required: true \ No newline at end of file diff --git a/test/templates/nstemplatetiers/appstudio/spacerole_maintainer.yaml b/test/templates/nstemplatetiers/appstudio/spacerole_maintainer.yaml deleted file mode 100644 index 08846aad8..000000000 --- a/test/templates/nstemplatetiers/appstudio/spacerole_maintainer.yaml +++ /dev/null @@ -1,177 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: appstudio-spacerole-maintainer -objects: - -# ServiceAccounts that represents the AppStudio user - the token of this SA is used by the proxy for forwarding the requests from UI (or any other client) -- apiVersion: v1 - kind: ServiceAccount - metadata: - namespace: ${NAMESPACE} - name: appstudio-${USERNAME} - -# RoleBinding that grants limited CRUD permissions on AppStudio components CRDs & secrets to the user's SA -# Role(s) and RoleBinding(s) that grant limited CRUD permissions on AppStudio components CRDs & secrets to the user's SA -- apiVersion: rbac.authorization.k8s.io/v1 - kind: Role - metadata: - namespace: ${NAMESPACE} - name: appstudio-maintainer-user-actions - rules: - - apiGroups: - - appstudio.redhat.com - resources: - - applications - - components - - componentdetectionqueries - verbs: - - get - - list - - watch - - create - - update - - patch - - apiGroups: - - appstudio.redhat.com - resources: - - promotionruns - - snapshotenvironmentbindings - - snapshots - - environments - verbs: - - get - - list - - watch - - apiGroups: - - appstudio.redhat.com - resources: - - deploymentttargets - - deploymenttargetclaims - verbs: - - get - - list - - watch - - apiGroups: - - managed-gitops.redhat.com - resources: - - gitopsdeployments - - gitopsdeploymentmanagedenvironments - - gitopsdeploymentrepositorycredentials - - gitopsdeploymentsyncruns - verbs: - - get - - list - - watch - - apiGroups: - - tekton.dev - resources: - - pipelineruns - verbs: - - get - - list - - watch - - apiGroups: - - results.tekton.dev - resources: - - results - - records - verbs: - - get - - list - - apiGroups: - - appstudio.redhat.com - resources: - - integrationtestscenarios - verbs: - - '*' - - apiGroups: - - appstudio.redhat.com - resources: - - enterprisecontractpolicies - verbs: - - get - - list - - watch - - apiGroups: - - appstudio.redhat.com - resources: - - releases - - releasestrategies - - releaseplans - verbs: - - '*' - - apiGroups: - - appstudio.redhat.com - resources: - - releaseplanadmissions - verbs: - - '*' - - apiGroups: - - jvmbuildservice.io - resources: - - jbsconfigs - - artifactbuilds - verbs: - - get - - list - - watch - - create - - update - - patch - - apiGroups: - - appstudio.redhat.com - resources: - - spiaccesstokenbindings - - spiaccesschecks - - spiaccesstokens - - spifilecontentrequests - - spiaccesstokendataupdates - verbs: - - get - - list - - watch - - create - - update - - patch - - apiGroups: - - '' - resources: - - configmaps - verbs: - - get - - list - - watch -- apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - namespace: ${NAMESPACE} - name: appstudio-${USERNAME}-user-actions - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: appstudio-maintainer-user-actions - subjects: - - apiGroup: "" - kind: ServiceAccount - name: appstudio-${USERNAME} -# Role & RoleBinding that grants view permissions to the user's SA -- apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - namespace: ${NAMESPACE} - name: appstudio-${USERNAME}-view - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: view - subjects: - - apiGroup: "" - kind: ServiceAccount - name: appstudio-${USERNAME} - -parameters: -- name: USERNAME - required: true -- name: NAMESPACE - required: true \ No newline at end of file diff --git a/test/templates/nstemplatetiers/appstudio/tier.yaml b/test/templates/nstemplatetiers/appstudio/tier.yaml deleted file mode 100644 index 9d6f424fb..000000000 --- a/test/templates/nstemplatetiers/appstudio/tier.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: appstudio-tier -objects: -- kind: NSTemplateTier - apiVersion: toolchain.dev.openshift.com/v1alpha1 - metadata: - name: appstudio - namespace: ${NAMESPACE} - spec: - clusterResources: - templateRef: ${CLUSTER_TEMPL_REF} - namespaces: - - templateRef: ${TENANT_TEMPL_REF} - spaceRoles: - admin: - templateRef: ${ADMIN_TEMPL_REF} - maintainer: - templateRef: ${MAINTAINER_TEMPL_REF} - contributor: - templateRef: ${CONTRIBUTOR_TEMPL_REF} -parameters: -- name: NAMESPACE -- name: CLUSTER_TEMPL_REF -- name: TENANT_TEMPL_REF -- name: ADMIN_TEMPL_REF -- name: MAINTAINER_TEMPL_REF -- name: CONTRIBUTOR_TEMPL_REF diff --git a/test/templates/nstemplatetiers/base/cluster.yaml b/test/templates/nstemplatetiers/base/cluster.yaml deleted file mode 100644 index c7d0f405a..000000000 --- a/test/templates/nstemplatetiers/base/cluster.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - labels: - toolchain.dev.openshift.com/provider: codeready-toolchain - name: base-cluster-resources -objects: -- apiVersion: quota.openshift.io/v1 - kind: ClusterResourceQuota - metadata: - name: for-${SPACE_NAME} - spec: - quota: - hard: - limits.cpu: ${CPU_LIMIT} - limits.memory: 7Gi - requests.storage: 7Gi - persistentvolumeclaims: "5" - selector: - annotations: null - labels: - matchLabels: - toolchain.dev.openshift.com/space: ${SPACE_NAME} -parameters: -- name: SPACE_NAME - required: true -- name: CPU_LIMIT - value: 4000m \ No newline at end of file diff --git a/test/templates/nstemplatetiers/base/ns_dev.yaml b/test/templates/nstemplatetiers/base/ns_dev.yaml deleted file mode 100644 index 0beea9a51..000000000 --- a/test/templates/nstemplatetiers/base/ns_dev.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - labels: - toolchain.dev.openshift.com/provider: codeready-toolchain - name: base-dev -objects: -- apiVersion: v1 - kind: Namespace - metadata: - annotations: - openshift.io/description: ${SPACE_NAME}-dev - openshift.io/display-name: ${SPACE_NAME}-dev - openshift.io/requester: ${SPACE_NAME} - labels: - toolchain.dev.openshift.com/provider: codeready-toolchain - name: ${SPACE_NAME}-dev - name: ${SPACE_NAME}-dev -parameters: -- name: SPACE_NAME - required: true \ No newline at end of file diff --git a/test/templates/nstemplatetiers/base/ns_stage.yaml b/test/templates/nstemplatetiers/base/ns_stage.yaml deleted file mode 100644 index 77fcc1e85..000000000 --- a/test/templates/nstemplatetiers/base/ns_stage.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - labels: - toolchain.dev.openshift.com/provider: codeready-toolchain - name: base-stage -objects: -- apiVersion: v1 - kind: Namespace - metadata: - annotations: - openshift.io/description: ${SPACE_NAME}-stage - openshift.io/display-name: ${SPACE_NAME}-stage - openshift.io/requester: ${SPACE_NAME} - labels: - toolchain.dev.openshift.com/provider: codeready-toolchain - name: ${SPACE_NAME}-stage - name: ${SPACE_NAME}-stage -parameters: -- name: SPACE_NAME - required: true \ No newline at end of file diff --git a/test/templates/nstemplatetiers/base/spacerole_admin.yaml b/test/templates/nstemplatetiers/base/spacerole_admin.yaml deleted file mode 100644 index 87dec97f4..000000000 --- a/test/templates/nstemplatetiers/base/spacerole_admin.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: base-spacerole-admin -objects: - -# Rolebindings that grant permissions to the users in their own namespaces -- apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - namespace: ${NAMESPACE} - name: ${USERNAME}-rbac-edit - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: rbac-edit - subjects: - - kind: User - name: ${USERNAME} - -parameters: -- name: USERNAME - required: true -- name: NAMESPACE - required: true \ No newline at end of file diff --git a/test/templates/nstemplatetiers/base/tier.yaml b/test/templates/nstemplatetiers/base/tier.yaml deleted file mode 100644 index 7eb81c180..000000000 --- a/test/templates/nstemplatetiers/base/tier.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: base-tier -objects: -- kind: NSTemplateTier - apiVersion: toolchain.dev.openshift.com/v1alpha1 - metadata: - name: base - namespace: ${NAMESPACE} - spec: - clusterResources: - templateRef: ${CLUSTER_TEMPL_REF} - namespaces: - - templateRef: ${DEV_TEMPL_REF} - - templateRef: ${STAGE_TEMPL_REF} - spaceRoles: - admin: - templateRef: ${ADMIN_TEMPL_REF} -parameters: -- name: NAMESPACE -- name: CLUSTER_TEMPL_REF -- name: DEV_TEMPL_REF -- name: STAGE_TEMPL_REF -- name: ADMIN_TEMPL_REF diff --git a/test/templates/nstemplatetiers/doc.go b/test/templates/nstemplatetiers/doc.go deleted file mode 100644 index a87e308e8..000000000 --- a/test/templates/nstemplatetiers/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -package nstemplatetiers_test - -// This package contains yaml files which are used by the link:../pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go[NSTemplateTier generator tests]. -// These YAML files may defer from the actual templates that will be used in production, but having a separate source for testing will avoid us having to maintain the tests as the YAML files evolve. diff --git a/test/templates/nstemplatetiers/metadata.yaml b/test/templates/nstemplatetiers/metadata.yaml deleted file mode 100644 index 24a5c9e65..000000000 --- a/test/templates/nstemplatetiers/metadata.yaml +++ /dev/null @@ -1,16 +0,0 @@ -advanced/based_on_tier: "abcd123" -base/tier: "123456z" -base/cluster: "654321a" -base/ns_dev: "123456b" -base/ns_stage: "123456c" -base/spacerole_admin: "123456d" -nocluster/tier: "1234567y" -nocluster/ns_dev: "123456j" -nocluster/ns_stage: "1234567" # here, a number string -nocluster/spacerole_admin: "123456k" -appstudio/tier: "123456z" -appstudio/cluster: "654321a" -appstudio/ns_tenant: "123456b" -appstudio/spacerole_admin: "123456c" -appstudio/spacerole_maintainer: "123456d" -appstudio/spacerole_contributor: "123456e" \ No newline at end of file diff --git a/test/templates/nstemplatetiers/nocluster/ns_dev.yaml b/test/templates/nstemplatetiers/nocluster/ns_dev.yaml deleted file mode 100644 index dfaa99831..000000000 --- a/test/templates/nstemplatetiers/nocluster/ns_dev.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - labels: - toolchain.dev.openshift.com/provider: codeready-toolchain - name: nocluster-dev -objects: -- apiVersion: v1 - kind: Namespace - metadata: - annotations: - openshift.io/description: ${SPACE_NAME}-dev - openshift.io/display-name: ${SPACE_NAME}-dev - openshift.io/requester: ${SPACE_NAME} - labels: - toolchain.dev.openshift.com/provider: codeready-toolchain - name: ${SPACE_NAME}-dev - name: ${SPACE_NAME}-dev -parameters: -- name: SPACE_NAME - required: true \ No newline at end of file diff --git a/test/templates/nstemplatetiers/nocluster/ns_stage.yaml b/test/templates/nstemplatetiers/nocluster/ns_stage.yaml deleted file mode 100644 index 35f9c93a8..000000000 --- a/test/templates/nstemplatetiers/nocluster/ns_stage.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - labels: - toolchain.dev.openshift.com/provider: codeready-toolchain - name: nocluster-stage -objects: -- apiVersion: v1 - kind: Namespace - metadata: - annotations: - openshift.io/description: ${SPACE_NAME}-stage - openshift.io/display-name: ${SPACE_NAME}-stage - openshift.io/requester: ${SPACE_NAME} - labels: - toolchain.dev.openshift.com/provider: codeready-toolchain - name: ${SPACE_NAME}-stage - name: ${SPACE_NAME}-stage -parameters: -- name: SPACE_NAME - required: true \ No newline at end of file diff --git a/test/templates/nstemplatetiers/nocluster/spacerole_admin.yaml b/test/templates/nstemplatetiers/nocluster/spacerole_admin.yaml deleted file mode 100644 index bf3bf1a4b..000000000 --- a/test/templates/nstemplatetiers/nocluster/spacerole_admin.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: base-spacerole-admin -objects: - -# Rolebindings that grant permissions to the users in their own namespaces -- apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - namespace: ${NAMESPACE} - name: user-rbac-edit - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: rbac-edit - subjects: - - kind: User - name: ${USERNAME} -- apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - namespace: ${NAMESPACE} - name: user-rbac-edit - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: rbac-edit - subjects: - - kind: User - name: ${USERNAME} - -parameters: -- name: USERNAME - required: true -- name: NAMESPACE - required: true \ No newline at end of file diff --git a/test/templates/nstemplatetiers/nocluster/tier.yaml b/test/templates/nstemplatetiers/nocluster/tier.yaml deleted file mode 100644 index e664d4655..000000000 --- a/test/templates/nstemplatetiers/nocluster/tier.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: nocluster-tier -objects: -- kind: NSTemplateTier - apiVersion: toolchain.dev.openshift.com/v1alpha1 - metadata: - name: nocluster - namespace: ${NAMESPACE} - spec: - namespaces: - - templateRef: ${DEV_TEMPL_REF} - - templateRef: ${STAGE_TEMPL_REF} - spaceRoles: - admin: - templateRef: ${ADMIN_TEMPL_REF} -parameters: -- name: NAMESPACE -- name: DEV_TEMPL_REF -- name: STAGE_TEMPL_REF -- name: ADMIN_TEMPL_REF From b39bddd4737513cc90abdb845d7ed7f8f724d0b0 Mon Sep 17 00:00:00 2001 From: Matous Jobanek Date: Wed, 3 Apr 2024 12:40:46 +0200 Subject: [PATCH 2/5] metadata --- go.mod | 2 +- go.sum | 4 ++-- pkg/templates/nstemplatetiers/nstemplatetier_generator.go | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 60eaf6e7b..f71def339 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( require github.com/google/uuid v1.3.0 // indirect -replace github.com/codeready-toolchain/toolchain-common => github.com/matousjobanek/toolchain-common v0.0.0-20240403092205-8439bbc15fa2 +replace github.com/codeready-toolchain/toolchain-common => github.com/matousjobanek/toolchain-common v0.0.0-20240403103433-189935dc05ef require ( cloud.google.com/go v0.97.0 // indirect diff --git a/go.sum b/go.sum index fc2be32c8..ba9fd06c1 100644 --- a/go.sum +++ b/go.sum @@ -430,8 +430,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matousjobanek/toolchain-common v0.0.0-20240403092205-8439bbc15fa2 h1:x1PVCNIzpE/iDB4ED/1yAQbXZck0L9uoh9RgTh/nBCM= -github.com/matousjobanek/toolchain-common v0.0.0-20240403092205-8439bbc15fa2/go.mod h1:OJ3L9aaTRMGjxr2WeH/9l6m5OjExwEK3Bp/+P+efoGg= +github.com/matousjobanek/toolchain-common v0.0.0-20240403103433-189935dc05ef h1:MIWToDWnd6pFAhdmAEedy1pYBZhlj3lPTI9aPqEDzLM= +github.com/matousjobanek/toolchain-common v0.0.0-20240403103433-189935dc05ef/go.mod h1:Iat3N+zBZcVgm/HWxa/ltSEoelM/YCXQUvbL9C8OSTw= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= diff --git a/pkg/templates/nstemplatetiers/nstemplatetier_generator.go b/pkg/templates/nstemplatetiers/nstemplatetier_generator.go index bb8500281..d29743535 100644 --- a/pkg/templates/nstemplatetiers/nstemplatetier_generator.go +++ b/pkg/templates/nstemplatetiers/nstemplatetier_generator.go @@ -24,12 +24,15 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt return errors.Wrapf(err, "unable to load templates") } metadata := make(map[string]string) - err = yaml.Unmarshal([]byte(metadataContent), &metadata) + err = yaml.Unmarshal(metadataContent, &metadata) if err != nil { return errors.Wrapf(err, "unable to load templates") } files := map[string][]byte{} for _, name := range assets.Names() { + if name == "metadata.yaml" { + continue + } content, err := assets.Asset(name) if err != nil { return errors.Wrapf(err, "unable to load templates") From ca35c0d346a48d9a60cbe6bb25609b6f26610dc0 Mon Sep 17 00:00:00 2001 From: Matous Jobanek Date: Wed, 17 Apr 2024 09:13:52 +0200 Subject: [PATCH 3/5] update dependency --- go.mod | 4 +--- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index a8ec79d3b..4b1cafc50 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/codeready-toolchain/host-operator require ( github.com/codeready-toolchain/api v0.0.0-20240322110702-5ab3840476e9 - github.com/codeready-toolchain/toolchain-common v0.0.0-20240404090512-046d250d7d78 + github.com/codeready-toolchain/toolchain-common v0.0.0-20240417071216-32311a6b30ce github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-bindata/go-bindata v3.1.2+incompatible github.com/go-logr/logr v1.2.3 @@ -32,8 +32,6 @@ require ( require github.com/google/uuid v1.3.0 // indirect -replace github.com/codeready-toolchain/toolchain-common => github.com/matousjobanek/toolchain-common v0.0.0-20240412095916-4dcad487a248 - require ( cloud.google.com/go v0.97.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect diff --git a/go.sum b/go.sum index 4b20b2127..6bda20a17 100644 --- a/go.sum +++ b/go.sum @@ -138,6 +138,8 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/codeready-toolchain/api v0.0.0-20240322110702-5ab3840476e9 h1:Lm7bFLrzfJzrUiRGVqtsSaZMpj+akLiR/fvAFjjE9gM= github.com/codeready-toolchain/api v0.0.0-20240322110702-5ab3840476e9/go.mod h1:cfNN6YPX4TORvhhZXMSjSPesqAHlB3nD/WAfGe4WLKQ= +github.com/codeready-toolchain/toolchain-common v0.0.0-20240417071216-32311a6b30ce h1:LCsVv23cZUH2/ezAmDqRrchOLlXM9trJe8gj1qI6kSo= +github.com/codeready-toolchain/toolchain-common v0.0.0-20240417071216-32311a6b30ce/go.mod h1:Iat3N+zBZcVgm/HWxa/ltSEoelM/YCXQUvbL9C8OSTw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -430,8 +432,6 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matousjobanek/toolchain-common v0.0.0-20240412095916-4dcad487a248 h1:pjJFv4tkAUorEAK/O4LGygOb1SgN3hAJ/EGlFcdfC7A= -github.com/matousjobanek/toolchain-common v0.0.0-20240412095916-4dcad487a248/go.mod h1:Iat3N+zBZcVgm/HWxa/ltSEoelM/YCXQUvbL9C8OSTw= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= From bc11d82c0741d0215ce6159c9883ce0d3a60303f Mon Sep 17 00:00:00 2001 From: Matous Jobanek Date: Wed, 17 Apr 2024 11:21:27 +0200 Subject: [PATCH 4/5] fix test --- pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go b/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go index eb2e1de9f..b48293656 100644 --- a/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go +++ b/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go @@ -165,7 +165,7 @@ func TestCreateOrUpdateResourcesWitProdAssets(t *testing.T) { err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, assets) // then require.Error(t, err) - assert.Regexp(t, "unable to create or update the '\\w+' NSTemplateTier: unable to create resource of kind: NSTemplateTier, version: v1alpha1: an error", err.Error()) + assert.Regexp(t, "unable to create NSTemplateTiers: unable to create or update the '\\w+' NSTemplateTier: unable to create resource of kind: NSTemplateTier, version: v1alpha1: an error", err.Error()) }) t.Run("failed to update nstemplatetiers", func(t *testing.T) { From dbd9730abd39285b3e85a3468d423cc602ab293d Mon Sep 17 00:00:00 2001 From: Matous Jobanek Date: Wed, 17 Apr 2024 11:34:27 +0200 Subject: [PATCH 5/5] update common & fix flaky tests 2.0 --- go.mod | 2 +- go.sum | 2 ++ .../nstemplatetier_generator_test.go | 36 ++++++++----------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/go.mod b/go.mod index 4b1cafc50..26070ba8a 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/codeready-toolchain/host-operator require ( github.com/codeready-toolchain/api v0.0.0-20240322110702-5ab3840476e9 - github.com/codeready-toolchain/toolchain-common v0.0.0-20240417071216-32311a6b30ce + github.com/codeready-toolchain/toolchain-common v0.0.0-20240417084737-d3c148491687 github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-bindata/go-bindata v3.1.2+incompatible github.com/go-logr/logr v1.2.3 diff --git a/go.sum b/go.sum index 6bda20a17..9c5e343fa 100644 --- a/go.sum +++ b/go.sum @@ -140,6 +140,8 @@ github.com/codeready-toolchain/api v0.0.0-20240322110702-5ab3840476e9 h1:Lm7bFLr github.com/codeready-toolchain/api v0.0.0-20240322110702-5ab3840476e9/go.mod h1:cfNN6YPX4TORvhhZXMSjSPesqAHlB3nD/WAfGe4WLKQ= github.com/codeready-toolchain/toolchain-common v0.0.0-20240417071216-32311a6b30ce h1:LCsVv23cZUH2/ezAmDqRrchOLlXM9trJe8gj1qI6kSo= github.com/codeready-toolchain/toolchain-common v0.0.0-20240417071216-32311a6b30ce/go.mod h1:Iat3N+zBZcVgm/HWxa/ltSEoelM/YCXQUvbL9C8OSTw= +github.com/codeready-toolchain/toolchain-common v0.0.0-20240417084737-d3c148491687 h1:ZPURdFfMNOsEyNKtCTzY9Gsj0jKQL13tR/uj7OAlZL4= +github.com/codeready-toolchain/toolchain-common v0.0.0-20240417084737-d3c148491687/go.mod h1:Iat3N+zBZcVgm/HWxa/ltSEoelM/YCXQUvbL9C8OSTw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= diff --git a/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go b/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go index b48293656..a75a56773 100644 --- a/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go +++ b/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go @@ -83,27 +83,19 @@ func TestCreateOrUpdateResourcesWitProdAssets(t *testing.T) { expectedSpaceRoleTmplRefs := map[string]map[string]string{} require.Len(t, nstemplateTiers.Items, len(expectedProdTiers)) for _, tier := range expectedProdTiers { - t.Run(tier, func(t *testing.T) { - for _, nsTypeName := range nsTypes(tier) { - t.Run("ns-"+nsTypeName, func(t *testing.T) { - templateName := verifyTierTemplate(t, cl, namespace, tier, nsTypeName) - expectedNamespaceTmplRefs[tier] = append(expectedNamespaceTmplRefs[tier], templateName) - }) - } - for _, role := range roles(tier) { - t.Run("spacerole-"+role, func(t *testing.T) { - roleName := verifyTierTemplate(t, cl, namespace, tier, role) - if expectedSpaceRoleTmplRefs[tier] == nil { - expectedSpaceRoleTmplRefs[tier] = map[string]string{} - } - expectedSpaceRoleTmplRefs[tier][role] = roleName - }) + for _, nsTypeName := range nsTypes(tier) { + templateName := verifyTierTemplate(t, cl, namespace, tier, nsTypeName) + expectedNamespaceTmplRefs[tier] = append(expectedNamespaceTmplRefs[tier], templateName) + } + for _, role := range roles(tier) { + roleName := verifyTierTemplate(t, cl, namespace, tier, role) + if expectedSpaceRoleTmplRefs[tier] == nil { + expectedSpaceRoleTmplRefs[tier] = map[string]string{} } - t.Run("clusterresources", func(t *testing.T) { - templateName := verifyTierTemplate(t, cl, namespace, tier, "clusterresources") - expectedClusterResourcesTmplRef[tier] = templateName - }) - }) + expectedSpaceRoleTmplRefs[tier][role] = roleName + } + templateName := verifyTierTemplate(t, cl, namespace, tier, "clusterresources") + expectedClusterResourcesTmplRef[tier] = templateName } // verify that each NSTemplateTier has the ClusterResources, Namespaces and SpaceRoles `TemplateRef` set as expected @@ -154,7 +146,7 @@ func TestCreateOrUpdateResourcesWitProdAssets(t *testing.T) { // given clt := commontest.NewFakeClient(t) clt.MockCreate = func(ctx context.Context, obj runtimeclient.Object, opts ...runtimeclient.CreateOption) error { - if obj.GetObjectKind().GroupVersionKind().Kind == "NSTemplateTier" { + if obj.GetObjectKind().GroupVersionKind().Kind == "NSTemplateTier" && obj.GetName() == "base" { // simulate a client/server error return errors.Errorf("an error") } @@ -165,7 +157,7 @@ func TestCreateOrUpdateResourcesWitProdAssets(t *testing.T) { err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, assets) // then require.Error(t, err) - assert.Regexp(t, "unable to create NSTemplateTiers: unable to create or update the '\\w+' NSTemplateTier: unable to create resource of kind: NSTemplateTier, version: v1alpha1: an error", err.Error()) + assert.Regexp(t, "unable to create NSTemplateTiers: unable to create or update the 'base' NSTemplateTier: unable to create resource of kind: NSTemplateTier, version: v1alpha1: an error", err.Error()) }) t.Run("failed to update nstemplatetiers", func(t *testing.T) {