Repository: codeready-toolchain/host-operator
Pull request #1165: KUBESAW-273: Replace go-bindata with go:embed
Author: login: fbm3307
Labels: name: approved
Created: 2025-04-04T13:30:51Z | Merged: 2025-05-12T08:07:58Z

We want to replace go-bindata with go:embed so that we don't rely on 3rd party libraries and use functionality provided as part of Go itself.

This PR is 3rd PR in the series and succeeds PR - https://github.com/codeready-toolchain/host-operator/pull/1161 .
This is for converting nstemplatetiers to use goembed instead of gobindata


Diff:
diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml
index 1a82c8dc8..e7ba74f9a 100644
--- a/.github/workflows/govulncheck.yml
+++ b/.github/workflows/govulncheck.yml
@@ -18,10 +18,6 @@ jobs:
       with:
         go-version-file: go.mod
 
-    - name: Generate Assets
-      run: |
-        make generate-assets
-
     - name: Run govulncheck
       uses: golang/govulncheck-action@v1
       with:
diff --git a/cmd/main.go b/cmd/main.go
index 51b98109d..75a2adfbe 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -34,7 +34,6 @@ import (
 	"github.com/codeready-toolchain/host-operator/pkg/cluster"
 	"github.com/codeready-toolchain/host-operator/pkg/metrics"
 	"github.com/codeready-toolchain/host-operator/pkg/segment"
-	"github.com/codeready-toolchain/host-operator/pkg/templates/assets"
 	"github.com/codeready-toolchain/host-operator/pkg/templates/nstemplatetiers"
 	"github.com/codeready-toolchain/host-operator/pkg/templates/usertiers"
 	"github.com/codeready-toolchain/host-operator/version"
@@ -426,8 +425,7 @@ func main() { // nolint:gocyclo
 
 		// create or update all NSTemplateTiers on the cluster at startup
 		setupLog.Info("Creating/updating the NSTemplateTier resources")
-		nstemplatetierAssets := assets.NewAssets(nstemplatetiers.AssetNames, nstemplatetiers.Asset)
-		if err := nstemplatetiers.CreateOrUpdateResources(ctx, mgr.GetScheme(), mgr.GetClient(), namespace, nstemplatetierAssets); err != nil {
+		if err := nstemplatetiers.CreateOrUpdateResources(ctx, mgr.GetScheme(), mgr.GetClient(), namespace); err != nil {
 			setupLog.Error(err, "")
 			os.Exit(1)
 		}
diff --git a/deploy/resources.go b/deploy/resources.go
index 25322aaf4..7b4c9c9c2 100644
--- a/deploy/resources.go
+++ b/deploy/resources.go
@@ -15,3 +15,6 @@ var RegistrationServiceFS embed.FS
 
 //go:embed templates/usertiers/*
 var UserTiersFS embed.FS
+
+//go:embed templates/nstemplatetiers/*
+var NSTemplateTiersFS embed.FS
diff --git a/go.mod b/go.mod
index 0cfcb48df..19bf00e84 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,6 @@ require (
 	github.com/codeready-toolchain/api v0.0.0-20250506092100-39b4862e1271
 	github.com/codeready-toolchain/toolchain-common v0.0.0-20250506093954-2b65ad3a2e12
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
-	github.com/go-bindata/go-bindata v3.1.2+incompatible
 	github.com/go-logr/logr v1.4.2
 	github.com/gofrs/uuid v4.4.0+incompatible
 	github.com/mailgun/mailgun-go/v4 v4.8.1
diff --git a/go.sum b/go.sum
index 9ab914c43..a25058bf8 100644
--- a/go.sum
+++ b/go.sum
@@ -71,8 +71,6 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
 github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
 github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE=
-github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
 github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
 github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
 github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
diff --git a/make/get-tool.mk b/make/get-tool.mk
index 5ef1f0593..78e65d5e0 100644
--- a/make/get-tool.mk
+++ b/make/get-tool.mk
@@ -7,10 +7,6 @@ CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
 controller-gen: ## Download controller-gen locally if necessary.
 	$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,${CONTROLLER_GEN_VERSION})
 
-GO_BINDATA = $(shell pwd)/bin/go-bindata
-go-bindata: ## Download go-bindata locally if necessary.
-	$(call go-get-tool,$(GO_BINDATA),github.com/go-bindata/go-bindata/go-bindata,${GO_BINDATA_VERSION})
-
 KUSTOMIZE = $(shell pwd)/bin/kustomize
 kustomize: ## Download kustomize locally if necessary.
 	$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION))
diff --git a/make/go.mk b/make/go.mk
index 728f3ea44..e1150872d 100644
--- a/make/go.mk
+++ b/make/go.mk
@@ -33,10 +33,7 @@ USERTEMPLATES_TEST_BASEDIR = test/templates/usertiers
 REGISTRATION_SERVICE_DIR=deploy/registration-service
 
 .PHONY: generate
-generate: install-go-bindata generate-metadata generate-assets 
-
-install-go-bindata:
-	@go install github.com/go-bindata/go-bindata/...
+generate: generate-metadata 
 
 clean-metadata:
 	@rm $(NSTEMPLATES_BASEDIR)/metadata.yaml 2>/dev/null || true
@@ -51,12 +48,6 @@ define git_commit
 	echo "$(patsubst $(2)/%.yaml,%,$(1)): \""`git log -1 --format=%h $(1)`"\"">> $(2)/$(3) # surround the commit hash with quotes to force the value as a string, even if it's a number
 endef
 
-.PHONY: generate-assets
-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)/...
-	
 
 .PHONY: verify-dependencies
 ## Runs commands to verify after the updated dependecies of toolchain-common/API(go mod replace), if the repo needs any changes to be made
diff --git a/pkg/templates/assets/assets.go b/pkg/templates/assets/assets.go
deleted file mode 100644
index 087da0d8b..000000000
--- a/pkg/templates/assets/assets.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package assets
-
-// NewAssets returns a new wrapper for the binary assets generated by go-bindata
-func NewAssets(names func() []string, asset func(string) ([]byte, error)) Assets {
-	return Assets{
-		Names: names,
-		Asset: asset,
-	}
-}
-
-// Assets a wrapper for the binary assets
-type Assets struct {
-	Names func() []string
-	Asset func(string) ([]byte, error)
-}
diff --git a/pkg/templates/nstemplatetiers/nstemplatetier_generator.go b/pkg/templates/nstemplatetiers/nstemplatetier_generator.go
index 7527ab718..56665e4ce 100644
--- a/pkg/templates/nstemplatetiers/nstemplatetier_generator.go
+++ b/pkg/templates/nstemplatetiers/nstemplatetier_generator.go
@@ -2,42 +2,31 @@ package nstemplatetiers
 
 import (
 	"context"
+	"embed"
+	"fmt"
+	"path/filepath"
+	"strings"
 
-	"github.com/codeready-toolchain/host-operator/pkg/templates/assets"
+	"github.com/codeready-toolchain/host-operator/deploy"
+	"github.com/codeready-toolchain/host-operator/pkg/templates"
 	commonclient "github.com/codeready-toolchain/toolchain-common/pkg/client"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template/nstemplatetiers"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-
 	"github.com/pkg/errors"
 	"gopkg.in/yaml.v2"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
 	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
+const NsTemplateTierRootDir = "templates/nstemplatetiers"
+
 // 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 {
+func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runtimeclient.Client, namespace string) error {
 
-	// load templates from assets
-	metadataContent, err := assets.Asset("metadata.yaml")
+	metadata, files, err := LoadFiles(deploy.NSTemplateTiersFS, NsTemplateTierRootDir)
 	if err != nil {
-		return errors.Wrapf(err, "unable to load templates")
-	}
-	metadata := make(map[string]string)
-	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")
-		}
-		files[name] = content
+		return err
 	}
 
 	// initialize tier generator, loads templates from assets
@@ -52,3 +41,47 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+// LoadFiles takes the file from deploy/nstemplatetiers/<tiername>/<yaml file name> . the folder structure should be 4 steps .
+// as the cologic here is written accordingly
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
+	// load templates from assets
+	metadataContent, err := nsTemplateTiers.ReadFile(filepath.Join(root, "metadata.yaml"))
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	metadata = make(map[string]string)
+	err = yaml.Unmarshal(metadataContent, &metadata)
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	paths, err := templates.GetAllFileNames(&nsTemplateTiers, root)
+	if err != nil {
+		return nil, nil, err
+	}
+	if len(paths) == 0 {
+		return nil, nil, fmt.Errorf("could not find any ns templates")
+	}
+	files = make(map[string][]byte)
+	for _, name := range paths {
+		if name == filepath.Join(root, "metadata.yaml") {
+			continue
+		}
+
+		parts := strings.Split(name, "/")
+		// skip any name that does not have 4 parts
+		if len(parts) != 4 {
+			return nil, nil, fmt.Errorf("unable to load templates: invalid name format for file '%s'", name)
+		}
+
+		fileName := filepath.Join(parts[2], parts[3])
+		content, err := nsTemplateTiers.ReadFile(name)
+		if err != nil {
+			return nil, nil, errors.Wrapf(err, "unable to load templates")
+		}
+		files[fileName] = content
+	}
+	return metadata, files, nil
+}
diff --git a/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go b/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go
index 00eb976b0..c22adc13b 100644
--- a/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go
+++ b/pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go
@@ -6,15 +6,14 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/gofrs/uuid"
-
 	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
+	"github.com/codeready-toolchain/host-operator/deploy"
 	"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"
 	commontest "github.com/codeready-toolchain/toolchain-common/pkg/test"
-
+	"github.com/gofrs/uuid"
 	"github.com/pkg/errors"
+
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -59,10 +58,9 @@ func TestCreateOrUpdateResourcesWitProdAssets(t *testing.T) {
 	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)
 
 	// when
-	err = nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, cl, namespace, templateAssets)
+	err = nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, cl, namespace)
 
 	// then
 	require.NoError(t, err)
@@ -113,24 +111,6 @@ func TestCreateOrUpdateResourcesWitProdAssets(t *testing.T) {
 	t.Run("failures", func(t *testing.T) {
 
 		namespace := "host-operator" + uuid.Must(uuid.NewV4()).String()[:7]
-
-		t.Run("failed to read assets", func(t *testing.T) {
-			// given
-			fakeAssets := assets.NewAssets(nstemplatetiers.AssetNames, func(name string) ([]byte, error) {
-				if name == "metadata.yaml" {
-					return []byte("advanced-code: abcdef"), nil
-				}
-				// error occurs when fetching the content of the 'advanced-code.yaml' template
-				return nil, errors.Errorf("an error")
-			})
-			clt := commontest.NewFakeClient(t)
-			// when
-			err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, fakeAssets)
-			// then
-			require.Error(t, err)
-			assert.Equal(t, "unable to load templates: an error", err.Error()) // error occurred while creating TierTemplate resources
-		})
-
 		t.Run("nstemplatetiers", func(t *testing.T) {
 
 			t.Run("failed to create nstemplatetiers", func(t *testing.T) {
@@ -143,9 +123,8 @@ func TestCreateOrUpdateResourcesWitProdAssets(t *testing.T) {
 					}
 					return clt.Client.Create(ctx, obj, opts...)
 				}
-				assets := assets.NewAssets(nstemplatetiers.AssetNames, nstemplatetiers.Asset)
 				// when
-				err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, assets)
+				err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace)
 				// then
 				require.Error(t, err)
 				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())
@@ -167,9 +146,9 @@ func TestCreateOrUpdateResourcesWitProdAssets(t *testing.T) {
 					}
 					return clt.Client.Update(ctx, obj, opts...)
 				}
-				testassets := assets.NewAssets(nstemplatetiers.AssetNames, nstemplatetiers.Asset)
+
 				// when
-				err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, testassets)
+				err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace)
 				// then
 				require.Error(t, err)
 				assert.Contains(t, err.Error(), "unable to create NSTemplateTiers: unable to create or update the 'advanced' NSTemplateTier: unable to create resource of kind: NSTemplateTier, version: v1alpha1: unable to update the resource")
@@ -188,50 +167,23 @@ func TestCreateOrUpdateResourcesWitProdAssets(t *testing.T) {
 					}
 					return clt.Client.Create(ctx, obj, opts...)
 				}
-				testassets := assets.NewAssets(nstemplatetiers.AssetNames, nstemplatetiers.Asset)
 				// when
-				err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace, testassets)
+				err := nstemplatetiers.CreateOrUpdateResources(context.TODO(), s, clt, namespace)
 				// then
 				require.Error(t, err)
 				assert.Regexp(t, fmt.Sprintf("unable to create TierTemplates: unable to create the 'base1ns-dev-\\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")
-		})
 	})
+	t.Run("failed to load assets", func(t *testing.T) {
+		// when
+		_, _, err := nstemplatetiers.LoadFiles(deploy.NSTemplateTiersFS, "/"+nstemplatetiers.NsTemplateTierRootDir)
+		// then
+		require.Error(t, err)
+		assert.Equal(t, "unable to load templates: open /templates/nstemplatetiers/metadata.yaml: file does not exist", err.Error()) // error occurred while creating TierTemplate resources
+	})
+
 }
 
 func verifyTierTemplate(t *testing.T, cl *commontest.FakeClient, namespace, tierName, typeName string) string {
diff --git a/pkg/templates/resources.go b/pkg/templates/resources.go
new file mode 100644
index 000000000..a01ce0825
--- /dev/null
+++ b/pkg/templates/resources.go
@@ -0,0 +1,21 @@
+package templates
+
+import (
+	"embed"
+	"io/fs"
+)
+
+func GetAllFileNames(TemplateTierFS *embed.FS, root string) (files []string, err error) {
+
+	if err := fs.WalkDir(TemplateTierFS, root, func(path string, d fs.DirEntry, err error) error {
+		if d.IsDir() {
+			return nil
+		}
+		files = append(files, path)
+		return nil
+	}); err != nil {
+		return nil, err
+	}
+
+	return files, nil
+}
diff --git a/pkg/templates/usertiers/usertier_generator.go b/pkg/templates/usertiers/usertier_generator.go
index 0b3fe42a5..686c25665 100644
--- a/pkg/templates/usertiers/usertier_generator.go
+++ b/pkg/templates/usertiers/usertier_generator.go
@@ -4,11 +4,11 @@ import (
 	"context"
 	"embed"
 	"fmt"
-	"io/fs"
 	"reflect"
 	"strings"
 
 	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
+	resource "github.com/codeready-toolchain/host-operator/pkg/templates"
 	commonclient "github.com/codeready-toolchain/toolchain-common/pkg/client"
 	commonTemplate "github.com/codeready-toolchain/toolchain-common/pkg/template"
 
@@ -117,7 +117,7 @@ func newUserTierGenerator(s *runtime.Scheme, client runtimeclient.Client, namesp
 // Each `tierData` object contains template objects for the tier.
 // Each `template` object contains a `revision` (`string`) and the `content` of the template to apply (`[]byte`)
 func loadTemplatesByTiers(userTierFS embed.FS, root string) (map[string]*tierData, error) {
-	paths, err := getAllFileNames(&userTierFS, root)
+	paths, err := resource.GetAllFileNames(&userTierFS, root)
 	if err != nil {
 		return nil, err
 	}
@@ -264,18 +264,3 @@ func (t *tierGenerator) newUserTier(sourceTierName, tierName string, userTierTem
 	}
 	return toolchainObjects, nil
 }
-
-func getAllFileNames(notificationFS *embed.FS, root string) (files []string, err error) {
-
-	if err := fs.WalkDir(notificationFS, root, func(path string, d fs.DirEntry, err error) error {
-		if d.IsDir() {
-			return nil
-		}
-		files = append(files, path)
-		return nil
-	}); err != nil {
-		return nil, err
-	}
-
-	return files, nil
-}
diff --git a/tools.go b/tools.go
deleted file mode 100644
index 45b067f28..000000000
--- a/tools.go
+++ /dev/null
@@ -1,9 +0,0 @@
-//go:build tools
-// +build tools
-
-package tools
-
-import (
-	// Code generators built at runtime.
-	_ "github.com/go-bindata/go-bindata"
-)


Reviews:
user: login: metlos
body: generally LGTM but I'd like us to explore the ways to improve the locality of the code and data.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T11:53:32Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T05:50:36Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T05:51:46Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T08:27:55Z
user: login: metlos
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T06:50:42Z
user: login: metlos
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T07:30:57Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T11:08:35Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T11:08:58Z
user: login: metlos
body: Thanks for taking my suggestions into account 🙏🏼 

I only have a couple more nitpicks, no need to implement them if you feel that's not necessary now or if you made those choices deliberately with some future use in mind that I didn't see.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T16:37:30Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T06:53:12Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T07:20:02Z
user: login: metlos
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T13:08:52Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T13:16:38Z
user: login: mfrancisc
body: Great Job 👍 

I have only one minor comment.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T14:05:31Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T05:46:30Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T05:52:25Z
user: login: mfrancisc
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T12:27:26Z
user: login: mfrancisc
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T12:27:57Z
user: login: mfrancisc
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T12:32:19Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T11:33:59Z
user: login: metlos
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T11:14:48Z
user: login: MatousJobanek
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T09:14:37Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T09:38:12Z
user: login: MatousJobanek
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T10:07:02Z
user: login: mfrancisc
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T13:19:05Z
user: login: mfrancisc
body: Looks good! 

Thanks for addressing my comments.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T13:20:00Z
user: login: MatousJobanek
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T07:44:15Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T12:01:01Z
user: login: MatousJobanek
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T07:56:57Z

Review comments:
diff_hunk: @@ -2,24 +2,28 @@ package nstemplatetiers
 
 import (
 	"context"
+	"embed"
+	"fmt"
+	"strings"
 
-	"github.com/codeready-toolchain/host-operator/pkg/templates/assets"
+	"github.com/codeready-toolchain/host-operator/pkg/templates"
 	commonclient "github.com/codeready-toolchain/toolchain-common/pkg/client"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template/nstemplatetiers"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-
 	"github.com/pkg/errors"
 	"gopkg.in/yaml.v2"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
 	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
+const NSTemplateTierRootDir = "templates/nstemplatetiers"
+
 // 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 {
+func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runtimeclient.Client, namespace string, nstemplatetierFS embed.FS, root string) error {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: metlos
body: The `nstemplatetierFS` and `root` always use the same values apart from 2 tests. Could we do away with those parameters (ideally, without sacrificing the tests either)?
created_at: 2025-04-08T11:32:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -28,16 +32,33 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 	if err != nil {
 		return errors.Wrapf(err, "unable to load templates")
 	}
+
+	paths, err := templates.GetAllFileNames(&nstemplatetierFS, root)
+	if err != nil {
+		return err
+	}
+	if len(paths) == 0 {
+		return fmt.Errorf("could not find any user templates")
+	}
+
 	files := map[string][]byte{}
-	for _, name := range assets.Names() {
-		if name == "metadata.yaml" {
+	for _, name := range paths {
+		if name == "templates/nstemplatetiers/metadata.yaml" {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: metlos
body: This feels tightly coupled to `deploy.NSTemplateTiersFS`. This would be ok if we didn't use the `nstemplatetierFS` as a parameter but IMHO is stylistically wrong when we're allowing passing in an arbitrary FS.
created_at: 2025-04-08T11:35:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -28,16 +32,33 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 	if err != nil {
 		return errors.Wrapf(err, "unable to load templates")
 	}
+
+	paths, err := templates.GetAllFileNames(&nstemplatetierFS, root)
+	if err != nil {
+		return err
+	}
+	if len(paths) == 0 {
+		return fmt.Errorf("could not find any user templates")
+	}
+
 	files := map[string][]byte{}
-	for _, name := range assets.Names() {
-		if name == "metadata.yaml" {
+	for _, name := range paths {
+		if name == "templates/nstemplatetiers/metadata.yaml" {
 			continue
 		}
-		content, err := assets.Asset(name)
+
+		parts := strings.Split(name, "/")
+		// skip any name that does not have 4 parts
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: metlos
body: Again, this is assuming the structure of the fs (that we don't restrict in any sense) and therefore feels tightly coupled to the expected `deploy.NSTemplateTiersFS`.
created_at: 2025-04-08T11:36:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -28,16 +32,33 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 	if err != nil {
 		return errors.Wrapf(err, "unable to load templates")
 	}
+
+	paths, err := templates.GetAllFileNames(&nstemplatetierFS, root)
+	if err != nil {
+		return err
+	}
+	if len(paths) == 0 {
+		return fmt.Errorf("could not find any user templates")
+	}
+
 	files := map[string][]byte{}
-	for _, name := range assets.Names() {
-		if name == "metadata.yaml" {
+	for _, name := range paths {
+		if name == "templates/nstemplatetiers/metadata.yaml" {
 			continue
 		}
-		content, err := assets.Asset(name)
+
+		parts := strings.Split(name, "/")
+		// skip any name that does not have 4 parts
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: fbm3307
body: Okay so this is done here because this [function](https://github.com/codeready-toolchain/host-operator/pull/1165/files#diff-75fb59490e920da60f86dae8e59891d725272ef82d0515a32447036e0b576e40R65) in [toolchain common](https://github.com/codeready-toolchain/toolchain-common/blob/f85b77b1e378ee8ec2c367559e13b0d35470f007/pkg/template/nstemplatetiers/nstemplatetier_generator.go#L150) expects only the last two parts of the folder structure. 
and the test are written accordingly. 

moreover, it has to be in line with how our template folder structure is. similar is for user tiers, notification and others.
created_at: 2025-04-09T05:50:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2,24 +2,28 @@ package nstemplatetiers
 
 import (
 	"context"
+	"embed"
+	"fmt"
+	"strings"
 
-	"github.com/codeready-toolchain/host-operator/pkg/templates/assets"
+	"github.com/codeready-toolchain/host-operator/pkg/templates"
 	commonclient "github.com/codeready-toolchain/toolchain-common/pkg/client"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template/nstemplatetiers"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-
 	"github.com/pkg/errors"
 	"gopkg.in/yaml.v2"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
 	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
+const NSTemplateTierRootDir = "templates/nstemplatetiers"
+
 // 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 {
+func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runtimeclient.Client, namespace string, nstemplatetierFS embed.FS, root string) error {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: fbm3307
body: i am not sure i understand it correctly, do you mean to remove these parameters?
created_at: 2025-04-09T05:51:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -28,16 +32,33 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 	if err != nil {
 		return errors.Wrapf(err, "unable to load templates")
 	}
+
+	paths, err := templates.GetAllFileNames(&nstemplatetierFS, root)
+	if err != nil {
+		return err
+	}
+	if len(paths) == 0 {
+		return fmt.Errorf("could not find any user templates")
+	}
+
 	files := map[string][]byte{}
-	for _, name := range assets.Names() {
-		if name == "metadata.yaml" {
+	for _, name := range paths {
+		if name == "templates/nstemplatetiers/metadata.yaml" {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: fbm3307
body: updated it
created_at: 2025-04-09T08:27:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2,24 +2,28 @@ package nstemplatetiers
 
 import (
 	"context"
+	"embed"
+	"fmt"
+	"strings"
 
-	"github.com/codeready-toolchain/host-operator/pkg/templates/assets"
+	"github.com/codeready-toolchain/host-operator/pkg/templates"
 	commonclient "github.com/codeready-toolchain/toolchain-common/pkg/client"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template/nstemplatetiers"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-
 	"github.com/pkg/errors"
 	"gopkg.in/yaml.v2"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
 	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
+const NSTemplateTierRootDir = "templates/nstemplatetiers"
+
 // 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 {
+func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runtimeclient.Client, namespace string, nstemplatetierFS embed.FS, root string) error {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: metlos
body: Yes, I think ideally the `nstemplatetiersFS` and `root` params should be removed because in the runtime code they always have the same value.
created_at: 2025-04-10T06:50:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -28,16 +32,33 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 	if err != nil {
 		return errors.Wrapf(err, "unable to load templates")
 	}
+
+	paths, err := templates.GetAllFileNames(&nstemplatetierFS, root)
+	if err != nil {
+		return err
+	}
+	if len(paths) == 0 {
+		return fmt.Errorf("could not find any user templates")
+	}
+
 	files := map[string][]byte{}
-	for _, name := range assets.Names() {
-		if name == "metadata.yaml" {
+	for _, name := range paths {
+		if name == "templates/nstemplatetiers/metadata.yaml" {
 			continue
 		}
-		content, err := assets.Asset(name)
+
+		parts := strings.Split(name, "/")
+		// skip any name that does not have 4 parts
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: metlos
body: I think that illustrates my point :) There's a function in `toolchain-common` that assumes the paths are 2 levels deep. Here, you assume that the paths are 4 levels deep but neither that function nor this function is enforcing that in any sense through the type-system or function signature.

This IMHO makes the code more fragile than it could be. If you removed the two parameters to this function, the assumption about 4 levels would suddenly be local to this function and the callers of this function would no longer need to care about it. This would IMHO improve the code.
created_at: 2025-04-10T07:30:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2,24 +2,28 @@ package nstemplatetiers
 
 import (
 	"context"
+	"embed"
+	"fmt"
+	"strings"
 
-	"github.com/codeready-toolchain/host-operator/pkg/templates/assets"
+	"github.com/codeready-toolchain/host-operator/pkg/templates"
 	commonclient "github.com/codeready-toolchain/toolchain-common/pkg/client"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template/nstemplatetiers"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-
 	"github.com/pkg/errors"
 	"gopkg.in/yaml.v2"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
 	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
+const NSTemplateTierRootDir = "templates/nstemplatetiers"
+
 // 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 {
+func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runtimeclient.Client, namespace string, nstemplatetierFS embed.FS, root string) error {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: fbm3307
body: Okay so  i removed the parameters from the main function and created another sub func added the parameters there so that i can use it for test and still it addresses the above concern.
PTAL @metlos .
created_at: 2025-04-21T11:08:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -28,16 +32,33 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 	if err != nil {
 		return errors.Wrapf(err, "unable to load templates")
 	}
+
+	paths, err := templates.GetAllFileNames(&nstemplatetierFS, root)
+	if err != nil {
+		return err
+	}
+	if len(paths) == 0 {
+		return fmt.Errorf("could not find any user templates")
+	}
+
 	files := map[string][]byte{}
-	for _, name := range assets.Names() {
-		if name == "metadata.yaml" {
+	for _, name := range paths {
+		if name == "templates/nstemplatetiers/metadata.yaml" {
 			continue
 		}
-		content, err := assets.Asset(name)
+
+		parts := strings.Split(name, "/")
+		// skip any name that does not have 4 parts
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: fbm3307
body: i updated the function PTAL
created_at: 2025-04-21T11:08:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -51,6 +52,9 @@ func roles(_ string) []string {
 	return []string{"admin"}
 }
 
+func TestMo(t *testing.T) {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go
user: login: metlos
body: Some leftover?
created_at: 2025-04-22T16:28:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,3 +40,45 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: metlos
body: Maybe add some comment here about the assumptions on the directory structure in the supplied FS?

Also, if the tests were in the same package as this file, this function could be private which again would improve the code cohesion and lower the possibility of misuse.
created_at: 2025-04-22T16:30:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2,42 +2,30 @@ package nstemplatetiers
 
 import (
 	"context"
+	"embed"
+	"fmt"
+	"strings"
 
-	"github.com/codeready-toolchain/host-operator/pkg/templates/assets"
+	"github.com/codeready-toolchain/host-operator/deploy"
+	"github.com/codeready-toolchain/host-operator/pkg/templates"
 	commonclient "github.com/codeready-toolchain/toolchain-common/pkg/client"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template/nstemplatetiers"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-
 	"github.com/pkg/errors"
 	"gopkg.in/yaml.v2"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
 	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
+const NSTemplateTierRootDir = "templates/nstemplatetiers"
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: metlos
body: This can be private.
created_at: 2025-04-22T16:32:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -51,6 +52,9 @@ func roles(_ string) []string {
 	return []string{"admin"}
 }
 
+func TestMo(t *testing.T) {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator_test.go
user: login: fbm3307
body: yes, removed it now 
created_at: 2025-04-23T06:53:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,3 +40,45 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: fbm3307
body: added the comments.
i did try to make the func private but the test package didnt pick it up. and hence kept it as is
created_at: 2025-04-23T07:20:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,3 +40,45 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: metlos
body: The reason why the test didn't see the function when you made it private is because of the fact that, for some reason that I don't fully understand, the test lives in another package, `nstemplatetier_test`, than the code itself - `nstemplatetier`. If the test file declared the same package as the the code, you'd be able to test the private functions, too.


created_at: 2025-04-23T13:08:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,3 +40,45 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: fbm3307
body: yes but  the reason, we cannot do that is, the import cycles aren't allowed in test, and we do need them in our test so we cannot have the same package. thats my understanding,
created_at: 2025-04-23T13:16:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -33,10 +33,7 @@ USERTEMPLATES_TEST_BASEDIR = test/templates/usertiers
 REGISTRATION_SERVICE_DIR=deploy/registration-service
 
 .PHONY: generate
-generate: install-go-bindata generate-metadata generate-assets 
-
-install-go-bindata:
-	@go install github.com/go-bindata/go-bindata/...
+generate: generate-metadata 
path: make/go.mk
user: login: mfrancisc
body: I'm wondering if we still need this ? I though this was specific to the go bindata flow .
created_at: 2025-04-24T11:47:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -33,10 +33,7 @@ USERTEMPLATES_TEST_BASEDIR = test/templates/usertiers
 REGISTRATION_SERVICE_DIR=deploy/registration-service
 
 .PHONY: generate
-generate: install-go-bindata generate-metadata generate-assets 
-
-install-go-bindata:
-	@go install github.com/go-bindata/go-bindata/...
+generate: generate-metadata 
path: make/go.mk
user: login: mfrancisc
body: Ok I see now. We will still be needing this as it contains the commit sha which is needed when generating the tiers. 
created_at: 2025-04-24T11:57:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,129 @@
+apiVersion: template.openshift.io/v1
path: pkg/templates/nstemplatetiers/testtemplates/fakenstemplatetiers/test/cluster.yaml
user: login: mfrancisc
body: minor - do we need to have this nested folders or could it just be :
`testtemplates/testtier/cluster|ns_dev|spacerole_admin|tier.yaml` 

I mean removing the `fakenstemplatetier` folder.
created_at: 2025-04-24T14:05:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -33,10 +33,7 @@ USERTEMPLATES_TEST_BASEDIR = test/templates/usertiers
 REGISTRATION_SERVICE_DIR=deploy/registration-service
 
 .PHONY: generate
-generate: install-go-bindata generate-metadata generate-assets 
-
-install-go-bindata:
-	@go install github.com/go-bindata/go-bindata/...
+generate: generate-metadata 
path: make/go.mk
user: login: fbm3307
body: it is not related to gobin data and its required to get the commit sha. 
created_at: 2025-04-25T05:46:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,129 @@
+apiVersion: template.openshift.io/v1
path: pkg/templates/nstemplatetiers/testtemplates/fakenstemplatetiers/test/cluster.yaml
user: login: fbm3307
body: so the logic is such that in it does require 4 step folder structure as it in deploy/ for actual prod . thats how we are getting the path. and hence this structure requires `faketesttemplatetier` . so for test cases we need to have same folder structure. 
https://github.com/codeready-toolchain/host-operator/pull/1165/files#diff-75fb59490e920da60f86dae8e59891d725272ef82d0515a32447036e0b576e40R44 
created_at: 2025-04-25T05:52:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,3 +40,47 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+// LoadFiles takes the file from deploy/nstemplatetiers/<tiername>/<yaml file name> . the folder structure should be 4 steps .
+// as the cologic here is written accordingly
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
+	// load templates from assets
+	metadataContent, err := nsTemplateTiers.ReadFile(root + "/metadata.yaml")
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	metadata = make(map[string]string)
+	err = yaml.Unmarshal(metadataContent, &metadata)
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	paths, err := templates.GetAllFileNames(&nsTemplateTiers, root)
+	if err != nil {
+		return nil, nil, err
+	}
+	if len(paths) == 0 {
+		return nil, nil, fmt.Errorf("could not find any ns templates")
+	}
+	files = make(map[string][]byte)
+	for _, name := range paths {
+		if name == root+"/metadata.yaml" {
+			continue
+		}
+
+		parts := strings.Split(name, "/")
+		// skip any name that does not have 4 parts
+		if len(parts) != 4 {
+			return nil, nil, fmt.Errorf("unable to load templates: invalid name format for file '%s'", name)
+		}
+
+		fileName := parts[2] + "/" + parts[3]
+		content, err := deploy.NSTemplateTiersFS.ReadFile(name)
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: mfrancisc
body: shouldn't this be"
```suggestion
		content, err := nsTemplateTiers.ReadFile(name)
```
created_at: 2025-04-28T12:27:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,129 @@
+apiVersion: template.openshift.io/v1
path: pkg/templates/nstemplatetiers/testtemplates/fakenstemplatetiers/test/cluster.yaml
user: login: mfrancisc
body: yeah, it's a bit weird but not sure how we can make it more flexible.
created_at: 2025-04-28T12:27:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,3 +40,47 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+// LoadFiles takes the file from deploy/nstemplatetiers/<tiername>/<yaml file name> . the folder structure should be 4 steps .
+// as the cologic here is written accordingly
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
+	// load templates from assets
+	metadataContent, err := nsTemplateTiers.ReadFile(root + "/metadata.yaml")
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	metadata = make(map[string]string)
+	err = yaml.Unmarshal(metadataContent, &metadata)
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	paths, err := templates.GetAllFileNames(&nsTemplateTiers, root)
+	if err != nil {
+		return nil, nil, err
+	}
+	if len(paths) == 0 {
+		return nil, nil, fmt.Errorf("could not find any ns templates")
+	}
+	files = make(map[string][]byte)
+	for _, name := range paths {
+		if name == root+"/metadata.yaml" {
+			continue
+		}
+
+		parts := strings.Split(name, "/")
+		// skip any name that does not have 4 parts
+		if len(parts) != 4 {
+			return nil, nil, fmt.Errorf("unable to load templates: invalid name format for file '%s'", name)
+		}
+
+		fileName := parts[2] + "/" + parts[3]
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: mfrancisc
body: minor - I was wondering if we could have this as part of the `GetAllFileNames` ( and maybe it rename it to `GetAllTemplateNames` ), but I guess the `nsTemplateTiers.ReadFile(name)` would than fail. 

Or if there's a way using `nsTemplateTiers.ReadDir("templates/nstemplatetiers")` to just pass the content of the directory, so that we don't have to worry about the 4 parts . As all the other logic requires 2 parts only, see : https://github.com/codeready-toolchain/toolchain-common/blob/c8834962f739648258091d29f9dfe916f9bdadcb/pkg/template/nstemplatetiers/nstemplatetier_generator.go#L150-L152

So ideally also this logic should require 2 parts and not 4. 
WDYT?  

created_at: 2025-04-28T12:32:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,3 +40,47 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+// LoadFiles takes the file from deploy/nstemplatetiers/<tiername>/<yaml file name> . the folder structure should be 4 steps .
+// as the cologic here is written accordingly
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
+	// load templates from assets
+	metadataContent, err := nsTemplateTiers.ReadFile(root + "/metadata.yaml")
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	metadata = make(map[string]string)
+	err = yaml.Unmarshal(metadataContent, &metadata)
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	paths, err := templates.GetAllFileNames(&nsTemplateTiers, root)
+	if err != nil {
+		return nil, nil, err
+	}
+	if len(paths) == 0 {
+		return nil, nil, fmt.Errorf("could not find any ns templates")
+	}
+	files = make(map[string][]byte)
+	for _, name := range paths {
+		if name == root+"/metadata.yaml" {
+			continue
+		}
+
+		parts := strings.Split(name, "/")
+		// skip any name that does not have 4 parts
+		if len(parts) != 4 {
+			return nil, nil, fmt.Errorf("unable to load templates: invalid name format for file '%s'", name)
+		}
+
+		fileName := parts[2] + "/" + parts[3]
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: fbm3307
body: @mfrancisc as discussed i did give it a try.. the `.ReadDir()` returns the name object as 3 parts. so probably it will be almost the same logic as now. 
i do understand there might be some extra layer of logic as in toolchain-common with the current code. but i didnt want to change the logic in toolchain common as its being used by konflux and might introduce some error, and hence did the change here.
Also the generation logic is something we will move away from. 
so i guess keeping 4 parts as is now would be okay?
created_at: 2025-04-30T11:33:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -9,7 +9,6 @@ tmp/
 
 # generated files
 deploy/**/metadata.yaml
-*_assets.go
path: .gitignore
user: login: MatousJobanek
body: do not remove this - it already causes unexpected changes included in the commit in member operator.
created_at: 2025-05-07T09:14:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -9,7 +9,6 @@ tmp/
 
 # generated files
 deploy/**/metadata.yaml
-*_assets.go
path: .gitignore
user: login: fbm3307
body: Going to revert this,
 but i didn't understand it completely, did you mean, it caused some changes since i removed it from member operator, (may be add them in member operator too) so i don't remove it from here..?
created_at: 2025-05-07T09:38:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -9,7 +9,6 @@ tmp/
 
 # generated files
 deploy/**/metadata.yaml
-*_assets.go
path: .gitignore
user: login: MatousJobanek
body: the `template_assets.go` files are already present in our projects locally. When you removed the .gitignore line, then git automatically picks these files and adds them to the commit. We have to manually delete the `template_assets.go` files to make sure that they are not included in the commit. This is not obvious and a bit tedious work.
created_at: 2025-05-07T10:07:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,3 +40,47 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+// LoadFiles takes the file from deploy/nstemplatetiers/<tiername>/<yaml file name> . the folder structure should be 4 steps .
+// as the cologic here is written accordingly
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
+	// load templates from assets
+	metadataContent, err := nsTemplateTiers.ReadFile(root + "/metadata.yaml")
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	metadata = make(map[string]string)
+	err = yaml.Unmarshal(metadataContent, &metadata)
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	paths, err := templates.GetAllFileNames(&nsTemplateTiers, root)
+	if err != nil {
+		return nil, nil, err
+	}
+	if len(paths) == 0 {
+		return nil, nil, fmt.Errorf("could not find any ns templates")
+	}
+	files = make(map[string][]byte)
+	for _, name := range paths {
+		if name == root+"/metadata.yaml" {
+			continue
+		}
+
+		parts := strings.Split(name, "/")
+		// skip any name that does not have 4 parts
+		if len(parts) != 4 {
+			return nil, nil, fmt.Errorf("unable to load templates: invalid name format for file '%s'", name)
+		}
+
+		fileName := parts[2] + "/" + parts[3]
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: mfrancisc
body: Thanks for looking into it. Yeah I agree it's fine to keep it as is for now, and later refactor the toolchain common logic maybe.
created_at: 2025-05-07T13:19:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,3 +40,47 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+// LoadFiles takes the file from deploy/nstemplatetiers/<tiername>/<yaml file name> . the folder structure should be 4 steps .
+// as the cologic here is written accordingly
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
+	// load templates from assets
+	metadataContent, err := nsTemplateTiers.ReadFile(root + "/metadata.yaml")
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	metadata = make(map[string]string)
+	err = yaml.Unmarshal(metadataContent, &metadata)
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	paths, err := templates.GetAllFileNames(&nsTemplateTiers, root)
+	if err != nil {
+		return nil, nil, err
+	}
+	if len(paths) == 0 {
+		return nil, nil, fmt.Errorf("could not find any ns templates")
+	}
+	files = make(map[string][]byte)
+	for _, name := range paths {
+		if name == root+"/metadata.yaml" {
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: MatousJobanek
body: I would say that using `filepath.Join` is the best practice for constructing the paths
```suggestion
		if name == filepath.Join(root, "metadata.yaml") {
```
created_at: 2025-05-08T07:27:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,3 +40,47 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+// LoadFiles takes the file from deploy/nstemplatetiers/<tiername>/<yaml file name> . the folder structure should be 4 steps .
+// as the cologic here is written accordingly
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
+	// load templates from assets
+	metadataContent, err := nsTemplateTiers.ReadFile(root + "/metadata.yaml")
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	metadata = make(map[string]string)
+	err = yaml.Unmarshal(metadataContent, &metadata)
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unable to load templates")
+	}
+
+	paths, err := templates.GetAllFileNames(&nsTemplateTiers, root)
+	if err != nil {
+		return nil, nil, err
+	}
+	if len(paths) == 0 {
+		return nil, nil, fmt.Errorf("could not find any ns templates")
+	}
+	files = make(map[string][]byte)
+	for _, name := range paths {
+		if name == root+"/metadata.yaml" {
+			continue
+		}
+
+		parts := strings.Split(name, "/")
+		// skip any name that does not have 4 parts
+		if len(parts) != 4 {
+			return nil, nil, fmt.Errorf("unable to load templates: invalid name format for file '%s'", name)
+		}
+
+		fileName := parts[2] + "/" + parts[3]
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: MatousJobanek
body: ```suggestion
		filepath.Join(parts[2], parts[3])
```
created_at: 2025-05-08T07:35:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,3 +40,47 @@ func CreateOrUpdateResources(ctx context.Context, s *runtime.Scheme, client runt
 		return applyCl.ApplyObject(ctx, toEnsure, commonclient.ForceUpdate(true))
 	}, namespace, metadata, files)
 }
+
+// LoadFiles takes the file from deploy/nstemplatetiers/<tiername>/<yaml file name> . the folder structure should be 4 steps .
+// as the cologic here is written accordingly
+func LoadFiles(nsTemplateTiers embed.FS, root string) (metadata map[string]string, files map[string][]byte, err error) {
+	// load templates from assets
+	metadataContent, err := nsTemplateTiers.ReadFile(root + "/metadata.yaml")
path: pkg/templates/nstemplatetiers/nstemplatetier_generator.go
user: login: MatousJobanek
body: ```suggestion
	metadataContent, err := nsTemplateTiers.ReadFile(filepath.Join(root, "metadata.yaml"))
```
created_at: 2025-05-08T07:37:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,129 @@
+apiVersion: template.openshift.io/v1
+kind: Template
+metadata:
+  name: test-cluster-resources
+objects:
+- apiVersion: quota.openshift.io/v1
+  kind: ClusterResourceQuota
+  metadata:
+    name: for-${SPACE_NAME}-deployments
+  spec:
+    quota:
+      hard:
+        count/deployments.apps: "30"
+        count/deploymentconfigs.apps: "30"
+        count/pods: "50"
+        count/virtualmachines.kubevirt.io: "2"
+    selector:
+      annotations: null
+      labels:
+        matchLabels:
+          toolchain.dev.openshift.com/space: ${SPACE_NAME}
path: pkg/templates/nstemplatetiers/testtemplates/fakenstemplatetiers/test/cluster.yaml
user: login: MatousJobanek
body: could you please point me to the place where these templates are currently used/defined? I cannot find anything corresponding to it.
My question is where (and why) did we get these files.
created_at: 2025-05-08T07:44:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,129 @@
+apiVersion: template.openshift.io/v1
+kind: Template
+metadata:
+  name: test-cluster-resources
+objects:
+- apiVersion: quota.openshift.io/v1
+  kind: ClusterResourceQuota
+  metadata:
+    name: for-${SPACE_NAME}-deployments
+  spec:
+    quota:
+      hard:
+        count/deployments.apps: "30"
+        count/deploymentconfigs.apps: "30"
+        count/pods: "50"
+        count/virtualmachines.kubevirt.io: "2"
+    selector:
+      annotations: null
+      labels:
+        matchLabels:
+          toolchain.dev.openshift.com/space: ${SPACE_NAME}
path: pkg/templates/nstemplatetiers/testtemplates/fakenstemplatetiers/test/cluster.yaml
user: login: fbm3307
body: as discussed removed those files
created_at: 2025-05-08T12:01:01Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-05-08T12:17:15Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=codeready-toolchain_host-operator&pullRequest=1165) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=codeready-toolchain_host-operator&pullRequest=1165&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=codeready-toolchain_host-operator&pullRequest=1165&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=codeready-toolchain_host-operator&pullRequest=1165&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=codeready-toolchain_host-operator&pullRequest=1165&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=codeready-toolchain_host-operator&pullRequest=1165&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=codeready-toolchain_host-operator&pullRequest=1165)


author_association: NONE
user: login: openshift-ci[bot]
created_at: 2025-05-12T07:57:03Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/codeready-toolchain/host-operator/pull/1165#" title="Author self-approved">fbm3307</a>*, *<a href="https://github.com/codeready-toolchain/host-operator/pull/1165#pullrequestreview-2832200209" title="Approved">MatousJobanek</a>*, *<a href="https://github.com/codeready-toolchain/host-operator/pull/1165#pullrequestreview-2814483929" title="Approved">metlos</a>*, *<a href="https://github.com/codeready-toolchain/host-operator/pull/1165#pullrequestreview-2821726466" title="Approved">mfrancisc</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=codeready-toolchain%2Fhost-operator).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/codeready-toolchain/host-operator/blob/master/OWNERS)~~ [MatousJobanek,fbm3307,metlos,mfrancisc]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: NONE
user: login: codecov[bot]
created_at: 2025-05-12T08:08:24Z
body: ## [Codecov](https://app.codecov.io/gh/codeready-toolchain/host-operator/pull/1165?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain) Report
Attention: Patch coverage is `62.00000%` with `19 lines` in your changes missing coverage. Please review.
> Project coverage is 83.39%. Comparing base [(`6ef6493`)](https://app.codecov.io/gh/codeready-toolchain/host-operator/commit/6ef649333bbd7d313ff7ce8476a74046ad387706?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain) to head [(`886d09d`)](https://app.codecov.io/gh/codeready-toolchain/host-operator/commit/886d09d57a89d124f128f64cc2243f60fd01b8e9?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain).
> Report is 1 commits behind head on master.

| [Files with missing lines](https://app.codecov.io/gh/codeready-toolchain/host-operator/pull/1165?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain) | Patch % | Lines |
|---|---|---|
| [...plates/nstemplatetiers/nstemplatetier\_generator.go](https://app.codecov.io/gh/codeready-toolchain/host-operator/pull/1165?src=pr&el=tree&filepath=pkg%2Ftemplates%2Fnstemplatetiers%2Fnstemplatetier_generator.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain#diff-cGtnL3RlbXBsYXRlcy9uc3RlbXBsYXRldGllcnMvbnN0ZW1wbGF0ZXRpZXJfZ2VuZXJhdG9yLmdv) | 56.75% | [11 Missing and 5 partials :warning: ](https://app.codecov.io/gh/codeready-toolchain/host-operator/pull/1165?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain) |
| [pkg/templates/resources.go](https://app.codecov.io/gh/codeready-toolchain/host-operator/pull/1165?src=pr&el=tree&filepath=pkg%2Ftemplates%2Fresources.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain#diff-cGtnL3RlbXBsYXRlcy9yZXNvdXJjZXMuZ28=) | 75.00% | [3 Missing :warning: ](https://app.codecov.io/gh/codeready-toolchain/host-operator/pull/1165?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain) |

:x: Your changes status has failed because you have indirect coverage changes. Learn more about [Unexpected Coverage Changes](https://docs.codecov.com/docs/unexpected-coverage-changes) and [reasons for indirect coverage changes](https://docs.codecov.com/docs/unexpected-coverage-changes#reasons-for-indirect-changes).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #1165      +/-   ##
==========================================
- Coverage   83.54%   83.39%   -0.16%     
==========================================
  Files          82       82              
  Lines        7927     7941      +14     
==========================================
- Hits         6623     6622       -1     
- Misses       1103     1113      +10     
- Partials      201      206       +5     
```

| [Files with missing lines](https://app.codecov.io/gh/codeready-toolchain/host-operator/pull/1165?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain) | Coverage Δ | |
|---|---|---|
| [pkg/templates/usertiers/usertier\_generator.go](https://app.codecov.io/gh/codeready-toolchain/host-operator/pull/1165?src=pr&el=tree&filepath=pkg%2Ftemplates%2Fusertiers%2Fusertier_generator.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain#diff-cGtnL3RlbXBsYXRlcy91c2VydGllcnMvdXNlcnRpZXJfZ2VuZXJhdG9yLmdv) | `65.00% <100.00%> (-0.79%)` | :arrow_down: |
| [pkg/templates/resources.go](https://app.codecov.io/gh/codeready-toolchain/host-operator/pull/1165?src=pr&el=tree&filepath=pkg%2Ftemplates%2Fresources.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain#diff-cGtnL3RlbXBsYXRlcy9yZXNvdXJjZXMuZ28=) | `75.00% <75.00%> (ø)` | |
| [...plates/nstemplatetiers/nstemplatetier\_generator.go](https://app.codecov.io/gh/codeready-toolchain/host-operator/pull/1165?src=pr&el=tree&filepath=pkg%2Ftemplates%2Fnstemplatetiers%2Fnstemplatetier_generator.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain#diff-cGtnL3RlbXBsYXRlcy9uc3RlbXBsYXRldGllcnMvbnN0ZW1wbGF0ZXRpZXJfZ2VuZXJhdG9yLmdv) | `62.50% <56.75%> (-27.16%)` | :arrow_down: |

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE