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

Issue:
Implement integration test cases for TKGS 
(This is used to request new product features)

**Describe the feature request**
Currently, in CLI, we don't have end-to-end integration test cases to execute for the TKGS specific use cases, this proposal is to implement CLI end-to-end integration test cases for TKGS. As part of this below scenarios should be covered:

1. Create TKGS TKC (legacy) cluster by taking configuration input, it should cover the below use cases:

    * CLI feature flag (features.global.package-based-lcm-beta) enabled, create a cluster for cluster plan dev and prod use cases
    * CLI feature flag (features.global.package-based-lcm-beta) disabled, create a cluster for cluster plan dev and prod use cases

2. Create TKGS cluster class based (classy cluster) by taking Cluster object YAML as input, it should cover the below use cases:

    * CLI feature flag (features.global.package-based-lcm-beta) enabled, create a classy cluster
    * CLI feature flag (features.global.package-based-lcm-beta) disabled, create a classy cluster

Provide instructions to run the test cases from the command line.

**Describe alternatives you've considered**

**Affected product area (please put an X in all that apply)**

- [ ] APIs
- [ ] Addons
- [x] CLI
- [ ] Docs
- [ ] IAM
- [ ] Installation
- [ ] Plugin
- [ ] Security
- [ ] Test and Release
- [ ] User Experience

**Additional context**



Discussion / hints:

Fix patch:
diff --git a/pkg/v1/tkg/constants/cluster_internal.go b/pkg/v1/tkg/constants/cluster_internal.go
index 42d1ad4253..cc1c78e3d1 100644
--- a/pkg/v1/tkg/constants/cluster_internal.go
+++ b/pkg/v1/tkg/constants/cluster_internal.go
@@ -86,6 +86,7 @@ const (
 // infrastructure provider name constants
 const (
 	InfrastructureProviderVSphere = "vsphere"
+	InfrastructureProviderTkgs    = "tkgs"
 	InfrastructureProviderAWS     = "aws"
 	InfrastructureProviderAzure   = "azure"
 	InfrastructureProviderDocker  = "docker"
@@ -93,6 +94,7 @@ const (
 
 var InfrastructureProviders = map[string]bool{
 	InfrastructureProviderVSphere: true,
+	InfrastructureProviderTkgs:    true,
 	InfrastructureProviderAWS:     true,
 	InfrastructureProviderAzure:   true,
 	InfrastructureProviderDocker:  true,
diff --git a/pkg/v1/tkg/tkgctl/create_cluster.go b/pkg/v1/tkg/tkgctl/create_cluster.go
index 3ce53022ae..3441fdbcd9 100644
--- a/pkg/v1/tkg/tkgctl/create_cluster.go
+++ b/pkg/v1/tkg/tkgctl/create_cluster.go
@@ -137,7 +137,7 @@ func (t *tkgctl) processManagementClusterInputFile(ir *InitRegionOptions) (bool,
 	isInputFileClusterClassBased := false
 
 	if t.tkgClient.IsFeatureActivated(config.FeatureFlagPackageBasedLCM) {
-		isInputFileClusterClassBased, clusterobj, err = t.checkIfInputFileIsClusterClassBased(ir.ClusterConfigFile)
+		isInputFileClusterClassBased, clusterobj, err = CheckIfInputFileIsClusterClassBased(ir.ClusterConfigFile)
 		if err != nil {
 			return isInputFileClusterClassBased, err
 		}
@@ -153,7 +153,7 @@ func (t *tkgctl) processManagementClusterInputFile(ir *InitRegionOptions) (bool,
 }
 
 func (t *tkgctl) processWorkloadClusterInputFile(cc *CreateClusterOptions, isTKGSCluster bool) (bool, error) {
-	isInputFileClusterClassBased, clusterobj, err := t.checkIfInputFileIsClusterClassBased(cc.ClusterConfigFile)
+	isInputFileClusterClassBased, clusterobj, err := CheckIfInputFileIsClusterClassBased(cc.ClusterConfigFile)
 	if err != nil {
 		return isInputFileClusterClassBased, err
 	}
diff --git a/pkg/v1/tkg/tkgctl/helpers.go b/pkg/v1/tkg/tkgctl/helpers.go
index ad154c907b..fd421d37c6 100644
--- a/pkg/v1/tkg/tkgctl/helpers.go
+++ b/pkg/v1/tkg/tkgctl/helpers.go
@@ -164,11 +164,11 @@ func (t *tkgctl) removeAuditLog(clusterName string) {
 	_ = os.Remove(path)
 }
 
-// checkIfInputFileIsClusterClassBased checks user input file, if it has Cluster object then
+// CheckIfInputFileIsClusterClassBased checks user input file, if it has Cluster object then
 // reads all non-empty variables in cluster.spec.topology.variables, and updates those variables in
 // environment and also CreateClusterOptions.
 // TODO (chandrareddyp) : need to make sure the legacy validation error and log messages should not have legacy variable names in case of cluster class use case, should refer Cluster Object attributes (https://github.com/vmware-tanzu/tanzu-framework/issues/2443)
-func (t *tkgctl) checkIfInputFileIsClusterClassBased(clusterConfigFile string) (bool, unstructured.Unstructured, error) {
+func CheckIfInputFileIsClusterClassBased(clusterConfigFile string) (bool, unstructured.Unstructured, error) {
 	var clusterObj unstructured.Unstructured
 
 	isInputFileClusterClassBased := false


Test patch:
diff --git a/pkg/v1/tkg/test/config/config.yaml b/pkg/v1/tkg/test/config/config.yaml
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/pkg/v1/tkg/test/config/tkgs.yaml b/pkg/v1/tkg/test/config/tkgs.yaml
new file mode 100644
index 0000000000..297252e5a3
--- /dev/null
+++ b/pkg/v1/tkg/test/config/tkgs.yaml
@@ -0,0 +1,22 @@
+---
+infrastructure_name: tkgs
+kubernetes_version: v1.22.8---vmware.1-tkg.2-zshippable
+tkgs_kubeconfig_path: /Users/kubeconf-tkgs-cluster
+tkgs_kubeconfig_context: 192.168.124.1
+workload_cluster_options:
+  NAMESPACE: ns01
+  CLUSTER_CIDR: 100.96.0.0/11
+  CLUSTER_NAME: tkc-prod-01
+  CLUSTER_PLAN: dev
+  INFRASTRUCTURE_PROVIDER: tkg-service-vsphere
+  OS_ARCH: amd64
+  OS_NAME: ubuntu
+  OS_VERSION: "20.04"
+  SERVICE_CIDR: 100.64.0.0/13
+  SERVICE_DOMAIN: "cluster.local"
+  CONTROL_PLANE_STORAGE_CLASS: "wcpglobal-storage-profile"
+  WORKER_STORAGE_CLASS: "wcpglobal-storage-profile"
+  CONTROL_PLANE_VM_CLASS: "best-effort-small"
+  WORKER_VM_CLASS: "best-effort-small"
+  NODE_POOL_0_NAME: "workers-name-overwrite"
+  CLUSTER_CLASS_FILE_PATH: /Users/cpamuluri/tkg/deployments/TKGS-Vsphere/TKGS/6-6-22/Cluster-tkgs4.yaml
diff --git a/pkg/v1/tkg/test/framework/e2e_config.go b/pkg/v1/tkg/test/framework/e2e_config.go
index 6514956b5a..6ff4f5fcda 100644
--- a/pkg/v1/tkg/test/framework/e2e_config.go
+++ b/pkg/v1/tkg/test/framework/e2e_config.go
@@ -42,6 +42,26 @@ type ManagementClusterOptions struct {
 	EnableTKGSOnVsphere7 bool   `yaml:"enable_tkgs_on_vSphere7,omitempty"`
 }
 
+// WorkloadClusterOptions represents options to create workload cluster
+type WorkloadClusterOptions struct {
+	ClusterName              string `json:"CLUSTER_NAME,omitempty"`
+	Namespace                string `json:"NAMESPACE,omitempty"`
+	ClusterPlan              string `json:"CLUSTER_PLAN,omitempty"`
+	ClusterCIDR              string `json:"CLUSTER_CIDR,omitempty"`
+	ServiceCIDR              string `json:"SERVICE_CIDR,omitempty"`
+	InfrastructureProvider   string `json:"INFRASTRUCTURE_PROVIDER,omitempty"`
+	OSArch                   string `json:"OS_ARCH,omitempty"`
+	OSName                   string `json:"OS_NAME,omitempty"`
+	OSVersion                string `json:"OS_VERSION,omitempty"`
+	ServiceDomain            string `json:"SERVICE_DOMAIN,omitempty"`
+	ControlPlaneStorageClass string `json:"CONTROL_PLANE_STORAGE_CLASS,omitempty"`
+	WorkerStorageClass       string `json:"WORKER_STORAGE_CLASS,omitempty"`
+	ControlPlaneVMClass      string `json:"CONTROL_PLANE_VM_CLASS,omitempty"`
+	WorkerVMClass            string `json:"WORKER_VM_CLASS,omitempty"`
+	NodePoolName             string `json:"NODE_POOL_0_NAME,omitempty"`
+	ClusterClassFilePath     string `json:"CLUSTER_CLASS_FILE_PATH,omitempty"`
+}
+
 // E2EConfig represents the configuration for the e2e tests
 type E2EConfig struct {
 	UseExistingCluster       bool                     `json:"use_existing_cluster,omitempty"`
@@ -55,6 +75,8 @@ type E2EConfig struct {
 	InfrastructureVersionOld string                   `json:"infrastructure_version_old,omitempty"`
 	ClusterAPIVersionOld     string                   `json:"capi_version_old,omitempty"`
 	KubernetesVersionOld     string                   `json:"kubernetes_version_old,omitempty"`
+	TKGSKubeconfigPath       string                   `json:"tkgs_kubeconfig_path,omitempty"`
+	TKGSKubeconfigContext    string                   `json:"tkgs_kubeconfig_context,omitempty"`
 	TkgCliPathOld            string                   `json:"tkg_cli_path_old,omitempty"`
 	DefaultTimeout           string                   `json:"default_timeout,omitempty"`
 	TkgConfigDir             string                   `json:"tkg_config_dir,omitempty"`
@@ -63,6 +85,7 @@ type E2EConfig struct {
 	ClusterPrefix            string                   `json:"cluster_prefix,omitempty"`
 	TkgConfigVariables       map[string]string        `json:"tkg_config_variables,omitempty"`
 	ManagementClusterOptions ManagementClusterOptions `json:"management_cluster_options,omitempty"`
+	WorkloadClusterOptions   WorkloadClusterOptions   `json:"workload_cluster_options,omitempty"`
 }
 
 // LoadE2EConfig loads the configuration for the e2e test environment
@@ -75,7 +98,8 @@ func LoadE2EConfig(ctx context.Context, input E2EConfigInput) *E2EConfig {
 	e2econfig := &E2EConfig{}
 	Expect(yaml.Unmarshal(e2eConfigData, e2econfig)).To(Succeed(), "Failed to convert the e2e test config file to yaml")
 
-	if e2eConfigString, err := yaml.Marshal(e2econfig); err == nil {
+	e2eConfigString, err := yaml.Marshal(e2econfig)
+	if err == nil {
 		fmt.Printf("E2E CONFIG: %s", string(e2eConfigString))
 	}
 
@@ -126,11 +150,15 @@ func (c *E2EConfig) Defaults() {
 	if c.ManagementClusterOptions.Plan == "" {
 		c.ManagementClusterOptions.Plan = "dev"
 	}
+
+	if c.WorkloadClusterOptions.ClusterPlan == "" {
+		c.WorkloadClusterOptions.ClusterPlan = "dev"
+	}
 }
 
 // Validate validates the configuration in the e2e config file
 func (c *E2EConfig) Validate() error {
-	if c.InfrastructureName == "" {
+	if c.InfrastructureName == "" || !constants.InfrastructureProviders[c.InfrastructureName] {
 		return errors.Errorf("config variable '%s' not set", "infrastructure_name")
 	}
 
@@ -170,3 +198,41 @@ func (c *E2EConfig) SaveTkgConfigVariables() error {
 
 	return nil
 }
+
+// SaveWorkloadClusterOptions saves the config variables from e2e config to the TKG config file
+func (c *E2EConfig) SaveWorkloadClusterOptions(clusterConfigFile string) error {
+	workloadOptionsStr, err := yaml.Marshal(c.WorkloadClusterOptions)
+	if err != nil {
+		return err
+	}
+	workloadOptionsMap := make(map[string]interface{})
+	err = yaml.Unmarshal(workloadOptionsStr, &workloadOptionsMap)
+	if err != nil {
+		return err
+	}
+	fileData, err := os.ReadFile(clusterConfigFile)
+	if err != nil {
+		return err
+	}
+
+	tkgConfigMap := make(map[string]interface{})
+	err = yaml.Unmarshal(fileData, &tkgConfigMap)
+	if err != nil {
+		return err
+	}
+
+	for key, value := range workloadOptionsMap {
+		tkgConfigMap[key] = value
+	}
+
+	outBytes, err := yaml.Marshal(&tkgConfigMap)
+	if err != nil {
+		return errors.Wrapf(err, "error marshaling configuration file")
+	}
+	err = os.WriteFile(clusterConfigFile, outBytes, constants.ConfigFilePermissions)
+	if err != nil {
+		return errors.Wrapf(err, "error writing configuration file")
+	}
+
+	return nil
+}
diff --git a/pkg/v1/tkg/test/framework/exec/kubectl.go b/pkg/v1/tkg/test/framework/exec/kubectl.go
index 7c183e82ab..a19d285c00 100644
--- a/pkg/v1/tkg/test/framework/exec/kubectl.go
+++ b/pkg/v1/tkg/test/framework/exec/kubectl.go
@@ -23,3 +23,15 @@ func KubectlApplyWithArgs(ctx context.Context, kubeconfigPath string, resources
 	err := applyCmd.RunAndRedirectOutput(ctx)
 	return err
 }
+
+// KubectlWithArgs runs kubectl command with args
+func KubectlWithArgs(ctx context.Context, kubeconfigPath string, args ...string) error {
+	aargs := append([]string{"--kubeconfig", kubeconfigPath}, args...)
+	applyCmd := NewCommand(
+		WithCommand("kubectl"),
+		WithArgs(aargs...),
+		WithStdout(GinkgoWriter),
+	)
+	err := applyCmd.RunAndRedirectOutput(ctx)
+	return err
+}
diff --git a/pkg/v1/tkg/test/framework/utils.go b/pkg/v1/tkg/test/framework/utils.go
index 91c1851ef4..fc333ecbac 100644
--- a/pkg/v1/tkg/test/framework/utils.go
+++ b/pkg/v1/tkg/test/framework/utils.go
@@ -8,12 +8,17 @@ import (
 	"os"
 	"path/filepath"
 	"strconv"
+	"strings"
 	"time"
 
 	"gopkg.in/yaml.v2"
 	"sigs.k8s.io/cluster-api/util"
 
 	. "github.com/onsi/ginkgo" // nolint:stylecheck
+	"github.com/pkg/errors"
+
+	configv1alpha1 "github.com/vmware-tanzu/tanzu-framework/apis/config/v1alpha1"
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/config"
 )
 
 // CreateClusterOptions represent options to create a TKG cluster
@@ -152,3 +157,38 @@ func GetTempClusterConfigFile(clusterConfigFile string, options *CreateClusterOp
 
 	return configFilePath, nil
 }
+
+//SetCliConfigFlag sets cli flag
+func SetCliConfigFlag(flagName string, value string) error {
+	cfg, err := config.GetClientConfig()
+	if err != nil {
+		return err
+	}
+	paramArray := strings.Split(flagName, ".")
+	err = SetFeatures(cfg, paramArray, value)
+	if err != nil {
+		return err
+	}
+	return config.StoreClientConfig(cfg)
+}
+
+//SetFeatures sets flags in given ClientConfig
+func SetFeatures(cfg *configv1alpha1.ClientConfig, paramArray []string, value string) error {
+	if len(paramArray) != 3 {
+		return errors.New("unable to parse config path parameter into three parts [" + strings.Join(paramArray, ".") + "]  (was expecting 'features.<plugin>.<feature>'")
+	}
+	plugin := paramArray[1]
+	featureName := paramArray[2]
+
+	if cfg.ClientOptions == nil {
+		cfg.ClientOptions = &configv1alpha1.ClientOptions{}
+	}
+	if cfg.ClientOptions.Features == nil {
+		cfg.ClientOptions.Features = make(map[string]configv1alpha1.FeatureMap)
+	}
+	if cfg.ClientOptions.Features[plugin] == nil {
+		cfg.ClientOptions.Features[plugin] = configv1alpha1.FeatureMap{}
+	}
+	cfg.ClientOptions.Features[plugin][featureName] = value
+	return nil
+}
diff --git a/pkg/v1/tkg/test/tkgctl/tkgs/tkgs_suite_test.go b/pkg/v1/tkg/test/tkgctl/tkgs/tkgs_suite_test.go
new file mode 100644
index 0000000000..bf95e04ccc
--- /dev/null
+++ b/pkg/v1/tkg/test/tkgctl/tkgs/tkgs_suite_test.go
@@ -0,0 +1,112 @@
+// Copyright 2021 VMware, Inc. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+// nolint:typecheck,nolintlint
+package tkgs
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	"github.com/onsi/ginkgo/config"
+	"github.com/onsi/ginkgo/reporters"
+	. "github.com/onsi/gomega"
+	"github.com/pkg/errors"
+
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/constants"
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/test/framework"
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/tkgctl"
+)
+
+const CLI_CLUSTERCLASS_FLAG = "features.global.package-based-lcm-beta"
+const TKGS_SETTINGS_FILE = "/Users/cpamuluri/tkg/tasks/uTKG-Testing/TKGS-tests/tkgs-inegration-tests/tanzu-framework/pkg/v1/tkg/test/config/config.yaml"
+
+var (
+	// path to the e2e config file
+	e2eConfigPath string
+	// path to store test artifacts
+	artifactsFolder string
+	// config read from e2eConfigPath to be used in the tests
+	e2eConfig *framework.E2EConfig
+)
+
+func TestE2E(t *testing.T) {
+	if folder, ok := os.LookupEnv("E2E_ARTIFACTS"); ok {
+		artifactsFolder = folder
+	}
+	if artifactsFolder == "" {
+		artifactsFolder = filepath.Join(os.TempDir(), "artifacts")
+	}
+	if configPath, ok := os.LookupEnv("E2E_CONFIG"); ok {
+		e2eConfigPath = configPath
+	}
+
+	RegisterFailHandler(Fail)
+	junitPath := filepath.Join(artifactsFolder, "junit", fmt.Sprintf("junit.e2e_suite.%d.xml", config.GinkgoConfig.ParallelNode))
+	junitReporter := reporters.NewJUnitReporter(junitPath)
+	RunSpecsWithDefaultAndCustomReporters(t, "tkgctl-tkgs-e2e", []Reporter{junitReporter})
+}
+
+var _ = SynchronizedBeforeSuite(func() []byte {
+	// Before all parallel nodes
+
+	Expect(e2eConfigPath).To(BeAnExistingFile(), "e2e config file is either not set or invalid")
+	Expect(os.MkdirAll(artifactsFolder, 0o700)).To(Succeed(), "can't create artifacts directory %q", artifactsFolder)
+
+	logsDir := filepath.Join(artifactsFolder, "logs")
+	Expect(os.MkdirAll(logsDir, 0o700)).To(Succeed(), "can't create logs directory %q", logsDir)
+
+	By(fmt.Sprintf("loading the e2e test configuration from %q", e2eConfigPath))
+	e2eConfig = framework.LoadE2EConfig(context.TODO(), framework.E2EConfigInput{ConfigPath: e2eConfigPath})
+	Expect(e2eConfigPath).ToNot(BeNil(), "failed to load e2e config from %s", e2eConfigPath)
+
+	Expect(ValidateTKGSConf(e2eConfig)).To(Succeed(), "e2e test configuration is not valid")
+
+	return []byte(
+		strings.Join([]string{
+			artifactsFolder,
+			e2eConfigPath,
+		}, ","),
+	)
+}, func(data []byte) {
+	// Before each parallel node
+	parts := strings.Split(string(data), ",")
+	Expect(parts).To(HaveLen(2))
+	artifactsFolder = parts[0]
+	e2eConfigPath = parts[1]
+	e2eConfig = framework.LoadE2EConfig(context.TODO(), framework.E2EConfigInput{ConfigPath: e2eConfigPath})
+	Expect(e2eConfigPath).ToNot(BeNil(), "failed to load e2e config from %s", e2eConfigPath)
+})
+
+var _ = SynchronizedAfterSuite(func() {
+}, func() {})
+
+// ValidateTKGSConf validate the configuration in the e2e config file
+func ValidateTKGSConf(c *framework.E2EConfig) error {
+	if c.InfrastructureName == "" || c.InfrastructureName != constants.InfrastructureProviderTkgs {
+		return errors.Errorf("config variable '%v' value is '%v', it should be 'tkgs' for tkgs test suite", "infrastructure_name", c.InfrastructureName)
+	}
+	return nil
+}
+
+func getDeleteClustersOptions(e2eConfig *framework.E2EConfig) tkgctl.DeleteClustersOptions {
+	return tkgctl.DeleteClustersOptions{
+		ClusterName: e2eConfig.WorkloadClusterOptions.ClusterName,
+		Namespace:   e2eConfig.WorkloadClusterOptions.Namespace,
+		SkipPrompt:  true,
+	}
+}
+
+func createClusterConfigFile(e2eConfig *framework.E2EConfig) string {
+	options := framework.CreateClusterOptions{}
+	clusterConfigFile, err := framework.GetTempClusterConfigFile(e2eConfig.TkgClusterConfigPath, &options)
+	Expect(err).To(BeNil())
+	err = e2eConfig.SaveWorkloadClusterOptions(clusterConfigFile)
+	Expect(err).To(BeNil())
+	return clusterConfigFile
+}
diff --git a/pkg/v1/tkg/test/tkgctl/tkgs/tkgs_workload_cluster_test.go b/pkg/v1/tkg/test/tkgctl/tkgs/tkgs_workload_cluster_test.go
new file mode 100644
index 0000000000..b5458ce603
--- /dev/null
+++ b/pkg/v1/tkg/test/tkgctl/tkgs/tkgs_workload_cluster_test.go
@@ -0,0 +1,187 @@
+// Copyright 2021 VMware, Inc. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+// nolint:typecheck,nolintlint
+package tkgs
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"path/filepath"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"sigs.k8s.io/cluster-api/util"
+
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/config"
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/constants"
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/test/framework"
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/test/framework/exec"
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/tkgctl"
+)
+
+var _ = Describe("TKGS - Create workload cluster use cases", func() {
+	var (
+		logsDir              string
+		clusterConfigFile    string
+		err                  error
+		deleteClusterOptions tkgctl.DeleteClustersOptions
+		clusterOptions       tkgctl.CreateClusterOptions
+		tkgctlOptions        tkgctl.Options
+		tkgctlClient         tkgctl.TKGClient
+	)
+	BeforeEach(func() {
+		logsDir = filepath.Join(artifactsFolder, "logs")
+		tkgctlOptions = tkgctl.Options{
+			ConfigDir:    e2eConfig.TkgConfigDir,
+			KubeConfig:   e2eConfig.TKGSKubeconfigPath,
+			KubeContext:  e2eConfig.TKGSKubeconfigContext,
+			SettingsFile: TKGS_SETTINGS_FILE,
+			LogOptions: tkgctl.LoggingOptions{
+				File:      filepath.Join(logsDir, "tkgs-create-wc.log"),
+				Verbosity: e2eConfig.TkgCliLogLevel,
+			},
+		}
+		clusterOptions = tkgctl.CreateClusterOptions{
+			ClusterConfigFile: clusterConfigFile,
+			Edition:           "tkg",
+			SkipPrompt:        true,
+		}
+	})
+	Context("input file is legacy config file - TKC cluster", func() {
+		BeforeEach(func() {
+			Expect(e2eConfig.TkrVersion).ToNot(BeEmpty(), fmt.Sprintf("the kubernetes_version should not be empty to create legacy TKGS cluster"))
+			clusterOptions.TkrVersion = e2eConfig.TkrVersion
+		})
+		Context("cluster Plan is dev", func() {
+			BeforeEach(func() {
+				e2eConfig.WorkloadClusterOptions.ClusterPlan = "dev"
+				e2eConfig.WorkloadClusterOptions.ClusterName = "tkc-e2e-" + util.RandomString(4)
+				deleteClusterOptions = getDeleteClustersOptions(e2eConfig)
+				clusterOptions.ClusterConfigFile = createClusterConfigFile(e2eConfig)
+			})
+			AfterEach(func() {
+				defer os.Remove(clusterConfigFile)
+			})
+			When("cluster class cli feature flag (features.global.package-based-lcm-beta) set true", func() {
+				BeforeEach(func() {
+					//set the cli feature flag as true -  (features.global.package-based-lcm-beta)
+					Expect(framework.SetCliConfigFlag(CLI_CLUSTERCLASS_FLAG, "true")).To(Succeed(), "error while setting CLI ClusterClass flag")
+					tkgctlClient, err = tkgctl.New(tkgctlOptions)
+					Expect(err).To(BeNil())
+				})
+				It("should create TKC Workload Cluster and delete it", func() {
+					By(fmt.Sprintf("creating TKC workload cluster %v in namespace: %v, cli feature flag is enabled", e2eConfig.WorkloadClusterOptions.ClusterName, e2eConfig.WorkloadClusterOptions.Namespace))
+					err = tkgctlClient.CreateCluster(clusterOptions)
+					Expect(err).To(BeNil())
+
+					By(fmt.Sprintf("deleting TKC workload cluster %v in namespace: %v", clusterOptions.ClusterName, clusterOptions.Namespace))
+					err = tkgctlClient.DeleteCluster(deleteClusterOptions)
+					Expect(err).To(BeNil())
+				})
+			})
+			When("cluster class cli feature flag (features.global.package-based-lcm-beta) set false", func() {
+				BeforeEach(func() {
+					//set the cli feature flag as false -  (features.global.package-based-lcm-beta)
+					Expect(framework.SetCliConfigFlag(CLI_CLUSTERCLASS_FLAG, "false")).To(Succeed(), "error while setting CLI ClusterClass flag")
+					tkgctlClient, err = tkgctl.New(tkgctlOptions)
+					Expect(err).To(BeNil())
+				})
+				It("should create TKC Workload Cluster and delete it", func() {
+					By(fmt.Sprintf("creating TKC workload cluster %v in namespace: %v, cli feature flag is disabled", e2eConfig.WorkloadClusterOptions.ClusterName, e2eConfig.WorkloadClusterOptions.Namespace))
+					err = tkgctlClient.CreateCluster(clusterOptions)
+					Expect(err).To(BeNil())
+
+					By(fmt.Sprintf("deleting TKC workload cluster %v in namespace: %v", e2eConfig.WorkloadClusterOptions.ClusterName, e2eConfig.WorkloadClusterOptions.Namespace))
+					err = tkgctlClient.DeleteCluster(deleteClusterOptions)
+					Expect(err).To(BeNil())
+				})
+			})
+		})
+		Context("Cluster Plan is prod", func() {
+			BeforeEach(func() {
+				e2eConfig.WorkloadClusterOptions.ClusterPlan = "prod"
+				e2eConfig.WorkloadClusterOptions.ClusterName = "tkc-e2e-" + util.RandomString(4)
+				deleteClusterOptions = getDeleteClustersOptions(e2eConfig)
+				clusterOptions.ClusterConfigFile = createClusterConfigFile(e2eConfig)
+			})
+			AfterEach(func() {
+				defer os.Remove(clusterConfigFile)
+			})
+			When("cluster class cli feature flag (features.global.package-based-lcm-beta) set true", func() {
+				BeforeEach(func() {
+					//set the cli feature flag as true -  (features.global.package-based-lcm-beta)
+					Expect(framework.SetCliConfigFlag(CLI_CLUSTERCLASS_FLAG, "true")).To(Succeed(), "error while setting CLI ClusterClass flag")
+					tkgctlClient, err = tkgctl.New(tkgctlOptions)
+					Expect(err).To(BeNil())
+				})
+				It("should create TKC Workload Cluster and delete it", func() {
+					By(fmt.Sprintf("creating TKC workload cluster %v in namespace: %v, cli feature flag is enabled", e2eConfig.WorkloadClusterOptions.ClusterName, e2eConfig.WorkloadClusterOptions.Namespace))
+					err = tkgctlClient.CreateCluster(clusterOptions)
+					Expect(err).To(BeNil())
+
+					By(fmt.Sprintf("deleting TKC workload cluster %v in namespace: %v ", e2eConfig.WorkloadClusterOptions.ClusterName, e2eConfig.WorkloadClusterOptions.Namespace))
+					err = tkgctlClient.DeleteCluster(deleteClusterOptions)
+					Expect(err).To(BeNil())
+				})
+			})
+			When("cluster class cli feature flag (features.global.package-based-lcm-beta) set false", func() {
+				BeforeEach(func() {
+					//set the cli feature flag as false -  (features.global.package-based-lcm-beta)
+					Expect(framework.SetCliConfigFlag(CLI_CLUSTERCLASS_FLAG, "false")).To(Succeed(), "error while setting CLI ClusterClass flag")
+					tkgctlClient, err = tkgctl.New(tkgctlOptions)
+					Expect(err).To(BeNil())
+				})
+				It("should create TKC Workload Cluster and delete it", func() {
+					By(fmt.Sprintf("Creating TKC workload cluster %v in namespace: %v, cli feature flag is disabled", e2eConfig.WorkloadClusterOptions.ClusterName, e2eConfig.WorkloadClusterOptions.Namespace))
+					err = tkgctlClient.CreateCluster(clusterOptions)
+					Expect(err).To(BeNil())
+
+					By(fmt.Sprintf("Deleting TKC workload cluster %v in namespace: %v", e2eConfig.WorkloadClusterOptions.ClusterName, e2eConfig.WorkloadClusterOptions.Namespace))
+					err = tkgctlClient.DeleteCluster(deleteClusterOptions)
+					Expect(err).To(BeNil())
+				})
+			})
+		})
+	})
+	Context("input file is Cluster Class based", func() {
+		BeforeEach(func() {
+			cclusterFile, err := os.ReadFile(e2eConfig.WorkloadClusterOptions.ClusterClassFilePath)
+			Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to read the input cluster class based config file from: %v", e2eConfig.WorkloadClusterOptions.ClusterClassFilePath))
+			Expect(cclusterFile).ToNot(BeEmpty(), fmt.Sprintf("the input cluster class based config file should not be empty, file path: %v", e2eConfig.WorkloadClusterOptions.ClusterClassFilePath))
+			clusterOptions.ClusterConfigFile = e2eConfig.WorkloadClusterOptions.ClusterClassFilePath
+		})
+		When("cluster class cli feature flag (features.global.package-based-lcm-beta) set true", func() {
+			BeforeEach(func() {
+				//set the cli feature flag as true -  (features.global.package-based-lcm-beta)
+				Expect(framework.SetCliConfigFlag(CLI_CLUSTERCLASS_FLAG, "true")).To(Succeed(), "error while setting CLI feature flag")
+				tkgctlClient, err = tkgctl.New(tkgctlOptions)
+				Expect(err).To(BeNil())
+			})
+			It("should create cluster class based workload cluster and delete it", func() {
+				By(fmt.Sprintf("creating cluster class based workload cluster, cli feature flag is enabled"))
+				err = tkgctlClient.CreateCluster(clusterOptions)
+				Expect(err).To(BeNil())
+
+				By(fmt.Sprintf("deleting cluster class based workload cluster"))
+				_, ccObject, _ := tkgctl.CheckIfInputFileIsClusterClassBased(e2eConfig.WorkloadClusterOptions.ClusterClassFilePath)
+				err = exec.KubectlWithArgs(context.Background(), e2eConfig.TKGSKubeconfigPath, "--context", e2eConfig.TKGSKubeconfigContext, "delete", "cluster", ccObject.GetName(), "-n", ccObject.GetNamespace())
+				Expect(err).To(BeNil())
+			})
+		})
+		When("cluster class cli feature flag (features.global.package-based-lcm-beta) set false", func() {
+			BeforeEach(func() {
+				//set the cli feature flag as false -  (features.global.package-based-lcm-beta)
+				Expect(framework.SetCliConfigFlag(CLI_CLUSTERCLASS_FLAG, "false")).To(Succeed(), "error while setting CLI ClusterClass flag")
+				tkgctlClient, err = tkgctl.New(tkgctlOptions)
+				Expect(err).To(BeNil())
+			})
+			It("should return error", func() {
+				By(fmt.Sprintf("creating Cluster class based workload cluster, cli feature flag is disabled"))
+				err = tkgctlClient.CreateCluster(clusterOptions)
+				Expect(err.Error()).To(ContainSubstring(fmt.Sprintf(constants.ErrorMsgCClassInputFeatureFlagDisabled, config.FeatureFlagPackageBasedLCM)))
+			})
+		})
+	})
+})
diff --git a/pkg/v1/tkg/tkgctl/create_cluster_test.go b/pkg/v1/tkg/tkgctl/create_cluster_test.go
index 345abcb436..773061678e 100644
--- a/pkg/v1/tkg/tkgctl/create_cluster_test.go
+++ b/pkg/v1/tkg/tkgctl/create_cluster_test.go
@@ -261,7 +261,7 @@ var _ = Describe("Unit tests for (AWS)  cluster_aws.yaml as input file for 'tanz
 			Expect(err).To(BeNil())
 
 			// process input file and get map, which has all attribute's path and its values
-			inputVariablesMap := getInputAttributesMap(&ctl, options.ClusterConfigFile)
+			inputVariablesMap := getInputAttributesMap(options.ClusterConfigFile)
 
 			// updates lower precedence variables with higher precedence variables values.
 			// In AWS use case, there were few attributes repeated in cluster yaml file, we need to take always higher precedence values.
@@ -338,7 +338,7 @@ var _ = Describe("Unit tests for (AWS)  cluster_aws.yaml as input file for 'tanz
 			Expect(err).To(BeNil())
 
 			// process input file and get map, which has all attribute's path and its values
-			inputVariablesMap := getInputAttributesMap(&ctl, options.ClusterConfigFile)
+			inputVariablesMap := getInputAttributesMap(options.ClusterConfigFile)
 
 			// updates lower precedence variables with higher precedence variables values.
 			// In AWS use case, there were few attributes repeated in cluster yaml file, we need to take always higher precedence values.
@@ -433,7 +433,7 @@ var _ = Describe("Unit tests for - (Vsphere) - cluster_vsphere.yaml as input fil
 			Expect(err).To(BeNil())
 
 			// process input file and get map, which has all attribute's path and its values
-			inputVariablesMap := getInputAttributesMap(&ctl, options.ClusterConfigFile)
+			inputVariablesMap := getInputAttributesMap(options.ClusterConfigFile)
 
 			// validate input Cluster Object yaml file attributes values with corresponding legacy variable values in environment, both should be same, as we have already updated the environment with Cluster Object attribute values.
 			validateFileInputAttributeValuesWithEnvironmentValues(&ctl, inputVariablesMap, constants.ClusterAttributesToLegacyVariablesMapVsphere)
@@ -538,7 +538,7 @@ var _ = Describe("Unit tests for - (Azure) - cluster_azure.yaml as input file fo
 			Expect(err).To(BeNil())
 
 			// process input file and get map, which has all attribute's path and its values
-			inputVariablesMap := getInputAttributesMap(&ctl, options.ClusterConfigFile)
+			inputVariablesMap := getInputAttributesMap(options.ClusterConfigFile)
 
 			// validate input Cluster Object yaml file attributes values with corresponding legacy variables values in environment, both should be same, as we have already updated the environment with Cluster Object attribute values.
 			validateFileInputAttributeValuesWithEnvironmentValues(&ctl, inputVariablesMap, constants.ClusterAttributesToLegacyVariablesMapAzure)
@@ -838,11 +838,11 @@ var _ = Describe("Clusterclass FeatureGate specific use cases", func() {
 })
 
 // getInputAttributesMap process input Cluster Object yaml file and returns a map which has all attributes from the input file and its values
-func getInputAttributesMap(ctl *tkgctl, inputFile string) map[string]interface{} {
+func getInputAttributesMap(inputFile string) map[string]interface{} {
 	// to make sure input cluster attributes values are updated with legacy variables in environment (tkgctl.TKGConfigReaderWriter())
 	// Processing the input cluster yaml file with the existing util api, and expecting Cluster YAML object,
 	// from the Cluster YAML object, map attributes path with values, and update Map variablesMap.
-	_, clusterObj, _ := ctl.checkIfInputFileIsClusterClassBased(inputFile)
+	_, clusterObj, _ := CheckIfInputFileIsClusterClassBased(inputFile)
 	inputVariablesMap := make(map[string]interface{})
 	inputVariablesMap["metadata.name"] = clusterObj.GetName()
 	inputVariablesMap["metadata.namespace"] = clusterObj.GetNamespace()