Repository: kubean-io/kubean (go)

Issue:
feature：automatically set the spray-job image version, when the image tag is empty
what need to do:
1. update the sample https://github.com/kubean-io/kubean/blob/main/artifacts/demo/kubeanClusterOps.yml:
change  "image: ghcr.m.daocloud.io/kubean-io/spray-job:latest" to " image: ghcr.m.daocloud.io/kubean-io/spray-job"
(maybe some other demo files will also be changed)

2. state the mechanism to user：when the spray-job image's tag is empty, kuebean will set the spray-job tag to same value of the kubean-operator tag

3. optimize the kubean code to achieve this function

Pull request description:
Optimize kubeanoperation spec.image tag
<!--  

Thanks for sending a pull request!  Here are some tips for you:

* make sure your commit is signed off

-->

**What type of PR is this?**
<!-- 
Uncomment only one ` /kind <>` line, hit enter to put that in a new line, and remove leading whitespaces from that line:

/kind api-change
/kind bug
/kind cleanup
/kind design
/kind documentation
/kind failing-test
/kind feature
/kind flake
-->

/kind bug

**What this PR does / why we need it**:

* automatically set the spray-job image version , fix #363 

**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #

**Special notes for your reviewer**:



Patch:
diff --git a/artifacts/demo/kubeanClusterOps.yml b/artifacts/demo/kubeanClusterOps.yml
index 0d69e08b0..ae71e00f6 100644
--- a/artifacts/demo/kubeanClusterOps.yml
+++ b/artifacts/demo/kubeanClusterOps.yml
@@ -6,7 +6,7 @@ metadata:
     clusterName: cluster1-demo
 spec:
   cluster: cluster1-demo
-  image: ghcr.m.daocloud.io/kubean-io/spray-job:latest # or change image to kubespray origin image version
+  image: ghcr.m.daocloud.io/kubean-io/spray-job # or add specific tag, such as v0.4.0
   backoffLimit: 0
   actionType: playbook
   action: cluster.yml
diff --git a/artifacts/offlineDemo/kubeanClusterOps.yml b/artifacts/offlineDemo/kubeanClusterOps.yml
index 4c987f95a..baae0be6d 100644
--- a/artifacts/offlineDemo/kubeanClusterOps.yml
+++ b/artifacts/offlineDemo/kubeanClusterOps.yml
@@ -6,7 +6,7 @@ metadata:
     clusterName: cluster1-offline-demo
 spec:
   cluster: cluster1-offline-demo
-  image: ghcr.io/kubean-io/spray-job:latest # or change image to kubespray origin image version
+  image: ghcr.io/kubean-io/spray-job # or add specific tag, such as v0.4.0
   backoffLimit: 0
   actionType: playbook
   action: cluster.yml
diff --git a/cmd/kubean-operator/app/app.go b/cmd/kubean-operator/app/app.go
index 5b945ea1b..fe8ac8383 100644
--- a/cmd/kubean-operator/app/app.go
+++ b/cmd/kubean-operator/app/app.go
@@ -20,8 +20,8 @@ import (
 	"k8s.io/client-go/tools/clientcmd"
 	"k8s.io/klog/v2"
 	kubeanClusterClientSet "kubean.io/api/generated/cluster/clientset/versioned"
-	kubeanClusterOpsClientSet "kubean.io/api/generated/clusteroperation/clientset/versioned"
-	kubeanofflineversionClientSet "kubean.io/api/generated/localartifactset/clientset/versioned"
+	kubeanClusterOperationClientSet "kubean.io/api/generated/clusteroperation/clientset/versioned"
+	kubeanLocalArtifactSetClientSet "kubean.io/api/generated/localartifactset/clientset/versioned"
 	kubeaninfomanifestClientSet "kubean.io/api/generated/manifest/clientset/versioned"
 	controllerruntime "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/healthz"
@@ -111,7 +111,7 @@ func setupManager(mgr controllerruntime.Manager, opt *Options, stopChan <-chan s
 	if err != nil {
 		return err
 	}
-	clusterClientOpsSet, err := kubeanClusterOpsClientSet.NewForConfig(resetConfig)
+	clusterClientOperationSet, err := kubeanClusterOperationClientSet.NewForConfig(resetConfig)
 	if err != nil {
 		return err
 	}
@@ -119,7 +119,7 @@ func setupManager(mgr controllerruntime.Manager, opt *Options, stopChan <-chan s
 	if err != nil {
 		return err
 	}
-	offlineversionClientSet, err := kubeanofflineversionClientSet.NewForConfig(resetConfig)
+	localArtifactSetClientSet, err := kubeanLocalArtifactSetClientSet.NewForConfig(resetConfig)
 	if err != nil {
 		return err
 	}
@@ -127,7 +127,7 @@ func setupManager(mgr controllerruntime.Manager, opt *Options, stopChan <-chan s
 		Client:              mgr.GetClient(),
 		ClientSet:           ClientSet,
 		KubeanClusterSet:    clusterClientSet,
-		KubeanClusterOpsSet: clusterClientOpsSet,
+		KubeanClusterOpsSet: clusterClientOperationSet,
 	}
 	// the message type
 	if err := clusterController.SetupWithManager(mgr); err != nil {
@@ -135,10 +135,11 @@ func setupManager(mgr controllerruntime.Manager, opt *Options, stopChan <-chan s
 		return err
 	}
 	clusterOpsController := &clusterops.Controller{
-		Client:              mgr.GetClient(),
-		ClientSet:           ClientSet,
-		KubeanClusterSet:    clusterClientSet,
-		KubeanClusterOpsSet: clusterClientOpsSet,
+		Client:                mgr.GetClient(),
+		ClientSet:             ClientSet,
+		KubeanClusterSet:      clusterClientSet,
+		KubeanClusterOpsSet:   clusterClientOperationSet,
+		InfoManifestClientSet: infomanifestClientSet,
 	}
 	if err := clusterOpsController.SetupWithManager(mgr); err != nil {
 		klog.Errorf("ControllerManager ClusterOps but %s", err)
@@ -146,10 +147,10 @@ func setupManager(mgr controllerruntime.Manager, opt *Options, stopChan <-chan s
 	}
 
 	offlineVersionController := &offlineversion.Controller{
-		Client:                  mgr.GetClient(),
-		ClientSet:               ClientSet,
-		InfoManifestClientSet:   infomanifestClientSet,
-		OfflineversionClientSet: offlineversionClientSet,
+		Client:                    mgr.GetClient(),
+		ClientSet:                 ClientSet,
+		InfoManifestClientSet:     infomanifestClientSet,
+		LocalArtifactSetClientSet: localArtifactSetClientSet,
 	}
 	if err := offlineVersionController.SetupWithManager(mgr); err != nil {
 		klog.Errorf("ControllerManager OfflineVersion but %s", err)
@@ -157,10 +158,10 @@ func setupManager(mgr controllerruntime.Manager, opt *Options, stopChan <-chan s
 	}
 
 	infomanifestController := &infomanifest.Controller{
-		Client:                  mgr.GetClient(),
-		InfoManifestClientSet:   infomanifestClientSet,
-		ClientSet:               ClientSet,
-		OfflineversionClientSet: offlineversionClientSet,
+		Client:                    mgr.GetClient(),
+		InfoManifestClientSet:     infomanifestClientSet,
+		ClientSet:                 ClientSet,
+		LocalArtifactSetClientSet: localArtifactSetClientSet,
 	}
 	if err := infomanifestController.SetupWithManager(mgr); err != nil {
 		klog.Errorf("ControllerManager Infomanifest but %s", err)
diff --git a/pkg/controllers/clusterops/controller.go b/pkg/controllers/clusterops/controller.go
index 9d7479a3b..741994cbe 100644
--- a/pkg/controllers/clusterops/controller.go
+++ b/pkg/controllers/clusterops/controller.go
@@ -22,9 +22,11 @@ import (
 	"kubean.io/api/apis"
 	clusterv1alpha1 "kubean.io/api/apis/cluster/v1alpha1"
 	clusteroperationv1alpha1 "kubean.io/api/apis/clusteroperation/v1alpha1"
+	manifestv1alpha1 "kubean.io/api/apis/manifest/v1alpha1"
 	"kubean.io/api/constants"
 	clusterClientSet "kubean.io/api/generated/cluster/clientset/versioned"
 	clusterOperationClientSet "kubean.io/api/generated/clusteroperation/clientset/versioned"
+	manifestClientSet "kubean.io/api/generated/manifest/clientset/versioned"
 	controllerruntime "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
@@ -38,9 +40,10 @@ const (
 
 type Controller struct {
 	client.Client
-	ClientSet           kubernetes.Interface
-	KubeanClusterSet    clusterClientSet.Interface
-	KubeanClusterOpsSet clusterOperationClientSet.Interface
+	ClientSet             kubernetes.Interface
+	KubeanClusterSet      clusterClientSet.Interface
+	KubeanClusterOpsSet   clusterOperationClientSet.Interface
+	InfoManifestClientSet manifestClientSet.Interface
 }
 
 func (c *Controller) Start(ctx context.Context) error {
@@ -107,6 +110,14 @@ func (c *Controller) UpdateStatusHasModified(clusterOps *clusteroperationv1alpha
 	return false, nil
 }
 
+func (c *Controller) FetchGlobalInfoManifest() (*manifestv1alpha1.Manifest, error) {
+	global, err := c.InfoManifestClientSet.KubeanV1alpha1().Manifests().Get(context.Background(), constants.InfoManifestGlobal, metav1.GetOptions{})
+	if err != nil {
+		return nil, err
+	}
+	return global, nil
+}
+
 func (c *Controller) UpdateStatusLoop(clusterOps *clusteroperationv1alpha1.ClusterOperation, fetchJobStatus func(*clusteroperationv1alpha1.ClusterOperation) (clusteroperationv1alpha1.OpsStatus, error)) (bool, error) {
 	if clusterOps.Status.Status == clusteroperationv1alpha1.RunningStatus || len(clusterOps.Status.Status) == 0 {
 		// need fetch jobStatus again when the last status of job is running
@@ -315,6 +326,25 @@ func (c *Controller) Reconcile(ctx context.Context, req controllerruntime.Reques
 	return controllerruntime.Result{Requeue: false}, nil
 }
 
+func (c *Controller) ProcessKubeanOperationImage(oldImage, globalManifestImageTag string) string {
+	if strings.Contains(oldImage, ":") { // kubespray-job:v1
+		return oldImage
+	}
+	if globalManifestImageTag == "" {
+		return fmt.Sprintf("%s:latest", oldImage)
+	}
+	return fmt.Sprintf("%s:%s", oldImage, globalManifestImageTag)
+}
+
+func (c *Controller) FetchGlobalManifestImageTag() string {
+	globalManifest, err := c.FetchGlobalInfoManifest()
+	if err != nil {
+		klog.Warningf("%s", err.Error())
+		return ""
+	}
+	return globalManifest.Spec.KubeanVersion
+}
+
 func (c *Controller) NewKubesprayJob(clusterOps *clusteroperationv1alpha1.ClusterOperation) *batchv1.Job {
 	BackoffLimit := int32(clusterOps.Spec.BackoffLimit)
 	DefaultMode := int32(0o700)
@@ -339,7 +369,7 @@ func (c *Controller) NewKubesprayJob(clusterOps *clusteroperationv1alpha1.Cluste
 					Containers: []corev1.Container{
 						{
 							Name:    "kubespray", // do not change this name
-							Image:   clusterOps.Spec.Image,
+							Image:   c.ProcessKubeanOperationImage(clusterOps.Spec.Image, c.FetchGlobalManifestImageTag()),
 							Command: []string{"/bin/entrypoint.sh"},
 							Env: []corev1.EnvVar{
 								{
diff --git a/pkg/controllers/infomanifest/controller.go b/pkg/controllers/infomanifest/controller.go
index eff9ef08c..2061f2c03 100644
--- a/pkg/controllers/infomanifest/controller.go
+++ b/pkg/controllers/infomanifest/controller.go
@@ -32,9 +32,9 @@ const LocalServiceConfigMap = "kubean-localservice"
 
 type Controller struct {
 	client.Client
-	InfoManifestClientSet   manifestClientSet.Interface
-	ClientSet               kubernetes.Interface
-	OfflineversionClientSet localartifactsetClientSet.Interface
+	InfoManifestClientSet     manifestClientSet.Interface
+	ClientSet                 kubernetes.Interface
+	LocalArtifactSetClientSet localartifactsetClientSet.Interface
 }
 
 func (c *Controller) Start(ctx context.Context) error {
@@ -85,7 +85,7 @@ func (c *Controller) FetchGlobalInfoManifest() (*manifestv1alpha1.Manifest, erro
 	if err != nil {
 		return nil, err
 	}
-	return global, err
+	return global, nil
 }
 
 func (c *Controller) EnsureGlobalInfoManifestBeingLatest(latestInfoManifest *manifestv1alpha1.Manifest) (*manifestv1alpha1.Manifest, error) {
@@ -196,7 +196,7 @@ func (c *Controller) UpdateLocalAvailableImage() {
 
 // IsOnlineENV indicates what the running env is onLine or air-gap.
 func (c *Controller) IsOnlineENV() bool {
-	result, err := c.OfflineversionClientSet.KubeanV1alpha1().LocalArtifactSets().List(context.Background(), metav1.ListOptions{})
+	result, err := c.LocalArtifactSetClientSet.KubeanV1alpha1().LocalArtifactSets().List(context.Background(), metav1.ListOptions{})
 	if err != nil {
 		klog.Errorf("%s ", err.Error())
 		return true
diff --git a/pkg/controllers/offlineversion/controller.go b/pkg/controllers/offlineversion/controller.go
index 651c11fa4..63bbd04db 100644
--- a/pkg/controllers/offlineversion/controller.go
+++ b/pkg/controllers/offlineversion/controller.go
@@ -23,9 +23,9 @@ const Loop = time.Second * 15
 
 type Controller struct {
 	client.Client
-	ClientSet               kubernetes.Interface
-	InfoManifestClientSet   manifestClientSet.Interface
-	OfflineversionClientSet localartifactsetClientSet.Interface
+	ClientSet                 kubernetes.Interface
+	InfoManifestClientSet     manifestClientSet.Interface
+	LocalArtifactSetClientSet localartifactsetClientSet.Interface
 }
 
 func (c *Controller) Start(ctx context.Context) error {


Test patch:
diff --git a/pkg/controllers/clusterops/controller_test.go b/pkg/controllers/clusterops/controller_test.go
index c91a7d9be..975c87873 100644
--- a/pkg/controllers/clusterops/controller_test.go
+++ b/pkg/controllers/clusterops/controller_test.go
@@ -13,9 +13,11 @@ import (
 	clientsetfake "k8s.io/client-go/kubernetes/fake"
 	clusterv1alpha1 "kubean.io/api/apis/cluster/v1alpha1"
 	clusteroperationv1alpha1 "kubean.io/api/apis/clusteroperation/v1alpha1"
+	manifestv1alpha1 "kubean.io/api/apis/manifest/v1alpha1"
 	"kubean.io/api/constants"
 	clusterv1alpha1fake "kubean.io/api/generated/cluster/clientset/versioned/fake"
 	clusteroperationv1alpha1fake "kubean.io/api/generated/clusteroperation/clientset/versioned/fake"
+	manifestv1alpha1fake "kubean.io/api/generated/manifest/clientset/versioned/fake"
 
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -406,7 +408,11 @@ func TestController_SetOwnerReferences(t *testing.T) {
 }
 
 func TestNewKubesprayJob(t *testing.T) {
-	controller := Controller{}
+	controller := Controller{
+		Client:                newFakeClient(),
+		ClientSet:             clientsetfake.NewSimpleClientset(),
+		InfoManifestClientSet: manifestv1alpha1fake.NewSimpleClientset(),
+	}
 	clusterOps := &clusteroperationv1alpha1.ClusterOperation{}
 	clusterOps.Spec.BackoffLimit = 10
 	clusterOps.Name = "myops"
@@ -676,10 +682,11 @@ func TestIsValidImageName(t *testing.T) {
 
 func TestCreateKubeSprayJob(t *testing.T) {
 	controller := Controller{
-		Client:              newFakeClient(),
-		ClientSet:           clientsetfake.NewSimpleClientset(),
-		KubeanClusterSet:    clusterv1alpha1fake.NewSimpleClientset(),
-		KubeanClusterOpsSet: clusteroperationv1alpha1fake.NewSimpleClientset(),
+		Client:                newFakeClient(),
+		ClientSet:             clientsetfake.NewSimpleClientset(),
+		KubeanClusterSet:      clusterv1alpha1fake.NewSimpleClientset(),
+		KubeanClusterOpsSet:   clusteroperationv1alpha1fake.NewSimpleClientset(),
+		InfoManifestClientSet: manifestv1alpha1fake.NewSimpleClientset(),
 	}
 	clusterOps := &clusteroperationv1alpha1.ClusterOperation{}
 	clusterOps.Spec.BackoffLimit = 10
@@ -1472,3 +1479,99 @@ func Test_BackUpDataRef(t *testing.T) {
 		})
 	}
 }
+
+func Test_ProcessKubeanOperationImage(t *testing.T) {
+	controller := Controller{
+		Client:                newFakeClient(),
+		ClientSet:             clientsetfake.NewSimpleClientset(),
+		KubeanClusterSet:      clusterv1alpha1fake.NewSimpleClientset(),
+		KubeanClusterOpsSet:   clusteroperationv1alpha1fake.NewSimpleClientset(),
+		InfoManifestClientSet: manifestv1alpha1fake.NewSimpleClientset(),
+	}
+	tests := []struct {
+		name string
+		args func() string
+		want string
+	}{
+		{
+			name: "nothing to update",
+			args: func() string {
+				return controller.ProcessKubeanOperationImage("a:c", "my_tag")
+			},
+			want: "a:c",
+		},
+		{
+			name: "update image tag",
+			args: func() string {
+				return controller.ProcessKubeanOperationImage("abc", "my_tag")
+			},
+			want: "abc:my_tag",
+		},
+		{
+			name: "update image tag with latest",
+			args: func() string {
+				return controller.ProcessKubeanOperationImage("abc", "")
+			},
+			want: "abc:latest",
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			if test.args() != test.want {
+				t.Fatal()
+			}
+		})
+	}
+}
+
+func Test_FetchGlobalManifestImageTag(t *testing.T) {
+	controller := Controller{
+		Client:                newFakeClient(),
+		ClientSet:             clientsetfake.NewSimpleClientset(),
+		KubeanClusterSet:      clusterv1alpha1fake.NewSimpleClientset(),
+		KubeanClusterOpsSet:   clusteroperationv1alpha1fake.NewSimpleClientset(),
+		InfoManifestClientSet: manifestv1alpha1fake.NewSimpleClientset(),
+	}
+
+	tests := []struct {
+		name string
+		args func() string
+		want string
+	}{
+		{
+			name: "none globalManifest",
+			args: func() string {
+				return controller.FetchGlobalManifestImageTag()
+			},
+			want: "",
+		},
+		{
+			name: "none globalManifest",
+			args: func() string {
+				global := &manifestv1alpha1.Manifest{
+					TypeMeta: metav1.TypeMeta{
+						Kind:       "Manifest",
+						APIVersion: "kubean.io/v1alpha1",
+					},
+					ObjectMeta: metav1.ObjectMeta{
+						Name:   constants.InfoManifestGlobal,
+						Labels: map[string]string{"origin": "v2"},
+					},
+					Spec: manifestv1alpha1.Spec{
+						KubeanVersion: "123",
+					},
+				}
+				controller.InfoManifestClientSet.KubeanV1alpha1().Manifests().Create(context.Background(), global, metav1.CreateOptions{})
+				return controller.FetchGlobalManifestImageTag()
+			},
+			want: "123",
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			if test.args() != test.want {
+				t.Fatal()
+			}
+		})
+	}
+}
diff --git a/pkg/controllers/infomanifest/controller_test.go b/pkg/controllers/infomanifest/controller_test.go
index 44cdfe19f..9cd043359 100644
--- a/pkg/controllers/infomanifest/controller_test.go
+++ b/pkg/controllers/infomanifest/controller_test.go
@@ -285,10 +285,10 @@ func Test_EnsureGlobalInfoManifestBeingLatest(t *testing.T) {
 
 func Test_UpdateGlobalLocalService1(t *testing.T) {
 	controller := &Controller{
-		Client:                  newFakeClient(),
-		ClientSet:               clientsetfake.NewSimpleClientset(),
-		InfoManifestClientSet:   manifestv1alpha1fake.NewSimpleClientset(),
-		OfflineversionClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
+		Client:                    newFakeClient(),
+		ClientSet:                 clientsetfake.NewSimpleClientset(),
+		InfoManifestClientSet:     manifestv1alpha1fake.NewSimpleClientset(),
+		LocalArtifactSetClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
 	}
 	tests := []struct {
 		name string
@@ -555,15 +555,15 @@ func addLocalArtifactSet(controller *Controller) {
 			},
 		},
 	}
-	controller.OfflineversionClientSet.KubeanV1alpha1().LocalArtifactSets().Create(context.Background(), set, metav1.CreateOptions{})
+	controller.LocalArtifactSetClientSet.KubeanV1alpha1().LocalArtifactSets().Create(context.Background(), set, metav1.CreateOptions{})
 }
 
 func TestIsOnlineENV(t *testing.T) {
 	controller := &Controller{
-		Client:                  newFakeClient(),
-		ClientSet:               clientsetfake.NewSimpleClientset(),
-		InfoManifestClientSet:   manifestv1alpha1fake.NewSimpleClientset(),
-		OfflineversionClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
+		Client:                    newFakeClient(),
+		ClientSet:                 clientsetfake.NewSimpleClientset(),
+		InfoManifestClientSet:     manifestv1alpha1fake.NewSimpleClientset(),
+		LocalArtifactSetClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
 	}
 	tests := []struct {
 		name string
diff --git a/pkg/controllers/offlineversion/controller_test.go b/pkg/controllers/offlineversion/controller_test.go
index 051224dca..31bb711a9 100644
--- a/pkg/controllers/offlineversion/controller_test.go
+++ b/pkg/controllers/offlineversion/controller_test.go
@@ -33,10 +33,10 @@ func newFakeClient() client.Client {
 
 func TestMergeOfflineVersion(t *testing.T) {
 	controller := &Controller{
-		Client:                  newFakeClient(),
-		ClientSet:               clientsetfake.NewSimpleClientset(),
-		OfflineversionClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
-		InfoManifestClientSet:   manifestv1alpha1fake.NewSimpleClientset(),
+		Client:                    newFakeClient(),
+		ClientSet:                 clientsetfake.NewSimpleClientset(),
+		LocalArtifactSetClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
+		InfoManifestClientSet:     manifestv1alpha1fake.NewSimpleClientset(),
 	}
 	tests := []struct {
 		name string
@@ -260,10 +260,10 @@ func TestReconcile(t *testing.T) {
 			name: "merge successfully",
 			args: func() bool {
 				controller := &Controller{
-					Client:                  newFakeClient(),
-					ClientSet:               clientsetfake.NewSimpleClientset(),
-					OfflineversionClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
-					InfoManifestClientSet:   manifestv1alpha1fake.NewSimpleClientset(),
+					Client:                    newFakeClient(),
+					ClientSet:                 clientsetfake.NewSimpleClientset(),
+					LocalArtifactSetClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
+					InfoManifestClientSet:     manifestv1alpha1fake.NewSimpleClientset(),
 				}
 				offlineVersionData := localartifactsetv1alpha1.LocalArtifactSet{
 					TypeMeta: metav1.TypeMeta{
@@ -294,7 +294,7 @@ func TestReconcile(t *testing.T) {
 				}
 
 				controller.Create(context.Background(), &offlineVersionData)
-				controller.OfflineversionClientSet.KubeanV1alpha1().LocalArtifactSets().Create(context.Background(), &offlineVersionData, metav1.CreateOptions{})
+				controller.LocalArtifactSetClientSet.KubeanV1alpha1().LocalArtifactSets().Create(context.Background(), &offlineVersionData, metav1.CreateOptions{})
 				controller.InfoManifestClientSet.KubeanV1alpha1().Manifests().Create(context.Background(), &globalComponentsVersion, metav1.CreateOptions{})
 
 				result, err := controller.Reconcile(context.Background(), controllerruntime.Request{NamespacedName: types.NamespacedName{Name: offlineVersionData.Name}})
@@ -307,10 +307,10 @@ func TestReconcile(t *testing.T) {
 			name: "ComponentsversionGlobal not exist",
 			args: func() bool {
 				controller := &Controller{
-					Client:                  newFakeClient(),
-					ClientSet:               clientsetfake.NewSimpleClientset(),
-					OfflineversionClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
-					InfoManifestClientSet:   manifestv1alpha1fake.NewSimpleClientset(),
+					Client:                    newFakeClient(),
+					ClientSet:                 clientsetfake.NewSimpleClientset(),
+					LocalArtifactSetClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
+					InfoManifestClientSet:     manifestv1alpha1fake.NewSimpleClientset(),
 				}
 				offlineVersionData := localartifactsetv1alpha1.LocalArtifactSet{
 					TypeMeta: metav1.TypeMeta{
@@ -331,7 +331,7 @@ func TestReconcile(t *testing.T) {
 				}
 
 				controller.Create(context.Background(), &offlineVersionData)
-				controller.OfflineversionClientSet.KubeanV1alpha1().LocalArtifactSets().Create(context.Background(), &offlineVersionData, metav1.CreateOptions{})
+				controller.LocalArtifactSetClientSet.KubeanV1alpha1().LocalArtifactSets().Create(context.Background(), &offlineVersionData, metav1.CreateOptions{})
 
 				result, _ := controller.Reconcile(context.Background(), controllerruntime.Request{NamespacedName: types.NamespacedName{Name: offlineVersionData.Name}})
 				return result.RequeueAfter == Loop
@@ -342,10 +342,10 @@ func TestReconcile(t *testing.T) {
 			name: "offlineVersion not found",
 			args: func() bool {
 				controller := &Controller{
-					Client:                  newFakeClient(),
-					ClientSet:               clientsetfake.NewSimpleClientset(),
-					OfflineversionClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
-					InfoManifestClientSet:   manifestv1alpha1fake.NewSimpleClientset(),
+					Client:                    newFakeClient(),
+					ClientSet:                 clientsetfake.NewSimpleClientset(),
+					LocalArtifactSetClientSet: localartifactsetv1alpha1fake.NewSimpleClientset(),
+					InfoManifestClientSet:     manifestv1alpha1fake.NewSimpleClientset(),
 				}
 				result, _ := controller.Reconcile(context.Background(), controllerruntime.Request{NamespacedName: types.NamespacedName{Name: "offlineversion-1"}})
 				return result.Requeue == false && result.RequeueAfter == 0
diff --git a/test/kubeanOps_functions_nightlye2e/kubeanOps_test.go b/test/kubeanOps_functions_nightlye2e/kubeanOps_test.go
index 12afff96a..a523e9938 100644
--- a/test/kubeanOps_functions_nightlye2e/kubeanOps_test.go
+++ b/test/kubeanOps_functions_nightlye2e/kubeanOps_test.go
@@ -13,7 +13,7 @@ import (
 	"github.com/onsi/gomega"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/tools/clientcmd"
-	kubeanClusterOpsClientSet "kubean.io/api/generated/clusteroperation/clientset/versioned"
+	kubeanClusterOperationClientSet "kubean.io/api/generated/clusteroperation/clientset/versioned"
 )
 
 var _ = ginkgo.Describe("kubean ops e2e test", func() {
@@ -23,7 +23,7 @@ var _ = ginkgo.Describe("kubean ops e2e test", func() {
 	var basepath = filepath.Dir(currentFile)
 	clusterInstallYamlsPath := "e2e-install-cluster"
 	opsFile := filepath.Join(basepath, "/e2e-install-cluster/kubeanClusterOps.yml")
-	clusterClientOpsSet, _ := kubeanClusterOpsClientSet.NewForConfig(config)
+	clusterClientOperationSet, _ := kubeanClusterOperationClientSet.NewForConfig(config)
 
 	ginkgo.Context("when apply two jobs for one cluster hosts", func() {
 		// step1 create the first ops
diff --git a/test/tools/kubean_create_CR.go b/test/tools/kubean_create_CR.go
index f008db390..44960d279 100644
--- a/test/tools/kubean_create_CR.go
+++ b/test/tools/kubean_create_CR.go
@@ -14,7 +14,7 @@ import (
 	kubeancluster "kubean.io/api/apis/cluster/v1alpha1"
 	kubeanclusterops "kubean.io/api/apis/clusteroperation/v1alpha1"
 	kubeanClusterClientSet "kubean.io/api/generated/cluster/clientset/versioned"
-	kubeanClusterOpsClientSet "kubean.io/api/generated/clusteroperation/clientset/versioned"
+	kubeanClusterOperationClientSet "kubean.io/api/generated/clusteroperation/clientset/versioned"
 )
 
 var hostsYaml = `
@@ -184,7 +184,7 @@ func CreatCR() {
 	}
 
 	// 4 create ClusterOperation
-	clusterClientOpsSet, err := kubeanClusterOpsClientSet.NewForConfig(config)
+	clusterClientOperationSet, err := kubeanClusterOperationClientSet.NewForConfig(config)
 	gomega.ExpectWithOffset(2, err).NotTo(gomega.HaveOccurred(), "failed new client set")
 	preHookAction :=
 		`ansible -i /conf/hosts.yml all -m ping;