Repository: codeready-toolchain/member-operator
Pull request #666: KUBESAW-143: Add Support for Go Templates
Author: login: fbm3307
Labels: name: approved
Created: 2025-04-28T05:53:45Z | Merged: 2025-05-22T05:58:28Z

This PR is the 1st PR in the series. 
This just contains the logic on fetching the TTR and fallback to the current logic if not found.

other  steps will follow this PR 


Diff:
diff --git a/controllers/nstemplateset/cluster_resources.go b/controllers/nstemplateset/cluster_resources.go
index bbb20eebf..d0962e91a 100644
--- a/controllers/nstemplateset/cluster_resources.go
+++ b/controllers/nstemplateset/cluster_resources.go
@@ -2,6 +2,7 @@ package nstemplateset
 
 import (
 	"context"
+
 	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
 	applycl "github.com/codeready-toolchain/toolchain-common/pkg/client"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template"
diff --git a/controllers/nstemplateset/nstemplatetier.go b/controllers/nstemplateset/nstemplatetier.go
index 1c937e24a..ced894dd6 100644
--- a/controllers/nstemplateset/nstemplatetier.go
+++ b/controllers/nstemplateset/nstemplatetier.go
@@ -11,37 +11,58 @@ import (
 	"github.com/codeready-toolchain/toolchain-common/pkg/cluster"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template"
 	templatev1 "github.com/openshift/api/template/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
-
 	"github.com/pkg/errors"
+	errs "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
+	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 var tierTemplatesCache = newTierTemplateCache()
 
-// getTierTemplate retrieves the TierTemplate resource with the given name from the host cluster
+// getTierTemplate retrieves the TierTemplateRevision resource with the given name from the host cluster,
+// if not found then falls back to the current logic of retrieving the TierTemplate
 // and returns an instance of the tierTemplate type for it whose template content can be parsable.
 // The returned tierTemplate contains all data from TierTemplate including its name.
 func getTierTemplate(ctx context.Context, hostClusterFunc cluster.GetHostClusterFunc, templateRef string) (*tierTemplate, error) {
+	var tierTmpl *tierTemplate
 	if templateRef == "" {
-		return nil, fmt.Errorf("templateRef is not provided - it's not possible to fetch related TierTemplate resource")
+		return nil, fmt.Errorf("templateRef is not provided - it's not possible to fetch related TierTemplate/TierTemplateRevision resource")
 	}
+
 	if tierTmpl, ok := tierTemplatesCache.get(templateRef); ok && tierTmpl != nil {
 		return tierTmpl, nil
 	}
-	tmpl, err := getToolchainTierTemplate(ctx, hostClusterFunc, templateRef)
+	ttr, err := getTierTemplateRevision(ctx, hostClusterFunc, templateRef)
 	if err != nil {
-		return nil, err
-	}
-	tierTmpl := &tierTemplate{
-		templateRef: templateRef,
-		tierName:    tmpl.Spec.TierName,
-		typeName:    tmpl.Spec.Type,
-		template:    tmpl.Spec.Template,
+		if errs.IsNotFound(err) {
+			tmpl, err := getToolchainTierTemplate(ctx, hostClusterFunc, templateRef)
+			if err != nil {
+				return nil, err
+			}
+			tierTmpl = &tierTemplate{
+				templateRef: templateRef,
+				tierName:    tmpl.Spec.TierName,
+				typeName:    tmpl.Spec.Type,
+				template:    tmpl.Spec.Template,
+			}
+		} else {
+			return nil, err
+		}
+	} else {
+		ttrTmpl, err := getToolchainTierTemplate(ctx, hostClusterFunc, ttr.GetLabels()[toolchainv1alpha1.TemplateRefLabelKey])
+		if err != nil {
+			return nil, err
+		}
+		tierTmpl = &tierTemplate{
+			templateRef: templateRef,
+			tierName:    ttrTmpl.Spec.TierName,
+			typeName:    ttrTmpl.Spec.Type,
+			ttr:         ttr,
+		}
 	}
-	tierTemplatesCache.add(tierTmpl)
 
+	tierTemplatesCache.add(tierTmpl)
 	return tierTmpl, nil
 }
 
@@ -55,7 +76,6 @@ func getToolchainTierTemplate(ctx context.Context, hostClusterFunc cluster.GetHo
 	if !cluster.IsReady(host.ClusterStatus) {
 		return nil, fmt.Errorf("the host cluster is not ready")
 	}
-
 	tierTemplate := &toolchainv1alpha1.TierTemplate{}
 	err := host.Client.Get(ctx, types.NamespacedName{
 		Namespace: host.OperatorNamespace,
@@ -73,6 +93,7 @@ type tierTemplate struct {
 	tierName    string
 	typeName    string
 	template    templatev1.Template
+	ttr         *toolchainv1alpha1.TierTemplateRevision
 }
 
 const (
diff --git a/controllers/nstemplateset/nstemplatetier_test.go b/controllers/nstemplateset/nstemplatetier_test.go
index 880d75e51..9e4563bb3 100644
--- a/controllers/nstemplateset/nstemplatetier_test.go
+++ b/controllers/nstemplateset/nstemplatetier_test.go
@@ -5,13 +5,12 @@ import (
 	"sync"
 	"testing"
 
-	commonconfig "github.com/codeready-toolchain/toolchain-common/pkg/configuration"
-	"k8s.io/apimachinery/pkg/runtime/serializer"
-
 	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
 	"github.com/codeready-toolchain/member-operator/pkg/apis"
 	"github.com/codeready-toolchain/member-operator/test"
+	commonconfig "github.com/codeready-toolchain/toolchain-common/pkg/configuration"
 	testcommon "github.com/codeready-toolchain/toolchain-common/pkg/test"
+	"k8s.io/apimachinery/pkg/runtime/serializer"
 
 	templatev1 "github.com/openshift/api/template/v1"
 	"github.com/stretchr/testify/assert"
@@ -110,6 +109,24 @@ func TestGetTierTemplate(t *testing.T) {
 	cl := testcommon.NewFakeClient(t, basicTierCode, basicTierDev, basicTierStage, basicTierCluster, advancedTierCode, advancedTierDev, advancedTierStage, other)
 	ctx := context.TODO()
 
+	t.Run("fetch ttr successfully and add it to the tiertemplate object", func(t *testing.T) {
+		// given
+		ttRev := createTierTemplateRevision("basic-clusterresources-aa11bb22")
+		ttRev.Labels = map[string]string{
+			toolchainv1alpha1.TierLabelKey:        "basic",
+			toolchainv1alpha1.TemplateRefLabelKey: "basic-clusterresources-aa11bb22",
+		}
+		ctx := context.TODO()
+		cl := testcommon.NewFakeClient(t, ttRev, basicTierCluster)
+		hostCluster := test.NewGetHostCluster(cl, true, apiv1.ConditionTrue)
+		//when
+		ttrTmpl, err := getTierTemplate(ctx, hostCluster, "basic-clusterresources-aa11bb22")
+
+		//then
+		require.NoError(t, err)
+		assert.Equal(t, ttrTmpl.ttr, ttRev)
+
+	})
 	t.Run("return code for basic tier", func(t *testing.T) {
 		// given
 		hostCluster := test.NewGetHostCluster(cl, true, apiv1.ConditionTrue)
@@ -261,7 +278,7 @@ func TestGetTierTemplate(t *testing.T) {
 			_, err := getTierTemplate(ctx, hostCluster, "")
 			// then
 			require.Error(t, err)
-			assert.Contains(t, err.Error(), "templateRef is not provided - it's not possible to fetch related TierTemplate resource")
+			assert.Contains(t, err.Error(), "templateRef is not provided - it's not possible to fetch related TierTemplate/TierTemplateRevision resource")
 		})
 	})
 
diff --git a/controllers/nstemplateset/tier_template_revision.go b/controllers/nstemplateset/tier_template_revision.go
new file mode 100644
index 000000000..b5322796f
--- /dev/null
+++ b/controllers/nstemplateset/tier_template_revision.go
@@ -0,0 +1,34 @@
+package nstemplateset
+
+import (
+	"context"
+	"fmt"
+
+	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
+	"github.com/codeready-toolchain/toolchain-common/pkg/cluster"
+
+	"github.com/pkg/errors"
+	"k8s.io/apimachinery/pkg/types"
+)
+
+// getTierTemplateRevision gets the TierTemplateRevision resource from the host cluster.
+func getTierTemplateRevision(ctx context.Context, hostClusterFunc cluster.GetHostClusterFunc, templateRef string) (*toolchainv1alpha1.TierTemplateRevision, error) {
+	// retrieve the ToolchainCluster instance representing the host cluster
+	host, ok := hostClusterFunc()
+	if !ok {
+		return nil, fmt.Errorf("unable to connect to the host cluster: unknown cluster")
+	}
+	if !cluster.IsReady(host.ClusterStatus) {
+		return nil, fmt.Errorf("the host cluster is not ready")
+	}
+
+	tierTemplateRevision := &toolchainv1alpha1.TierTemplateRevision{}
+	err := host.Client.Get(ctx, types.NamespacedName{
+		Namespace: host.OperatorNamespace,
+		Name:      templateRef,
+	}, tierTemplateRevision)
+	if err != nil {
+		return nil, errors.Wrapf(err, "unable to retrieve the TierTemplateRevision '%s' from 'Host' cluster", templateRef)
+	}
+	return tierTemplateRevision, nil
+}
diff --git a/controllers/nstemplateset/tier_template_revision_test.go b/controllers/nstemplateset/tier_template_revision_test.go
new file mode 100644
index 000000000..1fc074938
--- /dev/null
+++ b/controllers/nstemplateset/tier_template_revision_test.go
@@ -0,0 +1,79 @@
+package nstemplateset
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
+	testmem "github.com/codeready-toolchain/member-operator/test"
+	"github.com/codeready-toolchain/toolchain-common/pkg/test"
+	"github.com/stretchr/testify/require"
+	apiv1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+func createTierTemplateRevision(templateRef string) *toolchainv1alpha1.TierTemplateRevision {
+	return &toolchainv1alpha1.TierTemplateRevision{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      templateRef,
+			Namespace: test.HostOperatorNs,
+		},
+	}
+}
+
+func TestGetTTR(t *testing.T) {
+	// given
+	ttRev := createTierTemplateRevision("test-ttr")
+	ttRevTest := createTierTemplateRevision("test-ttr-abc")
+	ttRevExtra := createTierTemplateRevision("test-ttr-def")
+	ctx := context.TODO()
+	cl := test.NewFakeClient(t, ttRev, ttRevExtra, ttRevTest)
+	hostCluster := testmem.NewGetHostCluster(cl, true, apiv1.ConditionTrue)
+	t.Run("fetch ttr successfully", func(t *testing.T) {
+		//when
+		ttr, err := getTierTemplateRevision(ctx, hostCluster, "test-ttr")
+
+		//then
+		require.NoError(t, err)
+		require.Equal(t, ttRev, ttr)
+	})
+
+	t.Run("host cluster not ready", func(t *testing.T) {
+		//given
+		hostCluster := testmem.NewGetHostCluster(cl, true, apiv1.ConditionFalse)
+		//when
+		_, err := getTierTemplateRevision(ctx, hostCluster, "test-ttr")
+		//then
+		require.EqualError(t, err, "the host cluster is not ready")
+
+	})
+
+	t.Run("host cluster not ok", func(t *testing.T) {
+		noCluster := testmem.NewGetHostCluster(cl, false, apiv1.ConditionFalse)
+		//when
+		_, err := getTierTemplateRevision(ctx, noCluster, "test-ttr")
+		//then
+		require.EqualError(t, err, "unable to connect to the host cluster: unknown cluster")
+
+	})
+
+	t.Run("error while fetching ttr", func(t *testing.T) {
+		//given
+		cl.MockGet = func(ctx context.Context, key types.NamespacedName, obj runtimeclient.Object, opts ...runtimeclient.GetOption) error {
+			if _, ok := obj.(*toolchainv1alpha1.TierTemplateRevision); ok {
+				return fmt.Errorf("mock error")
+			}
+			return cl.Client.Get(ctx, key, obj, opts...)
+		}
+		//when
+		_, err := getTierTemplateRevision(ctx, hostCluster, "test-ttr")
+
+		//then
+		require.EqualError(t, err, "unable to retrieve the TierTemplateRevision 'test-ttr' from 'Host' cluster: mock error")
+
+	})
+
+}


Reviews:
user: login: MatousJobanek
body: Thanks for the changes, could you please update the unit tests accordingly?
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T08:10:51Z
user: login: metlos
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T13:47:23Z
user: login: mfrancisc
body: Looks Good 👍 

In addition to Lukas review, I have only few minor comments.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T14:32:35Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T07:32:37Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T08:52:14Z
user: login: metlos
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T11:49:13Z
user: login: MatousJobanek
body: :+1: 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T09:44:08Z
user: login: mfrancisc
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T11:48:41Z
user: login: mfrancisc
body: Nice Job 👍 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T11:48:58Z
user: login: fbm3307
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:54:47Z

Review comments:
diff_hunk: @@ -0,0 +1,34 @@
+package nstemplateset
+
+import (
+	"context"
+	"fmt"
+
+	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
+	"github.com/codeready-toolchain/toolchain-common/pkg/cluster"
+
+	"github.com/pkg/errors"
+	"k8s.io/apimachinery/pkg/types"
+)
+
+// getTierTemplateRevision gets the TierTemplateRevision resource from the host cluster.
path: controllers/nstemplateset/tier_template_revision.go
user: login: metlos
body: Nitpick: the name doesn't match the real function name (and I personally like the name without the "Toolchain" better) :)
created_at: 2025-05-14T13:42:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,37 +11,48 @@ import (
 	"github.com/codeready-toolchain/toolchain-common/pkg/cluster"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template"
 	templatev1 "github.com/openshift/api/template/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
-
 	"github.com/pkg/errors"
+	errs "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
+	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 var tierTemplatesCache = newTierTemplateCache()
 
-// getTierTemplate retrieves the TierTemplate resource with the given name from the host cluster
+// getTierTemplate retrieves the TierTemplateRevision resource with the given name from the host cluster,
+// if not found then falls back to the current logic of retrieving the TierTemplate
 // and returns an instance of the tierTemplate type for it whose template content can be parsable.
 // The returned tierTemplate contains all data from TierTemplate including its name.
 func getTierTemplate(ctx context.Context, hostClusterFunc cluster.GetHostClusterFunc, templateRef string) (*tierTemplate, error) {
+	var tmpl *toolchainv1alpha1.TierTemplate
 	if templateRef == "" {
-		return nil, fmt.Errorf("templateRef is not provided - it's not possible to fetch related TierTemplate resource")
+		return nil, fmt.Errorf("templateRef is not provided - it's not possible to fetch related TierTemplate/TierTemplateRevision resource")
 	}
+
 	if tierTmpl, ok := tierTemplatesCache.get(templateRef); ok && tierTmpl != nil {
 		return tierTmpl, nil
 	}
-	tmpl, err := getToolchainTierTemplate(ctx, hostClusterFunc, templateRef)
+	ttr, err := getToolchainTierTemplateRevision(ctx, hostClusterFunc, templateRef)
 	if err != nil {
-		return nil, err
+		if errs.IsNotFound(err) {
+			tmpl, err = getToolchainTierTemplate(ctx, hostClusterFunc, templateRef)
+			if err != nil {
+				return nil, err
+			}
+		} else {
+			return nil, err
+		}
 	}
 	tierTmpl := &tierTemplate{
 		templateRef: templateRef,
 		tierName:    tmpl.Spec.TierName,
path: controllers/nstemplateset/nstemplatetier.go
user: login: metlos
body: This will fail if the `ttr` is found, because `tmpl` will be `nil` in that case.
created_at: 2025-05-14T13:46:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,79 @@
+package nstemplateset
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
+	testmem "github.com/codeready-toolchain/member-operator/test"
+	"github.com/codeready-toolchain/toolchain-common/pkg/test"
+	"github.com/stretchr/testify/require"
+	apiv1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+func createTierTemplateRevision(templateRef string) *toolchainv1alpha1.TierTemplateRevision {
+	return &toolchainv1alpha1.TierTemplateRevision{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      templateRef,
+			Namespace: test.HostOperatorNs,
+		},
+	}
+}
+
+func TestGetTTR(t *testing.T) {
+	// given
+	ttRev := createTierTemplateRevision("test-ttr")
+	ttRevTest := createTierTemplateRevision("test-ttr-abc")
+	ttRevExtra := createTierTemplateRevision("test-ttr-def")
path: controllers/nstemplateset/tier_template_revision_test.go
user: login: mfrancisc
body: minor - those do not seem to be used , unless I'm missing something.
created_at: 2025-05-14T14:28:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,37 +11,48 @@ import (
 	"github.com/codeready-toolchain/toolchain-common/pkg/cluster"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template"
 	templatev1 "github.com/openshift/api/template/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
-
 	"github.com/pkg/errors"
+	errs "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
+	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 var tierTemplatesCache = newTierTemplateCache()
 
-// getTierTemplate retrieves the TierTemplate resource with the given name from the host cluster
+// getTierTemplate retrieves the TierTemplateRevision resource with the given name from the host cluster,
+// if not found then falls back to the current logic of retrieving the TierTemplate
 // and returns an instance of the tierTemplate type for it whose template content can be parsable.
 // The returned tierTemplate contains all data from TierTemplate including its name.
 func getTierTemplate(ctx context.Context, hostClusterFunc cluster.GetHostClusterFunc, templateRef string) (*tierTemplate, error) {
+	var tmpl *toolchainv1alpha1.TierTemplate
 	if templateRef == "" {
-		return nil, fmt.Errorf("templateRef is not provided - it's not possible to fetch related TierTemplate resource")
+		return nil, fmt.Errorf("templateRef is not provided - it's not possible to fetch related TierTemplate/TierTemplateRevision resource")
 	}
+
 	if tierTmpl, ok := tierTemplatesCache.get(templateRef); ok && tierTmpl != nil {
 		return tierTmpl, nil
 	}
-	tmpl, err := getToolchainTierTemplate(ctx, hostClusterFunc, templateRef)
+	ttr, err := getToolchainTierTemplateRevision(ctx, hostClusterFunc, templateRef)
 	if err != nil {
-		return nil, err
+		if errs.IsNotFound(err) {
+			tmpl, err = getToolchainTierTemplate(ctx, hostClusterFunc, templateRef)
+			if err != nil {
+				return nil, err
+			}
+		} else {
+			return nil, err
+		}
 	}
 	tierTmpl := &tierTemplate{
 		templateRef: templateRef,
 		tierName:    tmpl.Spec.TierName,
path: controllers/nstemplateset/nstemplatetier.go
user: login: mfrancisc
body: +1

we'll also need to add a unit test verifying that `getTierTemplate` works when ttr is present.
created_at: 2025-05-14T14:30:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,79 @@
+package nstemplateset
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
+	testmem "github.com/codeready-toolchain/member-operator/test"
+	"github.com/codeready-toolchain/toolchain-common/pkg/test"
+	"github.com/stretchr/testify/require"
+	apiv1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+func createTierTemplateRevision(templateRef string) *toolchainv1alpha1.TierTemplateRevision {
+	return &toolchainv1alpha1.TierTemplateRevision{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      templateRef,
+			Namespace: test.HostOperatorNs,
+		},
+	}
+}
+
+func TestGetTTR(t *testing.T) {
+	// given
+	ttRev := createTierTemplateRevision("test-ttr")
+	ttRevTest := createTierTemplateRevision("test-ttr-abc")
+	ttRevExtra := createTierTemplateRevision("test-ttr-def")
+	ctx := context.TODO()
+	cl := test.NewFakeClient(t, ttRev, ttRevExtra, ttRevTest)
+	hostCluster := testmem.NewGetHostCluster(cl, true, apiv1.ConditionTrue)
+	t.Run("fetch ttr successfully", func(t *testing.T) {
+		//when
+		ttr, err := getToolchainTierTemplateRevision(ctx, hostCluster, "test-ttr")
+
+		//then
+		require.NoError(t, err)
+		require.Equal(t, ttRev, ttr)
+	})
+
+	t.Run("host cluster not ready", func(t *testing.T) {
+		//given
+		hostCluster := testmem.NewGetHostCluster(cl, true, apiv1.ConditionFalse)
+		//when
+		_, err := getToolchainTierTemplateRevision(ctx, hostCluster, "test-ttr")
+		//then
+		require.EqualError(t, err, "the host cluster is not ready")
+
+	})
+
+	t.Run("host cluster not ok", func(t *testing.T) {
+		noCluster := testmem.NewGetHostCluster(cl, false, apiv1.ConditionFalse)
+		//when
+		_, err := getToolchainTierTemplateRevision(ctx, noCluster, "test-ttr")
+		//then
+		require.EqualError(t, err, "unable to connect to the host cluster: unknown cluster")
+
+	})
+
+	t.Run("error while fetching ttr", func(t *testing.T) {
+		//given
+		cl.MockGet = func(ctx context.Context, key types.NamespacedName, obj runtimeclient.Object, opts ...runtimeclient.GetOption) error {
+			if _, ok := obj.(*toolchainv1alpha1.TierTemplateRevision); ok {
+				return fmt.Errorf("mock error")
+			}
+			return cl.Client.Get(ctx, key, obj, opts...)
+		}
+		//when
+		_, err := getToolchainTierTemplateRevision(ctx, hostCluster, "test-ttr")
+
+		//then
+		require.ErrorContains(t, err, "unable to retrieve the TierTemplateRevision")
path: controllers/nstemplateset/tier_template_revision_test.go
user: login: mfrancisc
body: we should check that "mock error" is present, to be sure the error returned is the expected one.
created_at: 2025-05-14T14:32:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,79 @@
+package nstemplateset
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
+	testmem "github.com/codeready-toolchain/member-operator/test"
+	"github.com/codeready-toolchain/toolchain-common/pkg/test"
+	"github.com/stretchr/testify/require"
+	apiv1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+func createTierTemplateRevision(templateRef string) *toolchainv1alpha1.TierTemplateRevision {
+	return &toolchainv1alpha1.TierTemplateRevision{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      templateRef,
+			Namespace: test.HostOperatorNs,
+		},
+	}
+}
+
+func TestGetTTR(t *testing.T) {
+	// given
+	ttRev := createTierTemplateRevision("test-ttr")
+	ttRevTest := createTierTemplateRevision("test-ttr-abc")
+	ttRevExtra := createTierTemplateRevision("test-ttr-def")
path: controllers/nstemplateset/tier_template_revision_test.go
user: login: fbm3307
body: I just kept them to check that out of all the TTR present it matches and gets the required TTR only
created_at: 2025-05-15T07:32:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,37 +11,48 @@ import (
 	"github.com/codeready-toolchain/toolchain-common/pkg/cluster"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template"
 	templatev1 "github.com/openshift/api/template/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
-
 	"github.com/pkg/errors"
+	errs "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
+	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 var tierTemplatesCache = newTierTemplateCache()
 
-// getTierTemplate retrieves the TierTemplate resource with the given name from the host cluster
+// getTierTemplate retrieves the TierTemplateRevision resource with the given name from the host cluster,
+// if not found then falls back to the current logic of retrieving the TierTemplate
 // and returns an instance of the tierTemplate type for it whose template content can be parsable.
 // The returned tierTemplate contains all data from TierTemplate including its name.
 func getTierTemplate(ctx context.Context, hostClusterFunc cluster.GetHostClusterFunc, templateRef string) (*tierTemplate, error) {
+	var tmpl *toolchainv1alpha1.TierTemplate
 	if templateRef == "" {
-		return nil, fmt.Errorf("templateRef is not provided - it's not possible to fetch related TierTemplate resource")
+		return nil, fmt.Errorf("templateRef is not provided - it's not possible to fetch related TierTemplate/TierTemplateRevision resource")
 	}
+
 	if tierTmpl, ok := tierTemplatesCache.get(templateRef); ok && tierTmpl != nil {
 		return tierTmpl, nil
 	}
-	tmpl, err := getToolchainTierTemplate(ctx, hostClusterFunc, templateRef)
+	ttr, err := getToolchainTierTemplateRevision(ctx, hostClusterFunc, templateRef)
 	if err != nil {
-		return nil, err
+		if errs.IsNotFound(err) {
+			tmpl, err = getToolchainTierTemplate(ctx, hostClusterFunc, templateRef)
+			if err != nil {
+				return nil, err
+			}
+		} else {
+			return nil, err
+		}
 	}
 	tierTmpl := &tierTemplate{
 		templateRef: templateRef,
 		tierName:    tmpl.Spec.TierName,
path: controllers/nstemplateset/nstemplatetier.go
user: login: fbm3307
body: i have updated it  now as we discussed .PTAL
created_at: 2025-05-16T08:52:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,37 +11,59 @@ import (
 	"github.com/codeready-toolchain/toolchain-common/pkg/cluster"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template"
 	templatev1 "github.com/openshift/api/template/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
-
 	"github.com/pkg/errors"
+	errs "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
+	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 var tierTemplatesCache = newTierTemplateCache()
 
-// getTierTemplate retrieves the TierTemplate resource with the given name from the host cluster
+// getTierTemplate retrieves the TierTemplateRevision resource with the given name from the host cluster,
+// if not found then falls back to the current logic of retrieving the TierTemplate
 // and returns an instance of the tierTemplate type for it whose template content can be parsable.
 // The returned tierTemplate contains all data from TierTemplate including its name.
 func getTierTemplate(ctx context.Context, hostClusterFunc cluster.GetHostClusterFunc, templateRef string) (*tierTemplate, error) {
+	var tmpl *toolchainv1alpha1.TierTemplate
path: controllers/nstemplateset/nstemplatetier.go
user: login: MatousJobanek
body: only a detail, but this var doesn't have to be defined at this level
created_at: 2025-05-21T09:38:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,79 @@
+package nstemplateset
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
+	testmem "github.com/codeready-toolchain/member-operator/test"
+	"github.com/codeready-toolchain/toolchain-common/pkg/test"
+	"github.com/stretchr/testify/require"
+	apiv1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+func createTierTemplateRevision(templateRef string) *toolchainv1alpha1.TierTemplateRevision {
+	return &toolchainv1alpha1.TierTemplateRevision{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      templateRef,
+			Namespace: test.HostOperatorNs,
+		},
+	}
+}
+
+func TestGetTTR(t *testing.T) {
+	// given
+	ttRev := createTierTemplateRevision("test-ttr")
+	ttRevTest := createTierTemplateRevision("test-ttr-abc")
+	ttRevExtra := createTierTemplateRevision("test-ttr-def")
path: controllers/nstemplateset/tier_template_revision_test.go
user: login: mfrancisc
body: I see, thanks for explaining.
created_at: 2025-05-21T11:48:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,37 +11,59 @@ import (
 	"github.com/codeready-toolchain/toolchain-common/pkg/cluster"
 	"github.com/codeready-toolchain/toolchain-common/pkg/template"
 	templatev1 "github.com/openshift/api/template/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
-
 	"github.com/pkg/errors"
+	errs "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
+	runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 var tierTemplatesCache = newTierTemplateCache()
 
-// getTierTemplate retrieves the TierTemplate resource with the given name from the host cluster
+// getTierTemplate retrieves the TierTemplateRevision resource with the given name from the host cluster,
+// if not found then falls back to the current logic of retrieving the TierTemplate
 // and returns an instance of the tierTemplate type for it whose template content can be parsable.
 // The returned tierTemplate contains all data from TierTemplate including its name.
 func getTierTemplate(ctx context.Context, hostClusterFunc cluster.GetHostClusterFunc, templateRef string) (*tierTemplate, error) {
+	var tmpl *toolchainv1alpha1.TierTemplate
path: controllers/nstemplateset/nstemplatetier.go
user: login: fbm3307
body: removed it now , Thanks :) 
created_at: 2025-05-21T13:54:47Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-04-30T10:15:49Z
body: ## [Codecov](https://app.codecov.io/gh/codeready-toolchain/member-operator/pull/666?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 `93.61702%` with `3 lines` in your changes missing coverage. Please review.
> Project coverage is 81.23%. Comparing base [(`fe45095`)](https://app.codecov.io/gh/codeready-toolchain/member-operator/commit/fe45095db3abf87624a219e7e4d4b08e9c599075?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain) to head [(`7e1017e`)](https://app.codecov.io/gh/codeready-toolchain/member-operator/commit/7e1017eaf0123feedfaca1a3eb3f038d7f25786d?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/member-operator/pull/666?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 |
|---|---|---|
| [controllers/nstemplateset/nstemplatetier.go](https://app.codecov.io/gh/codeready-toolchain/member-operator/pull/666?src=pr&el=tree&filepath=controllers%2Fnstemplateset%2Fnstemplatetier.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain#diff-Y29udHJvbGxlcnMvbnN0ZW1wbGF0ZXNldC9uc3RlbXBsYXRldGllci5nbw==) | 89.65% | [2 Missing and 1 partial :warning: ](https://app.codecov.io/gh/codeready-toolchain/member-operator/pull/666?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     #666      +/-   ##
==========================================
- Coverage   81.28%   81.23%   -0.06%     
==========================================
  Files          47       48       +1     
  Lines        4473     4508      +35     
==========================================
+ Hits         3636     3662      +26     
- Misses        680      686       +6     
- Partials      157      160       +3     
```

| [Files with missing lines](https://app.codecov.io/gh/codeready-toolchain/member-operator/pull/666?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain) | Coverage Δ | |
|---|---|---|
| [controllers/nstemplateset/cluster\_resources.go](https://app.codecov.io/gh/codeready-toolchain/member-operator/pull/666?src=pr&el=tree&filepath=controllers%2Fnstemplateset%2Fcluster_resources.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain#diff-Y29udHJvbGxlcnMvbnN0ZW1wbGF0ZXNldC9jbHVzdGVyX3Jlc291cmNlcy5nbw==) | `83.87% <ø> (ø)` | |
| [...ontrollers/nstemplateset/tier\_template\_revision.go](https://app.codecov.io/gh/codeready-toolchain/member-operator/pull/666?src=pr&el=tree&filepath=controllers%2Fnstemplateset%2Ftier_template_revision.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain#diff-Y29udHJvbGxlcnMvbnN0ZW1wbGF0ZXNldC90aWVyX3RlbXBsYXRlX3JldmlzaW9uLmdv) | `100.00% <100.00%> (ø)` | |
| [controllers/nstemplateset/nstemplatetier.go](https://app.codecov.io/gh/codeready-toolchain/member-operator/pull/666?src=pr&el=tree&filepath=controllers%2Fnstemplateset%2Fnstemplatetier.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codeready-toolchain#diff-Y29udHJvbGxlcnMvbnN0ZW1wbGF0ZXNldC9uc3RlbXBsYXRldGllci5nbw==) | `84.21% <89.65%> (-10.71%)` | :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
user: login: fbm3307
created_at: 2025-05-13T16:04:27Z
body: > Thanks for the changes, could you please update the unit tests accordingly?

updated it
author_association: CONTRIBUTOR
user: login: fbm3307
created_at: 2025-05-14T07:38:20Z
body: /test e2e
author_association: CONTRIBUTOR
user: login: fbm3307
created_at: 2025-05-14T10:00:56Z
body: /retest
author_association: CONTRIBUTOR
user: login: openshift-ci[bot]
created_at: 2025-05-21T11:49:02Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/codeready-toolchain/member-operator/pull/666#" title="Author self-approved">fbm3307</a>*, *<a href="https://github.com/codeready-toolchain/member-operator/pull/666#pullrequestreview-2857022555" title="Approved">MatousJobanek</a>*, *<a href="https://github.com/codeready-toolchain/member-operator/pull/666#pullrequestreview-2846394525" title="Approved">metlos</a>*, *<a href="https://github.com/codeready-toolchain/member-operator/pull/666#pullrequestreview-2857395105" 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%2Fmember-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/member-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: sonarqubecloud[bot]
created_at: 2025-05-21T13:53:55Z
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_member-operator&pullRequest=666) **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_member-operator&pullRequest=666&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_member-operator&pullRequest=666&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_member-operator&pullRequest=666&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_member-operator&pullRequest=666&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.6% Duplication on New Code](https://sonarcloud.io/component_measures?id=codeready-toolchain_member-operator&pullRequest=666&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=codeready-toolchain_member-operator&pullRequest=666)


author_association: NONE