Repository: kubestellar/kubeflex
Pull request #352: 🐛 refactoring and design change on the usage of spf13/cobra
Author: login: rxinui
Created: 2025-05-08T01:38:43Z | Merged: 2025-05-10T02:02:46Z

# Proof-of-concept - READY TO MERGE

## Summary

Before this PR, a couple of parameters, types and others have been copy paste and did not leverage the power that spf13/cobra offers (reusability, modularity...)

To solve this problem, I decided to change the codebase design at the `cmd/kflex/` level. 

For this proof of concept, **ONLY** the commands listed below have been refactored and the code as it is currently would not build.
- `kflex create`
- `kflex list`

Overall, the design change vows to:
- simplify the codeflow which will make the OSS adoption by contributors easier
- avoid side-effects as much as possible, which could explain the number of bugs reported
- improve readability of the codebase with better naming convention, remove unecessary struct/types, better semantic
- be more idiomatic. My references are [Learn Go the Idiomatic way](https://www.oreilly.com/library/view/learning-go-2nd/9781098139285/) and [100 Mistakes in Go and how to avoid it](https://100go.co/)

**Note:** two types of comments are added within the codebase for this PoC
- `// REFACTOR:` explain the refactor made and consequences
- `// REFACTOR?` indicates a question -- should it be refactor?

## Code design

The new code design aims to alleviate the hassle of defining everything within the root file and aims to isolate command code within their own package to limit the dependencies between root and its commands.

### Project structure

It is inspired by domain-driven dev. To avoid cyclic import, the structure policy is the following:

```
cmd/kflex
├── common                   # shared package -- only package which does not represent commands but reusable types, functions and flags
│   ├── cp.go                # shared 
│   └── flags.go             # shared flags -- define global flags constants
├── create
│   └── create.go
├── ctx                      # package -- indicates command
│   ├── ctx.go               # command -- noticeable because matches package name
│   └── list.go              # any other files indicates they are subcommands
├── list
│   └── list.go
...
└── main.go                  # root command -- where kflex is defined
```

1. **Top-to-bottom direction**. A command cannot import/use root resources nor resources from its siblings `root --> commands --> subcommands ...`
2. **Shared package**. Only `common` package stores reusable resources callable by root command, its commands and subcommands. `common` is the only package not defining a command.
3. **Packages represent commands**. Definition of a command and its subcommand are made within its package: flags definition, code logic, subcommands.
4. **Files within command package**. A command is named after its package name, hence any file within a package with different naming would represent a subcommands. For instance `ctx/ctx.go` defines the `kflex ctx` whereas `ctx/list.go` defines the subcommand of ctx `kflex ctx list`. (NOT IMPLEMENTED IN THIS PoC !!!) --> *Note: in relation with #351, this structure will then allow to easily implement `kflex ctx rename` by creating `ctx/rename.go` file*

Proceding as follow makes every commands business-independant and has separation of concerns.

### spf13/cobra specificities

To avoid global variables within the root file, we make use of `FlagSet` which does not store CLI value within these variables. Instead, each command will define 

1. `func Command() *cobra.Command` which returns the command with its flags and execution code initialised.
2. `func execute(arguments...)` which represent the logic of the command (ie. create controlplane)

This guarentee that flags values passed cannot be modified by another process, which was a possibility before by the mean of global variables to store flag values. Now, `command.Flags().StringP` replaces `command.Flags().StringVarP`.

**NOTE:** the only global variables that may remains are those required by `gorelease` to inject values at compile time (such as `kflex version`).

To avoid memory consumption, `command.Flags()` is no longer call for each options. Instead, it instantiated once for one command in one execution, as shown here 
https://github.com/kubestellar/kubeflex/compare/main...rxinui:kubeflex:bug/308?expand=1#diff-20056e0c2eba9525b066cf3155bfc265204830faa73252b3ea4be36f4dd3b736R56-R65

Unnecessary `if-test` are replaced by the mean of cobra default values. See https://github.com/kubestellar/kubeflex/compare/main...rxinui:kubeflex:bug/308?expand=1#diff-20056e0c2eba9525b066cf3155bfc265204830faa73252b3ea4be36f4dd3b736R69-R73

### Better readability

Now, flags name are moved as constants. It will make OSS contribution easier when existing option for a given command should also be added to another one (for instance, `--set` flag define in `create` should be added to `ctx`). Shared flags are define in `common` package while command-specific flag are define within its command definition.

In the legacy codebase, a tricky/bad usage of variable name is done with control plane. In fact, `cp` is currently used as `var cp CP` but it gets overshadow by `tenancyv1alpha1.ControlPlane` type. This has been fix by improving variable naming. Now `cp` strictly refers to `common.CP` whereas `controlPlane` refers to `tenancyv1alpha1.ControlPlane`. See https://github.com/kubestellar/kubeflex/compare/main...rxinui:kubeflex:bug/308?expand=1#diff-97c377915d8ccea8cbc135426b489f90bae558e022c7c02310f2a02ecca99583R53-R89

(a) All struct variants of `CP` such as `CPCreate` or `CPList` are deleted as they are confusing and do not bring values. Instead, `CP` is now moved to `common` package and functions has been adapted to receive `CP` as function parameter rather than method parameter. See https://github.com/kubestellar/kubeflex/compare/main...rxinui:kubeflex:bug/308?expand=1#diff-97c377915d8ccea8cbc135426b489f90bae558e022c7c02310f2a02ecca99583R41

(b) Moreover, `CP` is now meant to be instantiated by the use of `NewCP` function. This factory implements the Option design pattern which allows optional parameters. That simplify a lot the codebase. We move from the old way

```go
cp := del.CPDelete{
			CP: common.CP{
				Ctx:        createContext(),
				Name:       args[0],
				Kubeconfig: kubeconfig,
			},
		}
```

to simply

```go
cp := common.NewCP(kubeconfig, common.WithName(args[0]))
```

In fact, `del.CPDelete` becomes irrelevant because of (a) and `NewCP` (b) embed `createContext()` process. With the option design pattern, it makes it easier with command that does not expect a `CP.Name` such as `listCtxCommand`. In such cases, we obtain

```go
cp := common.NewCP(kubeconfig)
```

## Related issue(s)

Fixes #308 


Linked issue #308: bug: improve cobra command code to set subcommand options in a struct that contained the cobra.Command. 
### Describe the bug

There is unnecessary copying of parameters used by the KubeFlex CLI. This can be avoided by defining  the subcommand options  in a struct that contained the cobra.Command rather than (roughly) the other way around. See https://github.com/kubestellar/kubeflex/pull/306#pullrequestreview-2521139829.

### Steps To Reproduce

Look at the code in `cmd/kflex/main.go`

### Expected Behavior

should avoid unnecessary copying of parameters.

### Additional Context

_No response_
Linked issue comments: user: login: rxinui
created_at: 2025-05-01T14:53:42Z
body: After a community meeting, I've shown my intention to help on kubeflex ([also here](https://kubernetes.slack.com/archives/C058SUSL5AA/p1745532439730009?thread_ts=1745349821.923599&cid=C058SUSL5AA)). I'll go ahead and look at this one 👍 
author_association: CONTRIBUTOR
user: login: rxinui
created_at: 2025-05-01T14:53:48Z
body: /assign
author_association: CONTRIBUTOR

Diff:
diff --git a/Makefile b/Makefile
index e6ad2462..c8ca3406 100644
--- a/Makefile
+++ b/Makefile
@@ -47,8 +47,8 @@ GIT_VERSION := $(shell go mod edit -json | jq '.Require[] | select(.Path == "k8s
 BUILD_DATE := $(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
 MAIN_VERSION := $(shell git tag -l --sort=-v:refname | head -n1)
 LDFLAGS := \
-	-X main.Version=${MAIN_VERSION}.${GIT_COMMIT} \
-	-X main.BuildDate=${BUILD_DATE} \
+	-X github.com/kubestellar/kubeflex/cmd/kflex/common.Version=${MAIN_VERSION}.${GIT_COMMIT} \
+	-X github.com/kubestellar/kubeflex/cmd/kflex/common.BuildDate=${BUILD_DATE} \
 	-X k8s.io/client-go/pkg/version.gitCommit=${GIT_COMMIT} \
 	-X k8s.io/client-go/pkg/version.gitTreeState=${GIT_DIRTY} \
 	-X k8s.io/client-go/pkg/version.gitVersion=${GIT_VERSION} \
diff --git a/cmd/kflex/adopt/adopt.go b/cmd/kflex/adopt/adopt.go
index 32a284da..15e2fb92 100644
--- a/cmd/kflex/adopt/adopt.go
+++ b/cmd/kflex/adopt/adopt.go
@@ -27,6 +27,7 @@ import (
 	"path/filepath"
 
 	homedir "github.com/mitchellh/go-homedir"
+	"github.com/spf13/cobra"
 	corev1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -42,6 +43,15 @@ import (
 	"github.com/kubestellar/kubeflex/pkg/util"
 )
 
+const (
+	AdoptedKubeconfigFlag = "adopted-kubeconfig"
+	AdoptedContextFlag    = "adopted-context"
+	URLOverrideFlag       = "url-override"
+	ExpirationSecondsFlag = "expiration-seconds"
+)
+
+const defaultExpirationSeconds = 86400 * 365
+
 type CPAdopt struct {
 	common.CP
 	AdoptedKubeconfig             string
@@ -50,42 +60,78 @@ type CPAdopt struct {
 	AdoptedTokenExpirationSeconds int64
 }
 
+func Command() *cobra.Command {
+	command := &cobra.Command{
+		Use:   "adopt <name>",
+		Short: "Adopt a control plane from an external cluster",
+		Long: `Adopt a control plane from an external cluster and switches the Kubeconfig context to
+				the current instance`,
+		Args: cobra.ExactArgs(1),
+		Run: func(cmd *cobra.Command, args []string) {
+			flagset := cmd.Flags()
+			kubeconfig, _ := flagset.GetString(common.KubeconfigFlag)
+			postCreateHook, _ := flagset.GetString(common.PostCreateHookFlag)
+			hookVars, _ := flagset.GetStringArray(common.SetFlag)
+			chattyStatus, _ := flagset.GetBool(common.ChattyStatusFlag)
+			adoptedKubeconfig, _ := flagset.GetString(AdoptedKubeconfigFlag)
+			adoptedContext, _ := flagset.GetString(AdoptedContextFlag)
+			adoptedURLOverride, _ := flagset.GetString(URLOverrideFlag)
+			adoptedTokenExpirationSeconds, _ := flagset.GetInt64(ExpirationSecondsFlag)
+			cpAdopt := CPAdopt{
+				CP:                            common.NewCP(kubeconfig, common.WithName(args[0])),
+				AdoptedKubeconfig:             adoptedKubeconfig,
+				AdoptedContext:                adoptedContext,
+				AdoptedURLOverride:            adoptedURLOverride,
+				AdoptedTokenExpirationSeconds: adoptedTokenExpirationSeconds,
+			}
+			ExecuteAdopt(cpAdopt, postCreateHook, hookVars, chattyStatus)
+		},
+	}
+	flagset := command.Flags()
+	// create passing the control plane type and backend type
+	flagset.StringP(common.PostCreateHookFlag, "p", "", "name of post create hook to run")
+	flagset.StringArrayP(common.SetFlag, "e", []string{}, "set post create hook variables, in the form name=value ")
+	flagset.StringP(AdoptedKubeconfigFlag, "a", "", "path to the kubeconfig file for the adopted cluster. If unspecified, it uses the default Kubeconfig")
+	flagset.StringP(AdoptedContextFlag, "c", "", "path to adopted cluster context in adopted kubeconfig")
+	flagset.StringP(URLOverrideFlag, "u", "", "URL overrride for adopted cluster. Required when cluster address uses local host address, e.g. `https://127.0.0.1`")
+	flagset.Int64P(ExpirationSecondsFlag, "x", defaultExpirationSeconds, "adopted token expiration in seconds. Default is one year.")
+	return command
+}
+
 // Adopt a control plane from another cluster
-func (c *CPAdopt) Adopt(hook string, hookVars []string, chattyStatus bool) {
+func ExecuteAdopt(cpAdopt CPAdopt, hook string, hookVars []string, chattyStatus bool) {
 	done := make(chan bool)
 	var wg sync.WaitGroup
 	cx := cont.CPCtx{}
-	cx.Context(chattyStatus, false, false, false)
+	cx.ExecuteCtx(chattyStatus, false, false, false)
 
 	controlPlaneType := tenancyv1alpha1.ControlPlaneTypeExternal
-	util.PrintStatus(fmt.Sprintf("Adopting control plane %s of type %s ...", c.Name, controlPlaneType), done, &wg, chattyStatus)
+	util.PrintStatus(fmt.Sprintf("Adopting control plane %s of type %s ...", cpAdopt.Name, controlPlaneType), done, &wg, chattyStatus)
 
-	bootstrapKubeconfig := getBootstrapKubeconfig(c)
-
-	cl, err := kfclient.GetClient(c.Kubeconfig)
+	cl, err := kfclient.GetClient(cpAdopt.Kubeconfig)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error getting kubeflex client: %v\n", err)
 		os.Exit(1)
 	}
 
-	clientsetp, err := kfclient.GetClientSet(c.Kubeconfig)
+	clientsetp, err := kfclient.GetClientSet(cpAdopt.Kubeconfig)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error getting clientset: %v\n", err)
 		os.Exit(1)
 	}
 
-	if err := applyAdoptedBootstrapSecret(clientsetp, c.Name, bootstrapKubeconfig, c.AdoptedContext, c.AdoptedURLOverride); err != nil {
+	if err := applyAdoptedBootstrapSecret(clientsetp, cpAdopt.Name, getBootstrapKubeconfig(cpAdopt), cpAdopt.AdoptedContext, cpAdopt.AdoptedURLOverride); err != nil {
 		fmt.Fprintf(os.Stderr, "error creating adopted cluster kubeconfig: %v\n", err)
 		os.Exit(1)
 	}
-
-	cp, err := common.GenerateControlPlane(c.Name, string(controlPlaneType), "", hook, hookVars, &c.AdoptedTokenExpirationSeconds)
+	// REFACTOR? is cpAdopt being pointer of (*CPAdopt) make sense?
+	controlPlane, err := common.GenerateControlPlane(cpAdopt.Name, string(controlPlaneType), "", hook, hookVars, &cpAdopt.AdoptedTokenExpirationSeconds)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "error generating control plane object: %v\n", err)
 		os.Exit(1)
 	}
 
-	if err := cl.Create(context.TODO(), cp, &client.CreateOptions{}); err != nil {
+	if err := cl.Create(context.TODO(), controlPlane, &client.CreateOptions{}); err != nil {
 		fmt.Fprintf(os.Stderr, "Error creating ControlPlane object: %v\n", err)
 		os.Exit(1)
 	}
@@ -231,12 +277,12 @@ func isValidServerURL(serverURL string) error {
 	return nil
 }
 
-func getBootstrapKubeconfig(c *CPAdopt) string {
-	if c.AdoptedKubeconfig != "" {
-		return c.AdoptedKubeconfig
+func getBootstrapKubeconfig(cpAdopt CPAdopt) string {
+	if cpAdopt.AdoptedKubeconfig != "" {
+		return cpAdopt.AdoptedKubeconfig
 	}
-	if c.Kubeconfig != "" {
-		return c.Kubeconfig
+	if cpAdopt.Kubeconfig != "" {
+		return cpAdopt.Kubeconfig
 	}
 	return getKubeConfigFromEnv()
 }
diff --git a/cmd/kflex/common/cp.go b/cmd/kflex/common/cp.go
index d3975b08..fd8dd6d1 100644
--- a/cmd/kflex/common/cp.go
+++ b/cmd/kflex/common/cp.go
@@ -21,8 +21,11 @@ import (
 	"fmt"
 	"strings"
 
+	"go.uber.org/zap"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
+	"github.com/go-logr/logr"
+	"github.com/go-logr/zapr"
 	tenancyv1alpha1 "github.com/kubestellar/kubeflex/api/v1alpha1"
 	"github.com/kubestellar/kubeflex/pkg/util"
 )
@@ -31,10 +34,44 @@ type CP struct {
 	Ctx        context.Context
 	Kubeconfig string
 	Name       string
+	AliasName  string
 }
 
+type CPOption func(*CP)
+
+// New control plane with option design pattern
+func NewCP(kubeconfigPath string, options ...CPOption) CP {
+	cp := CP{Ctx: createContext(), Kubeconfig: kubeconfigPath}
+	for _, option := range options {
+		option(&cp)
+	}
+	return cp
+}
+
+// Make Name optional using option design pattern
+func WithName(name string) CPOption {
+	return func(cp *CP) {
+		cp.Name = name
+	}
+}
+
+// Make AliasName optional using option design pattern
+func WithAliasName(aliasName string) CPOption {
+	return func(cp *CP) {
+		cp.AliasName = aliasName
+	}
+}
+
+// Create a new system context with logger
+func createContext() context.Context {
+	zapLogger, _ := zap.NewDevelopment(zap.AddCaller())
+	logger := zapr.NewLoggerWithOptions(zapLogger)
+	return logr.NewContext(context.Background(), logger)
+}
+
+// Generate a control plane based with a name
 func GenerateControlPlane(name, controlPlaneType, backendType, hook string, hookVars []string, tokenExpirationSeconds *int64) (*tenancyv1alpha1.ControlPlane, error) {
-	cp := &tenancyv1alpha1.ControlPlane{
+	controlPlane := &tenancyv1alpha1.ControlPlane{
 		ObjectMeta: metav1.ObjectMeta{
 			Name: name,
 		},
@@ -45,21 +82,21 @@ func GenerateControlPlane(name, controlPlaneType, backendType, hook string, hook
 		},
 	}
 	if hook != "" {
-		cp.Spec.PostCreateHook = &hook
+		controlPlane.Spec.PostCreateHook = &hook
 		var err error
-		cp.Spec.PostCreateHookVars, err = convertToMap(hookVars)
+		controlPlane.Spec.PostCreateHookVars, err = convertToMap(hookVars)
 		if err != nil {
 			return nil, err
 		}
 	}
 	if controlPlaneType == string(tenancyv1alpha1.ControlPlaneTypeExternal) {
-		cp.Spec.BootstrapSecretRef = &tenancyv1alpha1.BootstrapSecretReference{
+		controlPlane.Spec.BootstrapSecretRef = &tenancyv1alpha1.BootstrapSecretReference{
 			Name:         util.GenerateBootstrapSecretName(name),
 			Namespace:    util.SystemNamespace,
 			InClusterKey: util.KubeconfigSecretKeyInCluster,
 		}
 	}
-	return cp, nil
+	return controlPlane, nil
 }
 
 func convertToMap(pairs []string) (map[string]string, error) {
diff --git a/cmd/kflex/common/flags.go b/cmd/kflex/common/flags.go
new file mode 100644
index 00000000..d5f64595
--- /dev/null
+++ b/cmd/kflex/common/flags.go
@@ -0,0 +1,33 @@
+/*
+Copyright 2023 The KubeStellar Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package common
+
+import "k8s.io/client-go/tools/clientcmd"
+
+const (
+	KubeconfigFlag     = clientcmd.RecommendedConfigPathFlag
+	ChattyStatusFlag   = "chatty-status"
+	VerbosityFlag      = "verbosity"
+	PostCreateHookFlag = "postcreate-hook"
+	SetFlag            = "set"
+)
+
+// Version injected by makefile:LDFLAGS
+var Version string
+
+// BuildDate injected by makefile:LDFLAGS
+var BuildDate string
diff --git a/cmd/kflex/create/create.go b/cmd/kflex/create/create.go
index 5bf5a5de..b04e770e 100644
--- a/cmd/kflex/create/create.go
+++ b/cmd/kflex/create/create.go
@@ -23,6 +23,7 @@ import (
 	"sync"
 
 	tenancyv1alpha1 "github.com/kubestellar/kubeflex/api/v1alpha1"
+	"github.com/spf13/cobra"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	"github.com/kubestellar/kubeflex/cmd/kflex/common"
@@ -32,37 +33,71 @@ import (
 	"github.com/kubestellar/kubeflex/pkg/util"
 )
 
-type CPCreate struct {
-	common.CP
+// defaults
+const (
+	BKTypeDefault        = string(tenancyv1alpha1.BackendDBTypeShared) 
+	CTypeDefault         = string(tenancyv1alpha1.ControlPlaneTypeK8S) 
+	ControlPlaneTypeFlag = "type"
+	BackendTypeFlag      = "backend-type"
+)
+
+func Command() *cobra.Command {
+	command := &cobra.Command{
+		Use:   "create <name>",
+		Short: "Create a control plane instance",
+		Long: `Create a control plane instance and switches the Kubeconfig context to
+	        the current instance`,
+		Args: cobra.ExactArgs(1),
+		Run: func(cmd *cobra.Command, args []string) {
+			flagset := cmd.Flags()
+			kubeconfig, _ := flagset.GetString(common.KubeconfigFlag)
+			chattyStatus, _ := flagset.GetBool(common.ChattyStatusFlag)
+			cpType, _ := flagset.GetString(ControlPlaneTypeFlag)
+			backendType, _ := flagset.GetString(BackendTypeFlag)
+			postCreateHook, _ := flagset.GetString(common.PostCreateHookFlag)
+			hookVars, _ := flagset.GetStringArray(common.SetFlag)
+			cp := common.NewCP(kubeconfig, common.WithName(args[0]))
+			// create passing the control plane type and backend type
+			ExecuteCreate(cp, cpType, backendType, postCreateHook, hookVars, chattyStatus)
+		},
+	}
+
+	flagset := command.Flags()
+	flagset.StringP(ControlPlaneTypeFlag, "t", CTypeDefault, "type of control plane: k8s|ocm|vcluster")
+	flagset.StringP(BackendTypeFlag, "b", BKTypeDefault, "backend DB sharing: shared|dedicated")
+	flagset.StringP(common.PostCreateHookFlag, "p", "", "name of post create hook to run")
+	flagset.BoolP(common.ChattyStatusFlag, "s", true, "chatty status indicator")
+	flagset.StringArrayP(common.SetFlag, "e", []string{}, "set post create hook variables, in the form name=value ")
+	return command
 }
 
 // Create a new control plane
-func (c *CPCreate) Create(controlPlaneType, backendType, hook string, hookVars []string, chattyStatus bool) {
+func ExecuteCreate(cp common.CP, controlPlaneType string, backendType string, hook string, hookVars []string, chattyStatus bool) {
 	done := make(chan bool)
 	var wg sync.WaitGroup
 	cx := cont.CPCtx{}
-	cx.Context(chattyStatus, false, false, false)
+	cx.ExecuteCtx(chattyStatus, false, false, false)
 
-	cl, err := kfclient.GetClient(c.Kubeconfig)
+	cl, err := kfclient.GetClient(cp.Kubeconfig)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
 		os.Exit(1)
 	}
 
-	cp, err := common.GenerateControlPlane(c.Name, controlPlaneType, backendType, hook, hookVars, nil)
+	controlPlane, err := common.GenerateControlPlane(cp.Name, controlPlaneType, backendType, hook, hookVars, nil)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "error generating control plane object: %v\n", err)
 		os.Exit(1)
 	}
 
-	util.PrintStatus(fmt.Sprintf("Creating new control plane %s of type %s ...", c.Name, controlPlaneType), done, &wg, chattyStatus)
-	if err := cl.Create(context.TODO(), cp, &client.CreateOptions{}); err != nil {
+	util.PrintStatus(fmt.Sprintf("Creating new control plane %s of type %s ...", cp.Name, controlPlaneType), done, &wg, chattyStatus)
+	if err := cl.Create(context.TODO(), controlPlane, &client.CreateOptions{}); err != nil {
 		fmt.Fprintf(os.Stderr, "Error creating instance: %v\n", err)
 		os.Exit(1)
 	}
 	done <- true
 
-	clientsetp, err := kfclient.GetClientSet(c.Kubeconfig)
+	clientsetp, err := kfclient.GetClientSet(cp.Kubeconfig)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error getting clientset: %v\n", err)
 		os.Exit(1)
@@ -70,7 +105,7 @@ func (c *CPCreate) Create(controlPlaneType, backendType, hook string, hookVars [
 	clientset := *clientsetp
 
 	util.PrintStatus("Waiting for API server to become ready...", done, &wg, chattyStatus)
-	kubeconfig.WatchForSecretCreation(clientset, c.Name, util.GetKubeconfSecretNameByControlPlaneType(controlPlaneType))
+	kubeconfig.WatchForSecretCreation(clientset, cp.Name, util.GetKubeconfSecretNameByControlPlaneType(controlPlaneType))
 
 	switch controlPlaneType {
 	case string(tenancyv1alpha1.ControlPlaneTypeHost):
@@ -78,7 +113,7 @@ func (c *CPCreate) Create(controlPlaneType, backendType, hook string, hookVars [
 	case string(tenancyv1alpha1.ControlPlaneTypeVCluster):
 		if err := util.WaitForStatefulSetReady(clientset,
 			util.GetAPIServerDeploymentNameByControlPlaneType(controlPlaneType),
-			util.GenerateNamespaceFromControlPlaneName(cp.Name)); err != nil {
+			util.GenerateNamespaceFromControlPlaneName(controlPlane.Name)); err != nil {
 
 			fmt.Fprintf(os.Stderr, "Error waiting for stateful set to become ready: %v\n", err)
 			os.Exit(1)
@@ -86,7 +121,7 @@ func (c *CPCreate) Create(controlPlaneType, backendType, hook string, hookVars [
 	case string(tenancyv1alpha1.ControlPlaneTypeK8S), string(tenancyv1alpha1.ControlPlaneTypeOCM):
 		if err := util.WaitForDeploymentReady(clientset,
 			util.GetAPIServerDeploymentNameByControlPlaneType(controlPlaneType),
-			util.GenerateNamespaceFromControlPlaneName(cp.Name)); err != nil {
+			util.GenerateNamespaceFromControlPlaneName(controlPlane.Name)); err != nil {
 
 			fmt.Fprintf(os.Stderr, "Error waiting for deployment to become ready: %v\n", err)
 			os.Exit(1)
@@ -98,7 +133,7 @@ func (c *CPCreate) Create(controlPlaneType, backendType, hook string, hookVars [
 
 	done <- true
 
-	if err := kubeconfig.LoadAndMerge(c.Ctx, clientset, c.Name, controlPlaneType); err != nil {
+	if err := kubeconfig.LoadAndMerge(cp.Ctx, clientset, cp.Name, controlPlaneType); err != nil {
 		fmt.Fprintf(os.Stderr, "Error loading and merging kubeconfig: %s\n", err)
 		os.Exit(1)
 	}
diff --git a/cmd/kflex/ctx/ctx.go b/cmd/kflex/ctx/ctx.go
index 3e8f1046..ebd07d28 100644
--- a/cmd/kflex/ctx/ctx.go
+++ b/cmd/kflex/ctx/ctx.go
@@ -29,18 +29,58 @@ import (
 	kfclient "github.com/kubestellar/kubeflex/pkg/client"
 	"github.com/kubestellar/kubeflex/pkg/kubeconfig"
 	"github.com/kubestellar/kubeflex/pkg/util"
+	"github.com/spf13/cobra"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/tools/clientcmd/api"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
+const (
+	OverwriteExistingContextFlag = "overwrite-existing-context"
+	SetCurrentForHostingFlag     = "set-current-for-hosting"
+)
+
 type CPCtx struct {
 	common.CP
 	Type tenancyv1alpha1.ControlPlaneType
 }
 
+func Command() *cobra.Command {
+	command := &cobra.Command{
+		Use:   "ctx",
+		Short: "Switch or get kubeconfig context",
+		Long: `Running without an argument switches the context back to the hosting cluster context,
+						while providing the control plane name as argument switches the context to
+						that control plane. Use 'get' to retrieve the current context.`,
+		Args: cobra.MaximumNArgs(1),
+		Run: func(cmd *cobra.Command, args []string) {
+			flagset := cmd.Flags()
+			kubeconfig, _ := flagset.GetString(common.KubeconfigFlag)
+			chattyStatus, _ := flagset.GetBool(common.ChattyStatusFlag)
+			overwriteExistingCtx, _ := flagset.GetBool(OverwriteExistingContextFlag)
+			setCurrentCtxAsHosting, _ := flagset.GetBool(SetCurrentForHostingFlag)
+			aliasName, _ := flagset.GetString("alias")
+			cpName := ""
+			if len(args) == 1 {
+				cpName = args[0]
+			}
+			cp := CPCtx{
+				CP: common.NewCP(kubeconfig, common.WithName(cpName), common.WithAliasName(aliasName)),
+			}
+			cp.ExecuteCtx(chattyStatus, true, overwriteExistingCtx, setCurrentCtxAsHosting)
+		},
+	}
+	flagset := command.Flags()
+	flagset.BoolP(OverwriteExistingContextFlag, "o", false, "Overwrite of hosting cluster context with new control plane context")
+	flagset.BoolP(SetCurrentForHostingFlag, "c", false, "Set current context as hosting cluster context")
+	flagset.String("alias", "", "Set an alias name as the context, user and cluster value instead of cp name")
+	command.AddCommand(CommandGet())
+	command.AddCommand(CommandList())
+	return command
+}
+
 // Context switch context in Kubeconfig
-func (c *CPCtx) Context(chattyStatus, failIfNone, overwriteExistingCtx, setCurrentCtxAsHosting bool) {
+func (c *CPCtx) ExecuteCtx(chattyStatus, failIfNone, overwriteExistingCtx, setCurrentCtxAsHosting bool) {
 	done := make(chan bool)
 	var wg sync.WaitGroup
 	kconf, err := kubeconfig.LoadKubeconfig(c.Ctx)
@@ -51,15 +91,10 @@ func (c *CPCtx) Context(chattyStatus, failIfNone, overwriteExistingCtx, setCurre
 
 	switch c.CP.Name {
 	case "get":
-		currentContext, err := kubeconfig.GetCurrentContext(c.Ctx)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "Error retrieving current context: %s\n", err)
-			os.Exit(1)
-		}
-		fmt.Println(currentContext)
+		ExecuteCtxGet(c.CP)
 		return
 	case "list":
-		c.ListContexts()
+		ExecuteCtxList(c.CP)
 		return
 	case "":
 		if setCurrentCtxAsHosting { // set hosting cluster context unconditionally to the current context
@@ -88,42 +123,66 @@ func (c *CPCtx) Context(chattyStatus, failIfNone, overwriteExistingCtx, setCurre
 			done <- true
 		}
 	default:
-		ctxName := certs.GenerateContextName(c.Name)
-		if overwriteExistingCtx {
-			util.PrintStatus("Overwriting existing context for control plane", done, &wg, chattyStatus)
+		if c.AliasName != "" {
+			ctxName := certs.GenerateContextName(c.AliasName)
+			util.PrintStatus("Renaming context", done, &wg, chattyStatus)
+			// Load context from kubeconfig
+			kubeconfig.RenameKey(kconf.Clusters, certs.GenerateClusterName(c.Name), certs.GenerateClusterName(c.AliasName))
+			kubeconfig.RenameKey(kconf.AuthInfos, certs.GenerateAuthInfoAdminName(c.Name), certs.GenerateAuthInfoAdminName(c.AliasName))
+			kubeconfig.RenameKey(kconf.Contexts, certs.GenerateContextName(c.Name), ctxName)
+			kconf.Contexts[ctxName] = &api.Context{
+				Cluster:  certs.GenerateClusterName(c.AliasName),
+				AuthInfo: certs.GenerateAuthInfoAdminName(c.AliasName),
+			}
+			fmt.Fprintf(os.Stdout, "renaming context from %s to %s\n", c.Name, c.AliasName)
+			// Switch context -- no call of kubeconfig.SwitchContext required
+			kconf.CurrentContext = ctxName
+			// Create new context in kubeconfig
+
+			// Delete current context from kubeconfig
 			if err = kubeconfig.DeleteContext(kconf, c.Name); err != nil {
-				fmt.Fprintf(os.Stderr, "no kubeconfig context for %s was found: %s\n", c.Name, err)
+				fmt.Fprintf(os.Stderr, "no kubeconfig context for %s was found: %s\n", c.AliasName, err)
 			}
+			fmt.Fprintf(os.Stdout, "context %s is deleted\n", c.Name)
 			done <- true
-		}
-
-		util.PrintStatus(fmt.Sprintf("Switching to context %s...", ctxName), done, &wg, chattyStatus)
-		if err = kubeconfig.SwitchContext(kconf, c.Name); err != nil {
+			// Exit
+		} else {
 			if overwriteExistingCtx {
-				fmt.Fprintf(os.Stderr, "trying to load new context %s from server...\n", c.Name)
-			} else {
-				fmt.Fprintf(os.Stderr, "kubeconfig context %s not found (%s), trying to load from server...\n", c.Name, err)
-			}
-			if err := c.switchToHostingClusterContextAndWrite(kconf); err != nil {
-				fmt.Fprintf(os.Stderr, "Error switching back to hosting cluster context: %s\n", err)
-				os.Exit(1)
-			}
-			if err = c.loadAndMergeFromServer(kconf); err != nil {
-				fmt.Fprintf(os.Stderr, "Error loading kubeconfig context from server: %s\n", err)
-				os.Exit(1)
+				util.PrintStatus("Overwriting existing context for control plane", done, &wg, chattyStatus)
+				if err = kubeconfig.DeleteContext(kconf, c.Name); err != nil {
+					fmt.Fprintf(os.Stderr, "no kubeconfig context for %s was found: %s\n", c.Name, err)
+				}
+				done <- true
 			}
-			// context exists only for CPs that are not of type host
-			if c.Type != tenancyv1alpha1.ControlPlaneTypeHost {
-				if err = kubeconfig.SwitchContext(kconf, c.Name); err != nil {
-					fmt.Fprintf(os.Stderr, "Error switching kubeconfig context after loading from server: %s\n", err)
+			// TODO continue the --alias
+			util.PrintStatus(fmt.Sprintf("Switching to context %s...", c.Name), done, &wg, chattyStatus)
+			if err = kubeconfig.SwitchContext(kconf, c.Name); err != nil {
+				if overwriteExistingCtx {
+					fmt.Fprintf(os.Stderr, "trying to load new context %s from server...\n", c.Name)
+				} else {
+					fmt.Fprintf(os.Stderr, "kubeconfig context %s not found (%s), trying to load from server...\n", c.Name, err)
+				}
+				if err := c.switchToHostingClusterContextAndWrite(kconf); err != nil {
+					fmt.Fprintf(os.Stderr, "Error switching back to hosting cluster context: %s\n", err)
+					os.Exit(1)
+				}
+				if err = c.loadAndMergeFromServer(kconf); err != nil {
+					fmt.Fprintf(os.Stderr, "Error loading kubeconfig context from server: %s\n", err)
 					os.Exit(1)
 				}
-			} else {
-				fmt.Fprintf(os.Stderr, "control plane %s is of type 'host', using hosting cluster context (%s)\n", c.Name, kconf.CurrentContext)
-				os.Exit(0)
+				// context exists only for CPs that are not of type host
+				if c.Type != tenancyv1alpha1.ControlPlaneTypeHost {
+					if err = kubeconfig.SwitchContext(kconf, c.Name); err != nil {
+						fmt.Fprintf(os.Stderr, "Error switching kubeconfig context after loading from server: %s\n", err)
+						os.Exit(1)
+					}
+				} else {
+					fmt.Fprintf(os.Stderr, "control plane %s is of type 'host', using hosting cluster context (%s)\n", c.Name, kconf.CurrentContext)
+					os.Exit(0)
+				}
 			}
+			done <- true
 		}
-		done <- true
 	}
 
 	if err = kubeconfig.WriteKubeconfig(c.Ctx, kconf); err != nil {
@@ -191,35 +250,3 @@ func (c *CPCtx) isCurrentContextHostingClusterContext() bool {
 	clientset := *clientsetp
 	return util.CheckResourceExists(clientset, "tenancy.kflex.kubestellar.org", "v1alpha1", "controlplanes")
 }
-
-func (c *CPCtx) GetCurrentContext() {
-	currentContext, err := kubeconfig.GetCurrentContext(c.Ctx)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error retrieving current context: %s\n", err)
-		os.Exit(1)
-	}
-	fmt.Println(currentContext)
-}
-
-func (c *CPCtx) ListContexts() {
-	kconf, err := kubeconfig.LoadKubeconfig(c.Ctx)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error loading kubeconfig: %s\n", err)
-		os.Exit(1)
-	}
-
-	if len(kconf.Contexts) == 0 {
-		fmt.Println("No contexts found in kubeconfig")
-		return
-	}
-
-	currentContext := kconf.CurrentContext
-	fmt.Println("Available Contexts:")
-	for name := range kconf.Contexts {
-		prefix := " "
-		if name == currentContext {
-			prefix = "*"
-		}
-		fmt.Printf("%s %s\n", prefix, name)
-	}
-}
diff --git a/cmd/kflex/ctx/get.go b/cmd/kflex/ctx/get.go
new file mode 100644
index 00000000..7e6b1228
--- /dev/null
+++ b/cmd/kflex/ctx/get.go
@@ -0,0 +1,34 @@
+package ctx
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/kubestellar/kubeflex/cmd/kflex/common"
+	"github.com/kubestellar/kubeflex/pkg/kubeconfig"
+	"github.com/spf13/cobra"
+)
+
+func CommandGet() *cobra.Command {
+	command := &cobra.Command{
+		Use:   "get",
+		Short: "Get the current kubeconfig context",
+		Long:  `Retrieve and display the current context from the kubeconfig file`,
+		Args:  cobra.ExactArgs(0),
+		Run: func(cmd *cobra.Command, args []string) {
+			kubeconfig, _ := cmd.Flags().GetString(common.KubeconfigFlag)
+			cp := common.NewCP(kubeconfig)
+			ExecuteCtxGet(cp)
+		},
+	}
+	return command
+}
+
+func ExecuteCtxGet(cp common.CP) {
+	currentContext, err := kubeconfig.GetCurrentContext(cp.Ctx)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error retrieving current context: %s\n", err)
+		os.Exit(1)
+	}
+	fmt.Println(currentContext)
+}
diff --git a/cmd/kflex/ctx/list.go b/cmd/kflex/ctx/list.go
index 807e4043..c5fde587 100644
--- a/cmd/kflex/ctx/list.go
+++ b/cmd/kflex/ctx/list.go
@@ -21,15 +21,26 @@ import (
 	"os"
 
 	"github.com/kubestellar/kubeflex/cmd/kflex/common"
+	"github.com/spf13/cobra"
 	"k8s.io/client-go/tools/clientcmd"
 )
 
-type CPContextList struct {
-	CP common.CP
+func CommandList() *cobra.Command {
+	return &cobra.Command{
+		Use:   "list",
+		Short: "List all available contexts",
+		Long:  `List all available contexts in the kubeconfig file`,
+		Args:  cobra.ExactArgs(0),
+		Run: func(cmd *cobra.Command, args []string) {
+			kubeconfig, _ := cmd.Flags().GetString(common.KubeconfigFlag)
+			cp := common.NewCP(kubeconfig)
+			ExecuteCtxList(cp)
+		},
+	}
 }
 
-func (cp *CPContextList) ListContexts() {
-	config, err := clientcmd.LoadFromFile(cp.CP.Kubeconfig)
+func ExecuteCtxList(cp common.CP) {
+	config, err := clientcmd.LoadFromFile(cp.Kubeconfig)
 	if err != nil {
 		fmt.Printf("Error loading kubeconfig: %s\n", err)
 		os.Exit(1)
diff --git a/cmd/kflex/delete/delete.go b/cmd/kflex/delete/delete.go
index ec313a81..34771ca1 100644
--- a/cmd/kflex/delete/delete.go
+++ b/cmd/kflex/delete/delete.go
@@ -23,6 +23,7 @@ import (
 	"sync"
 
 	tenancyv1alpha1 "github.com/kubestellar/kubeflex/api/v1alpha1"
+	"github.com/spf13/cobra"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -33,21 +34,34 @@ import (
 	"github.com/kubestellar/kubeflex/pkg/util"
 )
 
-type CPDelete struct {
-	common.CP
+func Command() *cobra.Command {
+	return &cobra.Command{
+		Use:   "delete <name>",
+		Short: "Delete a control plane instance",
+		Long: `Delete a control plane instance and switches the context back to
+				the hosting cluster context`,
+		Args: cobra.ExactArgs(1),
+		Run: func(cmd *cobra.Command, args []string) {
+			flagset := cmd.Flags()
+			kubeconfig, _ := flagset.GetString(common.KubeconfigFlag)
+			chattyStatus, _ := flagset.GetBool(common.ChattyStatusFlag)
+			cp := common.NewCP(kubeconfig, common.WithName(args[0]))
+			ExecuteDelete(cp, chattyStatus)
+		},
+	}
 }
 
-func (c *CPDelete) Delete(chattyStatus bool) {
+func ExecuteDelete(cp common.CP, chattyStatus bool) {
 	done := make(chan bool)
-	cp := &tenancyv1alpha1.ControlPlane{
+	controlPlane := &tenancyv1alpha1.ControlPlane{
 		ObjectMeta: metav1.ObjectMeta{
-			Name: c.Name,
+			Name: cp.Name,
 		},
 	}
 	var wg sync.WaitGroup
 
-	util.PrintStatus(fmt.Sprintf("Deleting control plane %s...", c.Name), done, &wg, chattyStatus)
-	kconf, err := kubeconfig.LoadKubeconfig(c.Ctx)
+	util.PrintStatus(fmt.Sprintf("Deleting control plane %s...", cp.Name), done, &wg, chattyStatus)
+	kconf, err := kubeconfig.LoadKubeconfig(cp.Ctx)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error loading kubeconfig: %s\n", err)
 		os.Exit(1)
@@ -58,44 +72,44 @@ func (c *CPDelete) Delete(chattyStatus bool) {
 		os.Exit(1)
 	}
 
-	if err := kubeconfig.WriteKubeconfig(c.Ctx, kconf); err != nil {
+	if err := kubeconfig.WriteKubeconfig(cp.Ctx, kconf); err != nil {
 		fmt.Fprintf(os.Stderr, "Error writing kubeconfig: %s\n", err)
 		os.Exit(1)
 	}
 
-	kfcClient, err := kfclient.GetClient(c.Kubeconfig)
+	kfcClient, err := kfclient.GetClient(cp.Kubeconfig)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error getting kf client: %s\n", err)
 		os.Exit(1)
 	}
 
-	if err := kfcClient.Get(context.TODO(), client.ObjectKeyFromObject(cp), cp, &client.GetOptions{}); err != nil {
+	if err := kfcClient.Get(context.TODO(), client.ObjectKeyFromObject(controlPlane), controlPlane, &client.GetOptions{}); err != nil {
 		fmt.Fprintf(os.Stderr, "control plane not found on server: %s", err)
 	}
 
-	if err := kfcClient.Delete(context.TODO(), cp, &client.DeleteOptions{}); err != nil {
+	if err := kfcClient.Delete(context.TODO(), controlPlane, &client.DeleteOptions{}); err != nil {
 		fmt.Fprintf(os.Stderr, "Error deleting instance: %s\n", err)
 		os.Exit(1)
 	}
 	done <- true
 
-	clientsetp, err := kfclient.GetClientSet(c.Kubeconfig)
+	clientsetp, err := kfclient.GetClientSet(cp.Kubeconfig)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error getting kf client: %s\n", err)
 		os.Exit(1)
 	}
 	clientset := *clientsetp
-	util.PrintStatus(fmt.Sprintf("Waiting for control plane %s to be deleted...", c.Name), done, &wg, chattyStatus)
-	util.WaitForNamespaceDeletion(clientset, util.GenerateNamespaceFromControlPlaneName(c.Name))
+	util.PrintStatus(fmt.Sprintf("Waiting for control plane %s to be deleted...", cp.Name), done, &wg, chattyStatus)
+	util.WaitForNamespaceDeletion(clientset, util.GenerateNamespaceFromControlPlaneName(cp.Name))
 
-	if cp.Spec.Type != tenancyv1alpha1.ControlPlaneTypeHost &&
-		cp.Spec.Type != tenancyv1alpha1.ControlPlaneTypeExternal {
-		if err := kubeconfig.DeleteContext(kconf, c.Name); err != nil {
-			fmt.Fprintf(os.Stderr, "no kubeconfig context for %s was found: %s\n", c.Name, err)
+	if controlPlane.Spec.Type != tenancyv1alpha1.ControlPlaneTypeHost &&
+		controlPlane.Spec.Type != tenancyv1alpha1.ControlPlaneTypeExternal {
+		if err := kubeconfig.DeleteContext(kconf, cp.Name); err != nil {
+			fmt.Fprintf(os.Stderr, "no kubeconfig context for %s was found: %s\n", cp.Name, err)
 			os.Exit(1)
 		}
 
-		if err := kubeconfig.WriteKubeconfig(c.Ctx, kconf); err != nil {
+		if err := kubeconfig.WriteKubeconfig(cp.Ctx, kconf); err != nil {
 			fmt.Fprintf(os.Stderr, "Error writing kubeconfig: %s\n", err)
 			os.Exit(1)
 		}
diff --git a/cmd/kflex/init/init.go b/cmd/kflex/init/init.go
index 74d60015..f3a9416b 100644
--- a/cmd/kflex/init/init.go
+++ b/cmd/kflex/init/init.go
@@ -28,13 +28,75 @@ import (
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
+	"github.com/kubestellar/kubeflex/cmd/kflex/common"
+	"github.com/kubestellar/kubeflex/cmd/kflex/init/cluster"
 	"github.com/kubestellar/kubeflex/pkg/client"
 	"github.com/kubestellar/kubeflex/pkg/helm"
 	kcfg "github.com/kubestellar/kubeflex/pkg/kubeconfig"
 	"github.com/kubestellar/kubeflex/pkg/util"
+	"github.com/spf13/cobra"
 )
 
-func Init(ctx context.Context, kubeconfig, version, buildDate string, domain, externalPort, hostContainer string, chattyStatus, isOCP bool) {
+const (
+	CreateKindFlag        = "create-kind"
+	DomainFlag            = "domain"
+	HostContainerNameFlag = "host-container-name" // REFACTOR? replace with host-container-name?
+	ExternalPortFlag      = "external-port"        // REFACTOR? replace with external-port?
+)
+
+func Command() *cobra.Command {
+	command := &cobra.Command{
+		Use:   "init",
+		Short: "Initialize kubeflex",
+		Long:  `Installs the default shared storage backend and the kubeflex operator`,
+		Args:  cobra.ExactArgs(0),
+		Run: func(cmd *cobra.Command, args []string) {
+
+			flagset := cmd.Flags()
+			kubeconfig, _ := flagset.GetString(common.KubeconfigFlag)
+			chattyStatus, _ := flagset.GetBool(common.ChattyStatusFlag)
+			createkind, _ := flagset.GetBool(CreateKindFlag)
+			domain, _ := flagset.GetString(DomainFlag)
+			externalPort, _ := flagset.GetInt(ExternalPortFlag)
+			hostContainer, _ := flagset.GetString(HostContainerNameFlag)
+			done := make(chan bool)
+			var wg sync.WaitGroup
+			var isOCP bool
+
+			util.PrintStatus("Checking if OpenShift cluster...", done, &wg, chattyStatus)
+			clientsetp, err := client.GetClientSet(kubeconfig)
+			if err == nil {
+				isOCP = util.IsOpenShift(*clientsetp)
+				if isOCP {
+					done <- true
+					util.PrintStatus("OpenShift cluster detected", done, &wg, chattyStatus)
+				}
+			}
+			done <- true
+
+			if createkind {
+				if isOCP {
+					fmt.Fprintf(os.Stderr, "OpenShift cluster detected on existing context\n")
+					fmt.Fprintf(os.Stdout, "Switch to a non-OpenShift context with `kubectl config use-context <context-name>` and retry.\n")
+					os.Exit(1)
+				}
+				cluster.CreateKindCluster(chattyStatus)
+			}
+
+			cp := common.NewCP(kubeconfig)
+			ExecuteInit(cp.Ctx, cp.Kubeconfig, common.Version, common.BuildDate, domain, strconv.Itoa(externalPort), hostContainer, chattyStatus, isOCP)
+			wg.Wait()
+		},
+	}
+	flagset := command.Flags()
+	flagset.BoolP(CreateKindFlag, "c", false, "Create and configure a kind cluster for installing Kubeflex")
+	flagset.StringP(DomainFlag, "d", "localtest.me", "domain for FQDN")
+	flagset.StringP(HostContainerNameFlag, "n", "kubeflex-control-plane", "Name of the hosting cluster container (kind or k3d only)")
+	flagset.IntP(ExternalPortFlag, "p", 9443, "external port used by ingress")
+	return command
+}
+
+func ExecuteInit(ctx context.Context, kubeconfig, version, buildDate string, domain, externalPort, hostContainer string, chattyStatus, isOCP bool) {
 	done := make(chan bool)
 	var wg sync.WaitGroup
 
diff --git a/cmd/kflex/list/list.go b/cmd/kflex/list/list.go
index dccd7df0..6685e561 100644
--- a/cmd/kflex/list/list.go
+++ b/cmd/kflex/list/list.go
@@ -17,18 +17,29 @@ limitations under the License.
 package list
 
 import (
-	"context"
 	"fmt"
 	"os"
 	"time"
 
 	tenancyv1alpha1 "github.com/kubestellar/kubeflex/api/v1alpha1"
+	"github.com/kubestellar/kubeflex/cmd/kflex/common"
 	"github.com/kubestellar/kubeflex/pkg/client"
+	"github.com/spf13/cobra"
 )
 
-type CPList struct {
-	Ctx        context.Context
-	Kubeconfig string
+func Command() *cobra.Command {
+	return &cobra.Command{
+		Use:   "list",
+		Short: "List all control planes",
+		Long:  `List all control planes managed by KubeFlex`,
+		Args:  cobra.ExactArgs(0),
+		Run: func(cmd *cobra.Command, args []string) {
+			flagset := cmd.Flags()
+			kubeconfig, _ := flagset.GetString(common.KubeconfigFlag)
+			cp := common.NewCP(kubeconfig)
+			ExecuteList(cp)
+		},
+	}
 }
 
 func getAge(creationTime time.Time) string {
@@ -36,32 +47,32 @@ func getAge(creationTime time.Time) string {
 	return duration.Round(time.Second).String()
 }
 
-func (cp *CPList) List(chattyStatus bool) {
+func ExecuteList(cp common.CP) {
 	client, err := client.GetClient(cp.Kubeconfig)
 	if err != nil {
 		fmt.Printf("Error getting client: %s\n", err)
 		os.Exit(1)
 	}
 
-	var cps tenancyv1alpha1.ControlPlaneList
-	if err := client.List(cp.Ctx, &cps); err != nil {
+	var controlPlanes tenancyv1alpha1.ControlPlaneList
+	if err := client.List(cp.Ctx, &controlPlanes); err != nil {
 		fmt.Printf("Error listing control planes: %s\n", err)
 		os.Exit(1)
 	}
 
-	if len(cps.Items) == 0 {
+	if len(controlPlanes.Items) == 0 {
 		fmt.Println("No control planes found.")
 		return
 	}
 
 	fmt.Println("Control Planes:")
 	fmt.Printf("%-20s %-10s %-10s %-10s %-10s\n", "NAME", "SYNCED", "READY", "TYPE", "AGE")
-	for _, cp := range cps.Items {
-		age := getAge(cp.CreationTimestamp.Time)
+	for _, controlPlane := range controlPlanes.Items {
+		age := getAge(controlPlane.CreationTimestamp.Time)
 		synced := "Unknown"
 		ready := "Unknown"
 
-		for _, condition := range cp.Status.Conditions {
+		for _, condition := range controlPlane.Status.Conditions {
 			if condition.Type == "Synced" {
 				synced = string(condition.Status)
 			}
@@ -70,6 +81,6 @@ func (cp *CPList) List(chattyStatus bool) {
 			}
 		}
 
-		fmt.Printf("%-20s %-10s %-10s %-10s %-10s\n", cp.Name, synced, ready, cp.Spec.Type, age)
+		fmt.Printf("%-20s %-10s %-10s %-10s %-10s\n", controlPlane.Name, synced, ready, controlPlane.Spec.Type, age)
 	}
-}
\ No newline at end of file
+}
diff --git a/cmd/kflex/main.go b/cmd/kflex/main.go
index 8dcd5177..49f07e81 100644
--- a/cmd/kflex/main.go
+++ b/cmd/kflex/main.go
@@ -17,52 +17,26 @@ limitations under the License.
 package main
 
 import (
-	"context"
 	"fmt"
 	"os"
-	"strconv"
-	"sync"
 
-	"github.com/go-logr/logr"
-	"github.com/go-logr/zapr"
-	tenancyv1alpha1 "github.com/kubestellar/kubeflex/api/v1alpha1"
 	"github.com/kubestellar/kubeflex/cmd/kflex/adopt"
 	"github.com/kubestellar/kubeflex/cmd/kflex/common"
-	cr "github.com/kubestellar/kubeflex/cmd/kflex/create"
-	cont "github.com/kubestellar/kubeflex/cmd/kflex/ctx"
-	del "github.com/kubestellar/kubeflex/cmd/kflex/delete"
-	in "github.com/kubestellar/kubeflex/cmd/kflex/init"
-	cluster "github.com/kubestellar/kubeflex/cmd/kflex/init/cluster"
+	"github.com/kubestellar/kubeflex/cmd/kflex/create"
+	kflexCtx "github.com/kubestellar/kubeflex/cmd/kflex/ctx"
+	"github.com/kubestellar/kubeflex/cmd/kflex/delete"
+	kflexInit "github.com/kubestellar/kubeflex/cmd/kflex/init"
 	"github.com/kubestellar/kubeflex/cmd/kflex/list"
-	"github.com/kubestellar/kubeflex/pkg/client"
-	"github.com/kubestellar/kubeflex/pkg/util"
+	"github.com/kubestellar/kubeflex/cmd/kflex/version"
 	"github.com/spf13/cobra"
-	"go.uber.org/zap"
+	"k8s.io/client-go/tools/clientcmd"
 )
 
-var createkind bool
-var kubeconfig string
-var verbosity int
+// Version injected by makefile:LDFLAGS
 var Version string
-var BuildDate string
-var CType string
-var BkType string
-var Hook string
-var domain string
-var externalPort int
-var chattyStatus bool
-var hookVars []string
-var hostContainer string
-var overwriteExistingCtx bool
-var setCurrentCtxAsHosting bool
-var adoptedKubeconfig string
-var adoptedContext string
-var adoptedURLOverride string
-var adoptedTokenExpirationSeconds int64
 
-// defaults
-const BKTypeDefault = string(tenancyv1alpha1.BackendDBTypeShared)
-const CTypeDefault = string(tenancyv1alpha1.ControlPlaneTypeK8S)
+// BuildDate injected by makefile:LDFLAGS
+var BuildDate string
 
 var rootCmd = &cobra.Command{
 	Use:   "kflex",
@@ -70,250 +44,21 @@ var rootCmd = &cobra.Command{
 	Long:  `A flexible and scalable solution for running Kubernetes control plane APIs`,
 }
 
-var versionCmd = &cobra.Command{
-	Use:   "version",
-	Short: "Provide version info",
-	Long:  `Provide kubeflex version info for CLI`,
-	Args:  cobra.ExactArgs(0),
-	Run: func(cmd *cobra.Command, args []string) {
-		fmt.Printf("Kubeflex version: %s %s\n", Version, BuildDate)
-		kubeVersionInfo, err := util.GetKubernetesClusterVersionInfo(kubeconfig)
-		if err != nil {
-			fmt.Printf("Could not connect to a Kubernetes cluster: %s\n", err)
-			os.Exit(1)
-		}
-		fmt.Printf("Kubernetes version: %s\n", kubeVersionInfo)
-	},
-}
-
-var initCmd = &cobra.Command{
-	Use:   "init",
-	Short: "Initialize kubeflex",
-	Long:  `Installs the default shared storage backend and the kubeflex operator`,
-	Args:  cobra.ExactArgs(0),
-	Run: func(cmd *cobra.Command, args []string) {
-		done := make(chan bool)
-		var wg sync.WaitGroup
-		var isOCP bool
-
-		util.PrintStatus("Checking if OpenShift cluster...", done, &wg, chattyStatus)
-		clientsetp, err := client.GetClientSet(kubeconfig)
-		if err == nil {
-			isOCP = util.IsOpenShift(*clientsetp)
-			if isOCP {
-				done <- true
-				util.PrintStatus("OpenShift cluster detected", done, &wg, chattyStatus)
-			}
-		}
-		done <- true
-
-		ctx := createContext()
-		if createkind {
-			if isOCP {
-				fmt.Fprintf(os.Stderr, "OpenShift cluster detected on existing context\n")
-				fmt.Fprintf(os.Stdout, "Switch to a non-OpenShift context with `kubectl config use-context <context-name>` and retry.\n")
-				os.Exit(1)
-			}
-			cluster.CreateKindCluster(chattyStatus)
-		}
-		in.Init(ctx, kubeconfig, Version, BuildDate, domain, strconv.Itoa(externalPort), hostContainer, chattyStatus, isOCP)
-		wg.Wait()
-	},
-}
-
-var createCmd = &cobra.Command{
-	Use:   "create <name>",
-	Short: "Create a control plane instance",
-	Long: `Create a control plane instance and switches the Kubeconfig context to
-	        the current instance`,
-	Args: cobra.ExactArgs(1),
-	Run: func(cmd *cobra.Command, args []string) {
-		cp := cr.CPCreate{
-			CP: common.CP{
-				Ctx:        createContext(),
-				Name:       args[0],
-				Kubeconfig: kubeconfig,
-			},
-		}
-		if CType == "" {
-			CType = CTypeDefault
-		}
-		if BkType == "" {
-			BkType = BKTypeDefault
-		}
-		// create passing the control plane type and backend type
-		cp.Create(CType, BkType, Hook, hookVars, chattyStatus)
-	},
-}
-
-var adoptCmd = &cobra.Command{
-	Use:   "adopt <name>",
-	Short: "Adopt a control plane from an external cluster",
-	Long: `Adopt a control plane from an external cluster and switches the Kubeconfig context to
-	        the current instance`,
-	Args: cobra.ExactArgs(1),
-	Run: func(cmd *cobra.Command, args []string) {
-		cp := adopt.CPAdopt{
-			CP: common.CP{
-				Ctx:        createContext(),
-				Name:       args[0],
-				Kubeconfig: kubeconfig,
-			},
-			AdoptedKubeconfig:             adoptedKubeconfig,
-			AdoptedContext:                adoptedContext,
-			AdoptedURLOverride:            adoptedURLOverride,
-			AdoptedTokenExpirationSeconds: adoptedTokenExpirationSeconds,
-		}
-		// create passing the control plane type and backend type
-		cp.Adopt(Hook, hookVars, chattyStatus)
-	},
-}
-
-var deleteCmd = &cobra.Command{
-	Use:   "delete <name>",
-	Short: "Delete a control plane instance",
-	Long: `Delete a control plane instance and switches the context back to
-	        the hosting cluster context`,
-	Args: cobra.ExactArgs(1),
-	Run: func(cmd *cobra.Command, args []string) {
-		cp := del.CPDelete{
-			CP: common.CP{
-				Ctx:        createContext(),
-				Name:       args[0],
-				Kubeconfig: kubeconfig,
-			},
-		}
-		cp.Delete(chattyStatus)
-	},
-}
-
-var ctxGetCmd = &cobra.Command{
-	Use:   "get",
-	Short: "Get the current kubeconfig context",
-	Long:  `Retrieve and display the current context from the kubeconfig file`,
-	Args:  cobra.ExactArgs(0),
-	Run: func(cmd *cobra.Command, args []string) {
-		cp := cont.CPCtx{
-			CP: common.CP{
-				Ctx:        createContext(),
-				Kubeconfig: kubeconfig,
-			},
-		}
-		cp.GetCurrentContext()
-	},
-}
-
-var ctxCmd = &cobra.Command{
-	Use:   "ctx",
-	Short: "Switch or get kubeconfig context",
-	Long: `Running without an argument switches the context back to the hosting cluster context,
-			        while providing the control plane name as argument switches the context to
-					that control plane. Use 'get' to retrieve the current context.`,
-	Args: cobra.MaximumNArgs(1),
-	Run: func(cmd *cobra.Command, args []string) {
-		cpName := ""
-		if len(args) == 1 {
-			cpName = args[0]
-		}
-		cp := cont.CPCtx{
-			CP: common.CP{
-				Ctx:        createContext(),
-				Name:       cpName,
-				Kubeconfig: kubeconfig,
-			},
-		}
-		cp.Context(chattyStatus, true, overwriteExistingCtx, setCurrentCtxAsHosting)
-	},
-}
-
-var listCmd = &cobra.Command{
-	Use:   "list",
-	Short: "List all control planes",
-	Long:  `List all control planes managed by KubeFlex`,
-	Args:  cobra.ExactArgs(0),
-	Run: func(cmd *cobra.Command, args []string) {
-		cp := list.CPList{
-			Ctx:        createContext(),
-			Kubeconfig: kubeconfig,
-		}
-		cp.List(chattyStatus)
-	},
-}
-
-var listCtxCmd = &cobra.Command{
-	Use:   "list",
-	Short: "List all available contexts",
-	Long:  `List all available contexts in the kubeconfig file`,
-	Args:  cobra.ExactArgs(0),
-	Run: func(cmd *cobra.Command, args []string) {
-		cp := cont.CPCtx{
-			CP: common.CP{
-				Ctx:        createContext(),
-				Kubeconfig: kubeconfig,
-			},
-		}
-		cp.ListContexts()
-	},
-}
-
 func init() {
-	versionCmd.Flags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "path to the kubeconfig file for the KubeFlex hosting cluster. If not specified, and $KUBECONFIG is set, it uses the value in $KUBECONFIG, otherwise it falls back to ${HOME}/.kube/configg")
-	versionCmd.Flags().BoolVarP(&chattyStatus, "chatty-status", "s", true, "chatty status indicator")
-
-	initCmd.Flags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "path to the kubeconfig file for the KubeFlex hosting cluster. If not specified, and $KUBECONFIG is set, it uses the value in $KUBECONFIG, otherwise it falls back to ${HOME}/.kube/config")
-	initCmd.Flags().IntVarP(&verbosity, "verbosity", "v", 0, "log level") // TODO - figure out how to inject verbosity
-	initCmd.Flags().BoolVarP(&createkind, "create-kind", "c", false, "Create and configure a kind cluster for installing Kubeflex")
-	initCmd.Flags().StringVarP(&domain, "domain", "d", "localtest.me", "domain for FQDN")
-	initCmd.Flags().StringVarP(&hostContainer, "hostContainerName", "n", "kubeflex-control-plane", "Name of the hosting cluster container (kind or k3d only)")
-	initCmd.Flags().IntVarP(&externalPort, "externalPort", "p", 9443, "external port used by ingress")
-	initCmd.Flags().BoolVarP(&chattyStatus, "chatty-status", "s", true, "chatty status indicator")
-
-	createCmd.Flags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "path to the kubeconfig file for the KubeFlex hosting cluster. If not specified, it defaults to the value set in $KUBECONFIG, and if $KUBECONFIG is not set, it falls back to ${HOME}/.kube/config.")
-	createCmd.Flags().IntVarP(&verbosity, "verbosity", "v", 0, "log level") // TODO - figure out how to inject verbosity
-	createCmd.Flags().StringVarP(&CType, "type", "t", "", "type of control plane: k8s|ocm|vcluster")
-	createCmd.Flags().StringVarP(&BkType, "backend-type", "b", "", "backend DB sharing: shared|dedicated")
-	createCmd.Flags().StringVarP(&Hook, "postcreate-hook", "p", "", "name of post create hook to run")
-	createCmd.Flags().BoolVarP(&chattyStatus, "chatty-status", "s", true, "chatty status indicator")
-	createCmd.Flags().StringArrayVarP(&hookVars, "set", "e", []string{}, "set post create hook variables, in the form name=value ")
-
-	adoptCmd.Flags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "path to the kubeconfig file for the KubeFlex hosting cluster. If not specified, it defaults to the value set in $KUBECONFIG, and if $KUBECONFIG is not set, it falls back to ${HOME}/.kube/config.")
-	adoptCmd.Flags().IntVarP(&verbosity, "verbosity", "v", 0, "log level") // TODO - figure out how to inject verbosity
-	adoptCmd.Flags().StringVarP(&Hook, "postcreate-hook", "p", "", "name of post create hook to run")
-	adoptCmd.Flags().BoolVarP(&chattyStatus, "chatty-status", "s", true, "chatty status indicator")
-	adoptCmd.Flags().StringArrayVarP(&hookVars, "set", "e", []string{}, "set post create hook variables, in the form name=value ")
-	adoptCmd.Flags().StringVarP(&adoptedKubeconfig, "adopted-kubeconfig", "a", "", "path to the kubeconfig file for the adopted cluster. If unspecified, it uses the default Kubeconfig")
-	adoptCmd.Flags().StringVarP(&adoptedContext, "adopted-context", "c", "", "path to adopted cluster context in adopted kubeconfig")
-	adoptCmd.Flags().StringVarP(&adoptedURLOverride, "url-override", "u", "", "URL overrride for adopted cluster. Required when cluster address uses local host address, e.g. `https://127.0.0.1` ")
-	adoptCmd.Flags().Int64VarP(&adoptedTokenExpirationSeconds, "expiration-seconds", "x", 86400*365, "adopted token expiration in seconds. Default is one year.")
-
-	deleteCmd.Flags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "path to the kubeconfig file for the KubeFlex hosting cluster. If not specified, it defaults to the value set in $KUBECONFIG, and if $KUBECONFIG is not set, it falls back to ${HOME}/.kube/config.")
-	deleteCmd.Flags().IntVarP(&verbosity, "verbosity", "v", 0, "log level") // TODO - figure out how to inject verbosity
-	deleteCmd.Flags().BoolVarP(&chattyStatus, "chatty-status", "s", true, "chatty status indicator")
-
-	ctxCmd.Flags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "path to the kubeconfig file for the KubeFlex hosting cluster. If not specified, it defaults to the value set in $KUBECONFIG, and if $KUBECONFIG is not set, it falls back to ${HOME}/.kube/config.")
-	ctxCmd.Flags().IntVarP(&verbosity, "verbosity", "v", 0, "log level") // TODO - figure out how to inject verbosity
-	ctxCmd.Flags().BoolVarP(&chattyStatus, "chatty-status", "s", true, "chatty status indicator")
-	ctxCmd.Flags().BoolVarP(&overwriteExistingCtx, "overwrite-existing-context", "o", false, "Overwrite of hosting cluster context with new control plane context")
-	ctxCmd.Flags().BoolVarP(&setCurrentCtxAsHosting, "set-current-for-hosting", "c", false, "Set current context as hosting cluster context")
-
-	ctxCmd.AddCommand(ctxGetCmd)
-	ctxCmd.AddCommand(listCtxCmd)
-
-	rootCmd.AddCommand(versionCmd)
-	rootCmd.AddCommand(initCmd)
-	rootCmd.AddCommand(createCmd)
-	rootCmd.AddCommand(adoptCmd)
-	rootCmd.AddCommand(deleteCmd)
-	rootCmd.AddCommand(ctxCmd)
-	rootCmd.AddCommand(listCmd)
+	pflagset := rootCmd.PersistentFlags()
+	pflagset.StringP(common.KubeconfigFlag, "k", clientcmd.RecommendedHomeFile, "path to the kubeconfig file for the KubeFlex hosting cluster. If not specified, and $KUBECONFIG is set, it uses the value in $KUBECONFIG, otherwise it falls back to ${HOME}/.kube/config")
+	pflagset.BoolP(common.ChattyStatusFlag, "s", true, "chatty status indicator")
+	pflagset.IntP(common.VerbosityFlag, "v", 0, "log level") // TODO - figure out how to inject verbosity
+	rootCmd.AddCommand(kflexCtx.Command())
+	rootCmd.AddCommand(version.Command())
+	rootCmd.AddCommand(kflexInit.Command())
+	rootCmd.AddCommand(adopt.Command())
+	rootCmd.AddCommand(delete.Command())
+	rootCmd.AddCommand(create.Command())
+	rootCmd.AddCommand(list.Command())
 }
 
 // TODO - work on passing the verbosity to the logger
-func createContext() context.Context {
-	zapLogger, _ := zap.NewDevelopment(zap.AddCaller())
-	logger := zapr.NewLoggerWithOptions(zapLogger)
-	return logr.NewContext(context.Background(), logger)
-}
 
 func main() {
 	if err := rootCmd.Execute(); err != nil {
diff --git a/cmd/kflex/version/version.go b/cmd/kflex/version/version.go
new file mode 100644
index 00000000..04025490
--- /dev/null
+++ b/cmd/kflex/version/version.go
@@ -0,0 +1,35 @@
+package version
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/kubestellar/kubeflex/cmd/kflex/common"
+	"github.com/kubestellar/kubeflex/pkg/util"
+	"github.com/spf13/cobra"
+)
+
+func Command() *cobra.Command {
+	command := &cobra.Command{
+		Use:   "version",
+		Short: "Provide version info",
+		Long:  `Provide kubeflex version info for CLI`,
+		Args:  cobra.ExactArgs(0),
+		Run: func(cmd *cobra.Command, args []string) {
+			flagset := cmd.Flags()
+			kubeconfig, _ := flagset.GetString(common.KubeconfigFlag)
+			ExecuteVersion(kubeconfig, common.Version, common.BuildDate)
+		},
+	}
+	return command
+}
+
+func ExecuteVersion(kubeconfig string, version string, buildDate string) {
+	fmt.Printf("Kubeflex version: %s %s\n", version, buildDate)
+	kubeVersionInfo, err := util.GetKubernetesClusterVersionInfo(kubeconfig)
+	if err != nil {
+		fmt.Printf("Could not connect to a Kubernetes cluster: %s\n", err)
+		os.Exit(1)
+	}
+	fmt.Printf("Kubernetes version: %s\n", kubeVersionInfo)
+}
diff --git a/docs/debugging.md b/docs/debugging.md
index 443b7108..4ce6d76d 100644
--- a/docs/debugging.md
+++ b/docs/debugging.md
@@ -34,7 +34,7 @@ openssl x509 -noout -text -in certs/apiserver.crt
 ### Manually creating the configmap with KubeFlex defaults
 
 ```shell
-create configmap kubeflex-config -n kubeflex-system --from-literal=externalPort=9443 --from-literal=domain=localtest.me
+kubectl create configmap kubeflex-config -n kubeflex-system --from-literal=externalPort=9443 --from-literal=domain=localtest.me
 ```
 
 ### Get decoded value from secret
diff --git a/docs/users.md b/docs/users.md
index 20a0ddae..adb1d52e 100644
--- a/docs/users.md
+++ b/docs/users.md
@@ -79,7 +79,7 @@ helm install ingress-nginx ingress-nginx --set "controller.extraArgs.enable-ssl-
 
 
 ```shell
-kflex init --hostContainerName k3d-kubeflex-server-0
+kflex init --host-container-name k3d-kubeflex-server-0
 ```
 
 ### Installing on OpenShift
diff --git a/pkg/kubeconfig/kubeconfig.go b/pkg/kubeconfig/kubeconfig.go
index 113c028e..e31a832f 100644
--- a/pkg/kubeconfig/kubeconfig.go
+++ b/pkg/kubeconfig/kubeconfig.go
@@ -175,18 +175,18 @@ func WaitForNamespaceReady(ctx context.Context, clientset kubernetes.Interface,
 func adjustConfigKeys(config *clientcmdapi.Config, cpName, controlPlaneType string) {
 	switch controlPlaneType {
 	case string(tenancyv1alpha1.ControlPlaneTypeOCM):
-		renameKey(config.Clusters, "multicluster-controlplane", certs.GenerateClusterName(cpName))
-		renameKey(config.AuthInfos, "user", certs.GenerateAuthInfoAdminName(cpName))
-		renameKey(config.Contexts, "multicluster-controlplane", certs.GenerateContextName(cpName))
+		RenameKey(config.Clusters, "multicluster-controlplane", certs.GenerateClusterName(cpName))
+		RenameKey(config.AuthInfos, "user", certs.GenerateAuthInfoAdminName(cpName))
+		RenameKey(config.Contexts, "multicluster-controlplane", certs.GenerateContextName(cpName))
 		config.CurrentContext = certs.GenerateContextName(cpName)
 		config.Contexts[certs.GenerateContextName(cpName)] = &clientcmdapi.Context{
 			Cluster:  certs.GenerateClusterName(cpName),
 			AuthInfo: certs.GenerateAuthInfoAdminName(cpName),
 		}
 	case string(tenancyv1alpha1.ControlPlaneTypeVCluster):
-		renameKey(config.Clusters, "my-vcluster", certs.GenerateClusterName(cpName))
-		renameKey(config.AuthInfos, "my-vcluster", certs.GenerateAuthInfoAdminName(cpName))
-		renameKey(config.Contexts, "my-vcluster", certs.GenerateContextName(cpName))
+		RenameKey(config.Clusters, "my-vcluster", certs.GenerateClusterName(cpName))
+		RenameKey(config.AuthInfos, "my-vcluster", certs.GenerateAuthInfoAdminName(cpName))
+		RenameKey(config.Contexts, "my-vcluster", certs.GenerateContextName(cpName))
 		config.CurrentContext = certs.GenerateContextName(cpName)
 		config.Contexts[certs.GenerateContextName(cpName)] = &clientcmdapi.Context{
 			Cluster:  certs.GenerateClusterName(cpName),
@@ -197,7 +197,7 @@ func adjustConfigKeys(config *clientcmdapi.Config, cpName, controlPlaneType stri
 	}
 }
 
-func renameKey(m interface{}, oldKey string, newKey string) interface{} {
+func RenameKey(m interface{}, oldKey string, newKey string) interface{} {
 	switch v := m.(type) {
 	case map[string]*clientcmdapi.Cluster:
 		if cluster, ok := v[oldKey]; ok {


Reviews:
user: login: pdettori
body: Thanks @rxinui - overall it looks good to me - there is more clarity is the usage of some variables and the structure you propose for the create and list command is what I had in mind.  
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T02:56:53Z
user: login: pdettori
body: In addition to inline testing, here are a result of manual testing:
`kflex list` does not print the names of the control planes
```
$ kflex list
Control Planes:
NAME                 SYNCED     READY      TYPE       AGE       
                     True       True       vcluster   18h39m55s 
                     True       True       k8s        18h39m55s 
 ```                     
 e2e testing has passed and has exercised most commands  
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T19:32:37Z
user: login: rxinui
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T20:18:09Z
user: login: pdettori
body: /lgtm - thank you @rxinui !
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-10T02:02:38Z

Review comments:
diff_hunk: @@ -31,10 +34,44 @@ type CP struct {
 	Ctx        context.Context
 	Kubeconfig string
 	Name       string
+	AliasName  string
 }
 
+type CPOption func(*CP)
+
+// New control plane with option design pattern
+func NewCP(kubeconfigPath string, options ...CPOption) CP {
+	cp := CP{Ctx: createContext(), Kubeconfig: kubeconfigPath}
+	for _, option := range options {
+		option(&cp)
+	}
+	return cp
+}
+
+// Make Name optional using option design pattern
+func WithName(name string) CPOption {
+	return func(cp *CP) {
+		cp.Name = name
+	}
+}
+
+// Make AliasName optional using option design pattern
+func WithAliasName(aliasName string) CPOption {
+	return func(cp *CP) {
+		cp.AliasName = aliasName
+	}
+}
+
+// Create a new system context with logger
+func createContext() context.Context {
+	zapLogger, _ := zap.NewDevelopment(zap.AddCaller())
+	logger := zapr.NewLoggerWithOptions(zapLogger)
+	return logr.NewContext(context.Background(), logger)
+}
+
+// REFACTOR: the usage of the variable `cp` as *common.CP and then overshadowed by `cp` as tenancyv1alpha1.ControlPlane was tricky. Took a while to understand. Hence, `cp` from tenancyv1alpha1 becomes `controlPlane` and common.CP is referred by `cp`
path: cmd/kflex/common/cp.go
user: login: pdettori
body: good point @rxinui - it is much more clear now.
created_at: 2025-05-08T02:45:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,26 @@
+/*
+Copyright 2023 The KubeStellar Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package common
+
+import "k8s.io/client-go/tools/clientcmd"
+
+const (
+	KubeconfigFlag   = clientcmd.RecommendedConfigPathFlag
+	ChattyStatusFlag = "chatty-status"
+	VerbosityFlag    = "verbosity"
+	SetFlag          = "set"
+)
path: cmd/kflex/common/flags.go
user: login: pdettori
body: yes, this was overdue, there was repetition of the flags
created_at: 2025-05-08T02:47:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,61 +33,101 @@ import (
 	"github.com/kubestellar/kubeflex/pkg/util"
 )
 
-type CPCreate struct {
-	common.CP
+// defaults
+const (
+	BKTypeDefault        = string(tenancyv1alpha1.BackendDBTypeShared) // REFACTOR? local to create or common
+	CTypeDefault         = string(tenancyv1alpha1.ControlPlaneTypeK8S) // REFACTOR? local to create or common
+	ControlPlaneTypeFlag = "type"
+	BackendTypeFlag      = "backend-type"
+	PostCreateHookFlag   = "postcreate-hook"
+	ChattyStatusFlag     = "chatty-status"
+)
+
+// REFACTOR: removed variables such as `hookVars` as they are used by multiple commands (create and adopt...). It should be defined locally to each command package instead of pointing to the same variable defined in main avoiding extreme edge cases.
+
path: cmd/kflex/create/create.go
user: login: pdettori
body: good catch
created_at: 2025-05-08T02:47:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,42 +60,78 @@ type CPAdopt struct {
 	AdoptedTokenExpirationSeconds int64
 }
 
+func Command() *cobra.Command {
+	command := &cobra.Command{
+		Use:   "adopt <name>",
+		Short: "Adopt a control plane from an external cluster",
+		Long: `Adopt a control plane from an external cluster and switches the Kubeconfig context to
+				the current instance`,
+		Args: cobra.ExactArgs(1),
+		Run: func(cmd *cobra.Command, args []string) {
+			flagset := cmd.Flags()
+			kubeconfig, _ := flagset.GetString(common.KubeconfigFlag)
+			postCreateHook, _ := flagset.GetString(common.PostCreateHookFlag)
+			hookVars, _ := flagset.GetStringArray(common.SetFlag)
+			chattyStatus, _ := flagset.GetBool(common.ChattyStatusFlag)
+			adoptedKubeconfig, _ := flagset.GetString(AdoptedKubeconfigFlag)
+			adoptedContext, _ := flagset.GetString(AdoptedContextFlag)
+			adoptedURLOverride, _ := flagset.GetString(URLOverrideFlag)
+			adoptedTokenExpirationSeconds, _ := flagset.GetInt64(ExpirationSecondsFlag)
+			cp := CPAdopt{
+				CP:                            common.NewCP(kubeconfig, common.WithName(args[0])),
+				AdoptedKubeconfig:             adoptedKubeconfig,
+				AdoptedContext:                adoptedContext,
+				AdoptedURLOverride:            adoptedURLOverride,
+				AdoptedTokenExpirationSeconds: adoptedTokenExpirationSeconds,
+			}
+			ExecuteAdopt(cp, postCreateHook, hookVars, chattyStatus)
+		},
+	}
+	flagset := command.Flags()
+	// create passing the control plane type and backend type
+	flagset.StringP(common.PostCreateHookFlag, "p", "", "name of post create hook to run")
+	flagset.StringArrayP(common.SetFlag, "e", []string{}, "set post create hook variables, in the form name=value ")
+	flagset.StringP(AdoptedKubeconfigFlag, "a", "", "path to the kubeconfig file for the adopted cluster. If unspecified, it uses the default Kubeconfig")
+	flagset.StringP(AdoptedContextFlag, "c", "", "path to adopted cluster context in adopted kubeconfig")
+	flagset.StringP(URLOverrideFlag, "u", "", "URL overrride for adopted cluster. Required when cluster address uses local host address, e.g. `https://127.0.0.1`")
+	flagset.Int64P(ExpirationSecondsFlag, "x", defaultExpirationSeconds, "adopted token expiration in seconds. Default is one year.")
+	return command
+}
+
 // Adopt a control plane from another cluster
-func (c *CPAdopt) Adopt(hook string, hookVars []string, chattyStatus bool) {
+func ExecuteAdopt(cp CPAdopt, hook string, hookVars []string, chattyStatus bool) {
 	done := make(chan bool)
 	var wg sync.WaitGroup
 	cx := cont.CPCtx{}
-	cx.Context(chattyStatus, false, false, false)
+	cx.ExecuteCtx(chattyStatus, false, false, false)
 
 	controlPlaneType := tenancyv1alpha1.ControlPlaneTypeExternal
-	util.PrintStatus(fmt.Sprintf("Adopting control plane %s of type %s ...", c.Name, controlPlaneType), done, &wg, chattyStatus)
+	util.PrintStatus(fmt.Sprintf("Adopting control plane %s of type %s ...", cp.Name, controlPlaneType), done, &wg, chattyStatus)
 
-	bootstrapKubeconfig := getBootstrapKubeconfig(c)
-
-	cl, err := kfclient.GetClient(c.Kubeconfig)
+	cl, err := kfclient.GetClient(cp.Kubeconfig)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error getting kubeflex client: %v\n", err)
 		os.Exit(1)
 	}
 
-	clientsetp, err := kfclient.GetClientSet(c.Kubeconfig)
+	clientsetp, err := kfclient.GetClientSet(cp.Kubeconfig)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error getting clientset: %v\n", err)
 		os.Exit(1)
 	}
 
-	if err := applyAdoptedBootstrapSecret(clientsetp, c.Name, bootstrapKubeconfig, c.AdoptedContext, c.AdoptedURLOverride); err != nil {
+	if err := applyAdoptedBootstrapSecret(clientsetp, cp.Name, getBootstrapKubeconfig(cp), cp.AdoptedContext, cp.AdoptedURLOverride); err != nil {
 		fmt.Fprintf(os.Stderr, "error creating adopted cluster kubeconfig: %v\n", err)
 		os.Exit(1)
 	}
-
-	cp, err := common.GenerateControlPlane(c.Name, string(controlPlaneType), "", hook, hookVars, &c.AdoptedTokenExpirationSeconds)
+	// REFACTOR? is cp being pointer of (*CPAdopt) make sense?
+	controlPlane, err := common.GenerateControlPlane(cp.Name, string(controlPlaneType), "", hook, hookVars, &cp.AdoptedTokenExpirationSeconds)
 	if err != nil {
path: cmd/kflex/adopt/adopt.go
user: login: pdettori
body: I suggest to call it `cpa` or `cpAdopt` for more clarity
created_at: 2025-05-09T18:56:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -231,12 +277,13 @@ func isValidServerURL(serverURL string) error {
 	return nil
 }
 
-func getBootstrapKubeconfig(c *CPAdopt) string {
-	if c.AdoptedKubeconfig != "" {
-		return c.AdoptedKubeconfig
+// REFACTOR: no reason to be a pointer
path: cmd/kflex/adopt/adopt.go
user: login: pdettori
body: this has already been refactored, right ? The comment should no longer be needed
created_at: 2025-05-09T18:58:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,10 +34,44 @@ type CP struct {
 	Ctx        context.Context
 	Kubeconfig string
 	Name       string
+	AliasName  string
 }
 
+type CPOption func(*CP)
+
+// New control plane with option design pattern
+func NewCP(kubeconfigPath string, options ...CPOption) CP {
+	cp := CP{Ctx: createContext(), Kubeconfig: kubeconfigPath}
+	for _, option := range options {
+		option(&cp)
+	}
+	return cp
+}
+
+// Make Name optional using option design pattern
+func WithName(name string) CPOption {
+	return func(cp *CP) {
+		cp.Name = name
+	}
+}
+
+// Make AliasName optional using option design pattern
+func WithAliasName(aliasName string) CPOption {
+	return func(cp *CP) {
+		cp.AliasName = aliasName
+	}
+}
+
+// Create a new system context with logger
+func createContext() context.Context {
+	zapLogger, _ := zap.NewDevelopment(zap.AddCaller())
+	logger := zapr.NewLoggerWithOptions(zapLogger)
+	return logr.NewContext(context.Background(), logger)
+}
+
+// REFACTOR: the usage of the variable `cp` as *common.CP and then overshadowed by `cp` as tenancyv1alpha1.ControlPlane was tricky. Took a while to understand. Hence, `cp` from tenancyv1alpha1 becomes `controlPlane` and common.CP is referred by `cp`
path: cmd/kflex/common/cp.go
user: login: pdettori
body: comment should be no longer necessary since this was already done
created_at: 2025-05-09T18:59:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,61 +33,99 @@ import (
 	"github.com/kubestellar/kubeflex/pkg/util"
 )
 
-type CPCreate struct {
-	common.CP
+// defaults
+const (
+	BKTypeDefault        = string(tenancyv1alpha1.BackendDBTypeShared) // REFACTOR? local to create or common
+	CTypeDefault         = string(tenancyv1alpha1.ControlPlaneTypeK8S) // REFACTOR? local to create or common
+	ControlPlaneTypeFlag = "type"
+	BackendTypeFlag      = "backend-type"
+)
+
+// REFACTOR: removed variables such as `hookVars` as they are used by multiple commands (create and adopt...). It should be defined locally to each command package instead of pointing to the same variable defined in main avoiding extreme edge cases.
path: cmd/kflex/create/create.go
user: login: pdettori
body: same comment as before - this was already done
created_at: 2025-05-09T19:00:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,61 +33,99 @@ import (
 	"github.com/kubestellar/kubeflex/pkg/util"
 )
 
-type CPCreate struct {
-	common.CP
+// defaults
+const (
+	BKTypeDefault        = string(tenancyv1alpha1.BackendDBTypeShared) // REFACTOR? local to create or common
+	CTypeDefault         = string(tenancyv1alpha1.ControlPlaneTypeK8S) // REFACTOR? local to create or common
+	ControlPlaneTypeFlag = "type"
+	BackendTypeFlag      = "backend-type"
+)
+
+// REFACTOR: removed variables such as `hookVars` as they are used by multiple commands (create and adopt...). It should be defined locally to each command package instead of pointing to the same variable defined in main avoiding extreme edge cases.
+
+func Command() *cobra.Command {
+	command := &cobra.Command{
+		Use:   "create <name>",
+		Short: "Create a control plane instance",
+		Long: `Create a control plane instance and switches the Kubeconfig context to
+	        the current instance`,
+		Args: cobra.ExactArgs(1),
+		Run: func(cmd *cobra.Command, args []string) {
+			flagset := cmd.Flags()
+			kubeconfig, _ := flagset.GetString(common.KubeconfigFlag)
+			chattyStatus, _ := flagset.GetBool(common.ChattyStatusFlag)
+			cpType, _ := flagset.GetString(ControlPlaneTypeFlag)
+			backendType, _ := flagset.GetString(BackendTypeFlag)
+			postCreateHook, _ := flagset.GetString(common.PostCreateHookFlag)
+			hookVars, _ := flagset.GetStringArray(common.SetFlag)
+			cp := common.NewCP(kubeconfig, common.WithName(args[0]))
+			// create passing the control plane type and backend type
+			ExecuteCreate(cp, cpType, backendType, postCreateHook, hookVars, chattyStatus)
+		},
+	}
+
+	flagset := command.Flags()
+	// REFACTOR: putting CTypeDefault as default value, hence if empty string given to the flag, it picks up
path: cmd/kflex/create/create.go
user: login: pdettori
body: comment should no longer be necessary
created_at: 2025-05-09T19:08:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,61 +33,99 @@ import (
 	"github.com/kubestellar/kubeflex/pkg/util"
 )
 
-type CPCreate struct {
-	common.CP
+// defaults
+const (
+	BKTypeDefault        = string(tenancyv1alpha1.BackendDBTypeShared) // REFACTOR? local to create or common
+	CTypeDefault         = string(tenancyv1alpha1.ControlPlaneTypeK8S) // REFACTOR? local to create or common
+	ControlPlaneTypeFlag = "type"
+	BackendTypeFlag      = "backend-type"
+)
+
+// REFACTOR: removed variables such as `hookVars` as they are used by multiple commands (create and adopt...). It should be defined locally to each command package instead of pointing to the same variable defined in main avoiding extreme edge cases.
+
+func Command() *cobra.Command {
+	command := &cobra.Command{
+		Use:   "create <name>",
+		Short: "Create a control plane instance",
+		Long: `Create a control plane instance and switches the Kubeconfig context to
+	        the current instance`,
+		Args: cobra.ExactArgs(1),
+		Run: func(cmd *cobra.Command, args []string) {
+			flagset := cmd.Flags()
+			kubeconfig, _ := flagset.GetString(common.KubeconfigFlag)
+			chattyStatus, _ := flagset.GetBool(common.ChattyStatusFlag)
+			cpType, _ := flagset.GetString(ControlPlaneTypeFlag)
+			backendType, _ := flagset.GetString(BackendTypeFlag)
+			postCreateHook, _ := flagset.GetString(common.PostCreateHookFlag)
+			hookVars, _ := flagset.GetStringArray(common.SetFlag)
+			cp := common.NewCP(kubeconfig, common.WithName(args[0]))
+			// create passing the control plane type and backend type
+			ExecuteCreate(cp, cpType, backendType, postCreateHook, hookVars, chattyStatus)
+		},
+	}
+
+	flagset := command.Flags()
+	// REFACTOR: putting CTypeDefault as default value, hence if empty string given to the flag, it picks up
+	flagset.StringP(ControlPlaneTypeFlag, "t", CTypeDefault, "type of control plane: k8s|ocm|vcluster")
+	// REFACTOR: same than CTypeDefault for BKTypeDefault
path: cmd/kflex/create/create.go
user: login: pdettori
body: ditto
created_at: 2025-05-09T19:09:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,42 +60,78 @@ type CPAdopt struct {
 	AdoptedTokenExpirationSeconds int64
 }
 
+func Command() *cobra.Command {
+	command := &cobra.Command{
+		Use:   "adopt <name>",
+		Short: "Adopt a control plane from an external cluster",
+		Long: `Adopt a control plane from an external cluster and switches the Kubeconfig context to
+				the current instance`,
+		Args: cobra.ExactArgs(1),
+		Run: func(cmd *cobra.Command, args []string) {
+			flagset := cmd.Flags()
+			kubeconfig, _ := flagset.GetString(common.KubeconfigFlag)
+			postCreateHook, _ := flagset.GetString(common.PostCreateHookFlag)
+			hookVars, _ := flagset.GetStringArray(common.SetFlag)
+			chattyStatus, _ := flagset.GetBool(common.ChattyStatusFlag)
+			adoptedKubeconfig, _ := flagset.GetString(AdoptedKubeconfigFlag)
+			adoptedContext, _ := flagset.GetString(AdoptedContextFlag)
+			adoptedURLOverride, _ := flagset.GetString(URLOverrideFlag)
+			adoptedTokenExpirationSeconds, _ := flagset.GetInt64(ExpirationSecondsFlag)
+			cp := CPAdopt{
+				CP:                            common.NewCP(kubeconfig, common.WithName(args[0])),
+				AdoptedKubeconfig:             adoptedKubeconfig,
+				AdoptedContext:                adoptedContext,
+				AdoptedURLOverride:            adoptedURLOverride,
+				AdoptedTokenExpirationSeconds: adoptedTokenExpirationSeconds,
+			}
+			ExecuteAdopt(cp, postCreateHook, hookVars, chattyStatus)
+		},
+	}
+	flagset := command.Flags()
+	// create passing the control plane type and backend type
+	flagset.StringP(common.PostCreateHookFlag, "p", "", "name of post create hook to run")
+	flagset.StringArrayP(common.SetFlag, "e", []string{}, "set post create hook variables, in the form name=value ")
+	flagset.StringP(AdoptedKubeconfigFlag, "a", "", "path to the kubeconfig file for the adopted cluster. If unspecified, it uses the default Kubeconfig")
+	flagset.StringP(AdoptedContextFlag, "c", "", "path to adopted cluster context in adopted kubeconfig")
+	flagset.StringP(URLOverrideFlag, "u", "", "URL overrride for adopted cluster. Required when cluster address uses local host address, e.g. `https://127.0.0.1`")
+	flagset.Int64P(ExpirationSecondsFlag, "x", defaultExpirationSeconds, "adopted token expiration in seconds. Default is one year.")
+	return command
+}
+
 // Adopt a control plane from another cluster
-func (c *CPAdopt) Adopt(hook string, hookVars []string, chattyStatus bool) {
+func ExecuteAdopt(cp CPAdopt, hook string, hookVars []string, chattyStatus bool) {
 	done := make(chan bool)
 	var wg sync.WaitGroup
 	cx := cont.CPCtx{}
-	cx.Context(chattyStatus, false, false, false)
+	cx.ExecuteCtx(chattyStatus, false, false, false)
 
 	controlPlaneType := tenancyv1alpha1.ControlPlaneTypeExternal
-	util.PrintStatus(fmt.Sprintf("Adopting control plane %s of type %s ...", c.Name, controlPlaneType), done, &wg, chattyStatus)
+	util.PrintStatus(fmt.Sprintf("Adopting control plane %s of type %s ...", cp.Name, controlPlaneType), done, &wg, chattyStatus)
 
-	bootstrapKubeconfig := getBootstrapKubeconfig(c)
-
-	cl, err := kfclient.GetClient(c.Kubeconfig)
+	cl, err := kfclient.GetClient(cp.Kubeconfig)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error getting kubeflex client: %v\n", err)
 		os.Exit(1)
 	}
 
-	clientsetp, err := kfclient.GetClientSet(c.Kubeconfig)
+	clientsetp, err := kfclient.GetClientSet(cp.Kubeconfig)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error getting clientset: %v\n", err)
 		os.Exit(1)
 	}
 
-	if err := applyAdoptedBootstrapSecret(clientsetp, c.Name, bootstrapKubeconfig, c.AdoptedContext, c.AdoptedURLOverride); err != nil {
+	if err := applyAdoptedBootstrapSecret(clientsetp, cp.Name, getBootstrapKubeconfig(cp), cp.AdoptedContext, cp.AdoptedURLOverride); err != nil {
 		fmt.Fprintf(os.Stderr, "error creating adopted cluster kubeconfig: %v\n", err)
 		os.Exit(1)
 	}
-
-	cp, err := common.GenerateControlPlane(c.Name, string(controlPlaneType), "", hook, hookVars, &c.AdoptedTokenExpirationSeconds)
+	// REFACTOR? is cp being pointer of (*CPAdopt) make sense?
+	controlPlane, err := common.GenerateControlPlane(cp.Name, string(controlPlaneType), "", hook, hookVars, &cp.AdoptedTokenExpirationSeconds)
 	if err != nil {
path: cmd/kflex/adopt/adopt.go
user: login: rxinui
body: Agree, on it
created_at: 2025-05-09T20:18:09Z
author_association: CONTRIBUTOR

PR comments:
user: login: rxinui
created_at: 2025-05-08T01:40:23Z
body: @pdettori as discussed, I'm raising an intermediate PR as a Proof-of-concept before implementing the whole refactoring process, so we can decide together what is right and wrong.

CC: @clubanderson
author_association: CONTRIBUTOR
user: login: rxinui
created_at: 2025-05-08T01:44:35Z
body: **NOTE**: this branch has been checkout from #351 hence the changes in `pkg/kubeconfig.go`. Please ignore that.
author_association: CONTRIBUTOR
user: login: rxinui
created_at: 2025-05-09T14:20:17Z
body: # Refactoring is now complete

kflex commands that works

- create
- ctx
- ctx get
- ctx list
- help
- delete
- list

kflex command with a bug

- version ==> in fact, the version and build date are injected as LDFLAGS from the Makefile. I moved them from package main to package common and it seems that the path given in the makefile is not working. I would like some inputs from you there.

kflex commands not tested

- adopt
- completion
- init

## Notes

- Some potential refactoring can be done but haven't to avoid any big breaking. 
- Another refactoring phase will follow once this phase is confirmed.
author_association: CONTRIBUTOR
user: login: rxinui
created_at: 2025-05-09T20:57:10Z
body: @pdettori could you please retry `kflex list` and confirm its behaviour?
author_association: CONTRIBUTOR
user: login: pdettori
created_at: 2025-05-09T21:42:14Z
body: @rxinui now list works - thanks
author_association: CONTRIBUTOR