Repository: vmware-tanzu/tanzu-framework (language: go)

Issue:
Add validation and throw proper error when Cluster resource without ClusterClass is passed with cluster create command
**Bug description**
Currently, when user passes Cluster resource as part of `tanzu cluster create --file <input.yaml>` and the cluster resource doesn't include ClusterClass as part of `.spec.topology.cluster` a wrong error message is thrown which can be misleading to the user.

As part of the error message, throw meaningful error message as this usecase is not yet supported.

**Affected product area (please put an X in all that apply)**

- ( ) APIs
- ( ) Addons
- ( ) CLI
- ( ) Docs
- ( ) IAM
- ( ) Installation
- ( ) Plugin
- ( ) Security
- ( ) Test and Release
- ( ) User Experience
- ( ) Developer Experience

**Expected behavior**
Should throw meaningful error message as this usecase is not yet supported.

**Steps to reproduce the bug**

**Version** (include the SHA if the version is not obvious)

**Environment where the bug was observed (cloud, OS, etc)**

**Relevant Debug Output (Logs, manifests, etc)**



Discussion / hints:

Fix patch:
diff --git a/pkg/v1/tkg/constants/clusterclass_mapping.go b/pkg/v1/tkg/constants/clusterclass_mapping.go
index 54714fcc45..4cd8bc2263 100644
--- a/pkg/v1/tkg/constants/clusterclass_mapping.go
+++ b/pkg/v1/tkg/constants/clusterclass_mapping.go
@@ -279,7 +279,8 @@ const (
 
 	SPEC = "spec"
 
-	TopologyClassIncorrectValueErrMsg = "input cluster class file, attribute spec.topology.class has no value or incorrect value or not following correct naming convension"
+	TopologyClassIncorrectValueErrMsg            = "cluster class missing in configuration or not following naming convention"
+	ClusterResourceAsInputFileNotSupportedErrMsg = "input file with Cluster resource definition is not supported"
 )
 
 // InfrastructureSpecificVariableMappingMap has, infra name to variable mapping map, which makes easy to get infra specific mapping map
diff --git a/pkg/v1/tkg/tkgctl/create_cluster.go b/pkg/v1/tkg/tkgctl/create_cluster.go
index bb372a67af..0e0d43a46f 100644
--- a/pkg/v1/tkg/tkgctl/create_cluster.go
+++ b/pkg/v1/tkg/tkgctl/create_cluster.go
@@ -174,10 +174,13 @@ func (t *tkgctl) processWorkloadClusterInputFile(cc *CreateClusterOptions, isTKG
 	if err != nil {
 		return isInputFileClusterClassBased, err
 	}
+
+	// If TKGm and user is passing Cluster resource as an input throw error if feature-flag is disabled
+	if !isTKGSCluster && !t.tkgClient.IsFeatureActivated(config.FeatureFlagPackageBasedLCM) && clusterobj.GetKind() == constants.KindCluster {
+		return isInputFileClusterClassBased, errors.New(constants.ClusterResourceAsInputFileNotSupportedErrMsg)
+	}
+
 	if isInputFileClusterClassBased {
-		if !isTKGSCluster && !t.tkgClient.IsFeatureActivated(config.FeatureFlagPackageBasedLCM) {
-			return isInputFileClusterClassBased, fmt.Errorf(constants.ErrorMsgCClassInputFeatureFlagDisabled, config.FeatureFlagPackageBasedLCM)
-		}
 		if isTKGSCluster {
 			t.TKGConfigReaderWriter().Set(constants.ConfigVariableClusterName, clusterobj.GetName())
 			t.TKGConfigReaderWriter().Set(constants.ConfigVariableNamespace, clusterobj.GetNamespace())
@@ -189,6 +192,7 @@ func (t *tkgctl) processWorkloadClusterInputFile(cc *CreateClusterOptions, isTKG
 		}
 		t.overrideClusterOptionsWithLatestEnvironmentConfigurationValues(cc)
 	}
+
 	if isTKGSCluster {
 		err = t.validateTKGSFeatureGateStatus(isInputFileClusterClassBased)
 		if err != nil {


Test patch:
diff --git a/pkg/v1/tkg/tkgctl/create_cluster_test.go b/pkg/v1/tkg/tkgctl/create_cluster_test.go
index 2368d182a7..93bac4de6f 100644
--- a/pkg/v1/tkg/tkgctl/create_cluster_test.go
+++ b/pkg/v1/tkg/tkgctl/create_cluster_test.go
@@ -327,6 +327,7 @@ var _ = Describe("Unit tests for (AWS)  cluster_aws.yaml as input file for 'tanz
 			mappedVal, _ = ctl.TKGConfigReaderWriter().Get(constants.ConfigVariableNodeMachineType2)
 			Expect("worker2").To(Equal(fmt.Sprintf("%v", mappedVal)))
 		})
+
 		It("When Input file is cluster type with multiple objects, Environment should be updated with legacy variables and CreateClusterOptions also updated with Cluster attribute values:", func() {
 
 			// Process input cluster yaml file, this should process input cluster yaml file
@@ -352,6 +353,18 @@ var _ = Describe("Unit tests for (AWS)  cluster_aws.yaml as input file for 'tanz
 			Expect("aws-workload-cluster1").To(Equal(fmt.Sprintf("%v", mappedVal)))
 		})
 
+		It("When Input file is cluster type with multiple objects but feature-flag (FeatureFlagPackageBasedLCM) is false, should return an error", func() {
+			tkgClient.IsFeatureActivatedReturns(false)
+			// Process input cluster yaml file, this should process input cluster yaml file
+			// and update the environment with legacy name and values
+			// most of cluster yaml attributes are mapped to legacy variable for more look this - constants.ClusterToLegacyVariablesMapAws
+			options.ClusterConfigFile = inputFileMultipleObjectsAws
+			IsInputFileClusterClassBased, err := ctl.processWorkloadClusterInputFile(&options, isTKGSCluster)
+			Expect(IsInputFileClusterClassBased).Should(BeTrue())
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(ContainSubstring(constants.ClusterResourceAsInputFileNotSupportedErrMsg))
+		})
+
 		It("When Input file is cluster type does not have value for spec.topology.class, return an error", func() {
 
 			// Process input cluster.yaml file, this should process input cluster.yaml file
diff --git a/pkg/v1/tkg/tkgctl/helpers_test.go b/pkg/v1/tkg/tkgctl/helpers_test.go
index a7c920988f..431af2eb6e 100644
--- a/pkg/v1/tkg/tkgctl/helpers_test.go
+++ b/pkg/v1/tkg/tkgctl/helpers_test.go
@@ -11,6 +11,7 @@ import (
 	. "github.com/onsi/gomega"
 
 	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/constants"
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/utils"
 )
 
 var _ = Describe("Cluster Class - IP Family Validation related test cases: ", func() {
@@ -211,3 +212,57 @@ var _ = Describe("Cluster Class - IP Family Validation related test cases: ", fu
 		})
 	})
 })
+
+var _ = Describe("Test cases for CheckIfInputFileIsClusterClassBased", func() {
+	Context("Test cases for CheckIfInputFileIsClusterClassBased", func() {
+		var (
+			configFile          string
+			configFileContent   string
+			isClusterClassBased bool
+			err                 error
+		)
+
+		JustBeforeEach(func() {
+			configFile, err = utils.CreateTempFile("", "")
+			Expect(err).To(BeNil())
+			err = utils.SaveFile(configFile, []byte(configFileContent))
+			Expect(err).To(BeNil())
+			isClusterClassBased, _, err = CheckIfInputFileIsClusterClassBased(configFile)
+		})
+		When("File contains cluster resource with clusterclass defined", func() {
+			BeforeEach(func() {
+				configFileContent = `apiVersion: cluster.x-k8s.io/v1beta1
+kind: Cluster
+metadata:
+  name: aws-workload-cluster1
+  namespace: default
+spec:
+  topology:
+    class: tkg-aws-default`
+			})
+			It("should return true and error should be nil", func() {
+				Expect(isClusterClassBased).To(BeTrue())
+				Expect(err).NotTo(HaveOccurred())
+			})
+		})
+		When("File doesn't contain cluster resource", func() {
+			BeforeEach(func() {
+				configFileContent = ``
+			})
+			It("should return false without error", func() {
+				Expect(isClusterClassBased).To(Equal(false))
+				Expect(err).NotTo(HaveOccurred())
+			})
+		})
+		When("File contain non-yaml string", func() {
+			BeforeEach(func() {
+				configFileContent = `incorrect yaml string`
+			})
+			It("should return false with error", func() {
+				Expect(isClusterClassBased).To(Equal(false))
+				Expect(err).To(HaveOccurred())
+				Expect(err.Error()).To(ContainSubstring("Input file content is not yaml formatted"))
+			})
+		})
+	})
+})