Add serviceaccount for DS components to read configmaps (#4)

diff --git a/api/v1alpha1/ds_public.go b/api/v1alpha1/ds_public.go
index 78b2b63..670d884 100644
--- a/api/v1alpha1/ds_public.go
+++ b/api/v1alpha1/ds_public.go
@@ -66,6 +66,9 @@
 	DsApiPort              = 12345
 	DsAlertPort            = 50052
 	DsWorkerHpa            = "ds-worker-hpa"
+	DsServiceAccount       = "ds-service-account"
+	DsRole                 = "ds-role"
+	DsRoleBinding          = "ds-role-binding"
 )
 
 // DsCondition represents one current condition of a ds cluster.
diff --git a/api/v1alpha1/dsalert_types.go b/api/v1alpha1/dsalert_types.go
index a57fa1a..acd27cc 100644
--- a/api/v1alpha1/dsalert_types.go
+++ b/api/v1alpha1/dsalert_types.go
@@ -43,6 +43,8 @@
 	// +kubebuilder:default=apache/dolphinscheduler-master
 	Repository string `json:"repository,omitempty"`
 
+	ServiceAccount string `json:"service_account,omitempty"`
+
 	// Replicas is the expected size of the ms-master.
 	// The ds-master-operator will eventually make the size of the running
 	//  equal to the expected size.
diff --git a/api/v1alpha1/dsapi_types.go b/api/v1alpha1/dsapi_types.go
index ca70603..bbcf1bc 100644
--- a/api/v1alpha1/dsapi_types.go
+++ b/api/v1alpha1/dsapi_types.go
@@ -39,6 +39,8 @@
 
 	ZookeeperConnect string `json:"zookeeper_connect,omitempty"`
 
+	ServiceAccount string `json:"service_account,omitempty"`
+
 	// Repository is the name of the repository that hosts
 	// ds container images. It should be direct clone of the repository in official
 	// By default, it is `apache/dolphinscheduler-master`.
diff --git a/api/v1alpha1/dsmaster_types.go b/api/v1alpha1/dsmaster_types.go
index f7971ca..e3dcd42 100644
--- a/api/v1alpha1/dsmaster_types.go
+++ b/api/v1alpha1/dsmaster_types.go
@@ -61,6 +61,8 @@
 
 	HpaPolicy *HpaPolicy `json:"hpa,omitempty"`
 
+	ServiceAccount string `json:"service_account,omitempty"`
+
 	// Paused is to pause the control of the operator for the ds-master .
 	// +kubebuilder:default=false
 	Paused bool `json:"paused,omitempty"`
diff --git a/api/v1alpha1/dsworker_types.go b/api/v1alpha1/dsworker_types.go
index e0dc9b0..81ef041 100644
--- a/api/v1alpha1/dsworker_types.go
+++ b/api/v1alpha1/dsworker_types.go
@@ -38,6 +38,8 @@
 	// +kubebuilder:default="3.0.0-alpha"
 	Version string `json:"version,omitempty"`
 
+	ServiceAccount string `json:"service_account,omitempty"`
+
 	// Repository is the name of the repository that hosts
 	// ds container images. It should be direct clone of the repository in official
 	// By default, it is `apache/dolphinscheduler-worker`.
diff --git a/config/crd/bases/ds.apache.dolphinscheduler.dev_dsalerts.yaml b/config/crd/bases/ds.apache.dolphinscheduler.dev_dsalerts.yaml
index 2781983..4efc259 100644
--- a/config/crd/bases/ds.apache.dolphinscheduler.dev_dsalerts.yaml
+++ b/config/crd/bases/ds.apache.dolphinscheduler.dev_dsalerts.yaml
@@ -1148,6 +1148,8 @@
                   container images. It should be direct clone of the repository in
                   official By default, it is `apache/dolphinscheduler-master`.
                 type: string
+              service_account:
+                type: string
               version:
                 default: 3.0.0-alpha
                 description: Version is the expected version of the ds cluster. The
diff --git a/config/crd/bases/ds.apache.dolphinscheduler.dev_dsapis.yaml b/config/crd/bases/ds.apache.dolphinscheduler.dev_dsapis.yaml
index efd8dad..d18656d 100644
--- a/config/crd/bases/ds.apache.dolphinscheduler.dev_dsapis.yaml
+++ b/config/crd/bases/ds.apache.dolphinscheduler.dev_dsapis.yaml
@@ -1152,6 +1152,8 @@
                   container images. It should be direct clone of the repository in
                   official By default, it is `apache/dolphinscheduler-master`.
                 type: string
+              service_account:
+                type: string
               version:
                 default: 3.0.0-alpha
                 description: Version is the expected version of the ds cluster. The
diff --git a/config/crd/bases/ds.apache.dolphinscheduler.dev_dsmasters.yaml b/config/crd/bases/ds.apache.dolphinscheduler.dev_dsmasters.yaml
index 973643f..cf5800b 100644
--- a/config/crd/bases/ds.apache.dolphinscheduler.dev_dsmasters.yaml
+++ b/config/crd/bases/ds.apache.dolphinscheduler.dev_dsmasters.yaml
@@ -1349,6 +1349,8 @@
                   container images. It should be direct clone of the repository in
                   official By default, it is `apache/dolphinscheduler-master`.
                 type: string
+              service_account:
+                type: string
               version:
                 default: 3.0.0-alpha
                 description: Version is the expected version of the ds cluster. The
diff --git a/config/crd/bases/ds.apache.dolphinscheduler.dev_dsworkers.yaml b/config/crd/bases/ds.apache.dolphinscheduler.dev_dsworkers.yaml
index e816f41..00967f7 100644
--- a/config/crd/bases/ds.apache.dolphinscheduler.dev_dsworkers.yaml
+++ b/config/crd/bases/ds.apache.dolphinscheduler.dev_dsworkers.yaml
@@ -1351,6 +1351,8 @@
                   container images. It should be direct clone of the repository in
                   official By default, it is `apache/dolphinscheduler-worker`.
                 type: string
+              service_account:
+                type: string
               version:
                 default: 3.0.0-alpha
                 description: Version is the expected version of the ds cluster. The
diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml
index 13f8ea0..d69e79a 100644
--- a/config/manager/kustomization.yaml
+++ b/config/manager/kustomization.yaml
@@ -30,4 +30,4 @@
 images:
 - name: controller
   newName: apache/dolphinscheduler-operator
-  newTag: 0.1.0
+  newTag: latest
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index 074be47..98d6598 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -47,6 +47,15 @@
 - apiGroups:
   - ""
   resources:
+  - serviceaccounts
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+- apiGroups:
+  - ""
+  resources:
   - services
   verbs:
   - create
@@ -184,3 +193,21 @@
   - get
   - patch
   - update
+- apiGroups:
+  - rbac.authorization.k8s.io
+  resources:
+  - role
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+- apiGroups:
+  - rbac.authorization.k8s.io
+  resources:
+  - rolebinding
+  verbs:
+  - create
+  - delete
+  - get
+  - list
diff --git a/config/samples/ds_v1alpha1_dsalert.yaml b/config/samples/ds_v1alpha1_dsalert.yaml
index 801178d..481f744 100644
--- a/config/samples/ds_v1alpha1_dsalert.yaml
+++ b/config/samples/ds_v1alpha1_dsalert.yaml
@@ -24,8 +24,9 @@
     app: ds-alert
 spec:
   replicas: 1
-  version: 3.0.0-alpha
-  repository: apache/dolphinscheduler-alert-server
+  version: latest
+  repository: ghcr.io/apache/dolphinscheduler/dolphinscheduler-alert-server
+  service_account: "ds-service-account"
   datasource:
     drive_name: "org.postgresql.Driver"
     url: "jdbc:postgresql://postgres-service:5432/dolphinscheduler"
diff --git a/config/samples/ds_v1alpha1_dsapi.yaml b/config/samples/ds_v1alpha1_dsapi.yaml
index 4d18eaa..7c646bf 100644
--- a/config/samples/ds_v1alpha1_dsapi.yaml
+++ b/config/samples/ds_v1alpha1_dsapi.yaml
@@ -24,10 +24,11 @@
     app: ds-api
 spec:
   replicas: 1
-  version: 3.0.0-alpha
+  version: latest
   zookeeper_connect: "zookeeper-service:2181"
-  repository: apache/dolphinscheduler-api
+  repository: ghcr.io/apache/dolphinscheduler/dolphinscheduler-api
   node_port: 30002
+  service_account: "ds-service-account"
   datasource:
     drive_name: "org.postgresql.Driver"
     url: "jdbc:postgresql://postgres-service:5432/dolphinscheduler"
diff --git a/config/samples/ds_v1alpha1_dsmaster.yaml b/config/samples/ds_v1alpha1_dsmaster.yaml
index 55aea38..59501ac 100644
--- a/config/samples/ds_v1alpha1_dsmaster.yaml
+++ b/config/samples/ds_v1alpha1_dsmaster.yaml
@@ -25,8 +25,9 @@
 spec:
   replicas: 1
   zookeeper_connect: "zookeeper-service:2181"
-  version: 3.0.0-alpha
-  repository: apache/dolphinscheduler-master
+  version: latest
+  repository: ghcr.io/apache/dolphinscheduler/dolphinscheduler-master
+  service_account: "ds-service-account"
   datasource:
     drive_name: "org.postgresql.Driver"
     url: "jdbc:postgresql://postgres-service:5432/dolphinscheduler"
diff --git a/config/samples/ds_v1alpha1_dsworker.yaml b/config/samples/ds_v1alpha1_dsworker.yaml
index c0eb982..48a3299 100644
--- a/config/samples/ds_v1alpha1_dsworker.yaml
+++ b/config/samples/ds_v1alpha1_dsworker.yaml
@@ -25,18 +25,12 @@
 spec:
   replicas: 1
   zookeeper_connect: "zookeeper-service:2181"
-  version: 3.0.0-alpha
-  repository: apache/dolphinscheduler-worker
+  version: latest
+  repository: ghcr.io/apache/dolphinscheduler/dolphinscheduler-worker
+  service_account: "ds-service-account"
   datasource:
     drive_name: "org.postgresql.Driver"
     url: "jdbc:postgresql://postgres-service:5432/dolphinscheduler"
     username: "postgresadmin"
     password: "admin12345"
-  pod:
-    resources:
-      limits:
-        cpu: "1000m"
-        memory: "2Gi"
-      requests:
-        cpu: "500m"
-        memory: "1Gi"
+
diff --git a/controllers/alert_reconcile.go b/controllers/alert_reconcile.go
index b5e1ee2..878e9ce 100644
--- a/controllers/alert_reconcile.go
+++ b/controllers/alert_reconcile.go
@@ -69,6 +69,7 @@
 					},
 				},
 				Spec: corev1.PodSpec{
+					ServiceAccountName: cluster.Spec.ServiceAccount,
 					Containers: []corev1.Container{{
 						Name:            dsv1alpha1.DsAlert,
 						Image:           ImageName(cluster.Spec.Repository, cluster.Spec.Version),
diff --git a/controllers/api_reconcile.go b/controllers/api_reconcile.go
index bd5cf09..01ad35c 100644
--- a/controllers/api_reconcile.go
+++ b/controllers/api_reconcile.go
@@ -75,6 +75,7 @@
 					},
 				},
 				Spec: corev1.PodSpec{
+					ServiceAccountName: cluster.Spec.ServiceAccount,
 					Containers: []corev1.Container{{
 						Name:            dsv1alpha1.DsApi,
 						Image:           ImageName(cluster.Spec.Repository, cluster.Spec.Version),
diff --git a/controllers/dsmaster_controller.go b/controllers/dsmaster_controller.go
index fadbdeb..c239ac7 100644
--- a/controllers/dsmaster_controller.go
+++ b/controllers/dsmaster_controller.go
@@ -20,6 +20,7 @@
 import (
 	"context"
 	"k8s.io/api/autoscaling/v2beta2"
+	v1 "k8s.io/api/rbac/v1"
 	"time"
 
 	dsv1alpha1 "dolphinscheduler-operator/api/v1alpha1"
@@ -57,6 +58,9 @@
 //+kubebuilder:rbac:groups="",resources=persistentvolumeclaims,verbs=get;create;delete;list;watch
 //+kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch;create;update;patch;delete
 //+kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;create;delete
+//+kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=role,verbs=get;list;create;delete
+//+kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=rolebinding,verbs=get;list;create;delete
 
 // Reconcile is part of the main kubernetes reconciliation loop which aims to
 // move the current state of the cluster closer to the desired state.
@@ -77,6 +81,21 @@
 	}
 	desired := cluster.DeepCopy()
 
+	sa := &corev1.ServiceAccount{}
+	saReq := ctrl.Request{
+		NamespacedName: types.NamespacedName{
+			Namespace: req.Namespace,
+			Name:      dsv1alpha1.DsServiceAccount,
+		},
+	}
+	err := r.Get(ctx, saReq.NamespacedName, sa)
+	if apierrors.IsNotFound(err) {
+		err := r.createServiceAccountIfNotExists(ctx, cluster)
+		if err != nil {
+			return ctrl.Result{}, err
+		}
+	}
+
 	// Handler finalizer
 	// examine DeletionTimestamp to determine if object is under deletion
 	if cluster.ObjectMeta.DeletionTimestamp.IsZero() {
@@ -199,6 +218,9 @@
 		Owns(&corev1.Pod{}).
 		Owns(&corev1.Service{}).
 		Owns(&v2beta2.HorizontalPodAutoscaler{}).
+		Owns(&corev1.ServiceAccount{}).
+		Owns(&v1.Role{}).
+		Owns(&v1.RoleBinding{}).
 		// or use WithEventFilter()
 		WithEventFilter(filter).
 		Complete(r)
@@ -435,3 +457,60 @@
 	}
 	return nil
 }
+
+// 创建 ServiceAccount
+func (r *DSMasterReconciler) createServiceAccountIfNotExists(ctx context.Context, cluster *dsv1alpha1.DSMaster) (err error) {
+
+	masterLogger.Info("start create service account.")
+
+	sa := &corev1.ServiceAccount{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      dsv1alpha1.DsServiceAccount,
+			Namespace: cluster.Namespace,
+		},
+	}
+
+	err = r.Create(ctx, sa)
+	if err != nil {
+		masterLogger.Error(err, "create service account error")
+		return err
+	}
+	// binding the sa
+	err = controllerutil.SetControllerReference(cluster, sa, r.Scheme)
+	if err != nil {
+		masterLogger.Error(err, "sa SetControllerReference error")
+		return err
+	}
+
+	ro := &v1.Role{}
+	namespacedName := types.NamespacedName{Namespace: cluster.Namespace, Name: dsv1alpha1.DsRole}
+	if err := r.Client.Get(ctx, namespacedName, ro); err != nil {
+		if apierrors.IsNotFound(err) && !apierrors.IsAlreadyExists(err) {
+			// Remote may already exist, so we will return err, for the next time, this code will not execute
+			ro := r.createRole(cluster)
+			if err := controllerutil.SetControllerReference(cluster, ro, r.Scheme); err != nil {
+				masterLogger.Info("set controller role  error")
+				return err
+			}
+			if err := r.Client.Create(ctx, ro); err != nil {
+				return err
+			}
+		}
+	}
+
+	rb := &v1.RoleBinding{}
+	rbNamespacedName := types.NamespacedName{Namespace: cluster.Namespace, Name: dsv1alpha1.DsRoleBinding}
+	if err := r.Client.Get(ctx, rbNamespacedName, rb); err != nil {
+		if apierrors.IsNotFound(err) && !apierrors.IsAlreadyExists(err) {
+			rb := r.createRoleBinding(cluster)
+			if err := controllerutil.SetControllerReference(cluster, rb, r.Scheme); err != nil {
+				masterLogger.Info("set controller  rolebinding error")
+				return err
+			}
+			if err := r.Client.Create(ctx, rb); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
diff --git a/controllers/master_reconcile.go b/controllers/master_reconcile.go
index d51099d..1cab854 100644
--- a/controllers/master_reconcile.go
+++ b/controllers/master_reconcile.go
@@ -20,6 +20,7 @@
 import (
 	"context"
 	dsv1alpha1 "dolphinscheduler-operator/api/v1alpha1"
+	v1 "k8s.io/api/rbac/v1"
 
 	"k8s.io/api/autoscaling/v2beta2"
 
@@ -67,14 +68,14 @@
 			Name:      podName,
 			Namespace: cr.Namespace,
 			Labels: map[string]string{dsv1alpha1.DsAppName: dsv1alpha1.DsMasterLabel,
-				dsv1alpha1.DsVersionLabel: ImageName(cr.Spec.Repository, cr.Spec.Version),
+				dsv1alpha1.DsVersionLabel: cr.Spec.Version,
 				dsv1alpha1.DsServiceLabel: dsv1alpha1.DsServiceLabelValue},
 		},
 		Spec: corev1.PodSpec{
-			Hostname:          podName,
-			Subdomain:         dsv1alpha1.DsServiceLabelValue,
-			SetHostnameAsFQDN: &isSetHostnameAsFQDN,
-
+			Hostname:           podName,
+			Subdomain:          dsv1alpha1.DsServiceLabelValue,
+			SetHostnameAsFQDN:  &isSetHostnameAsFQDN,
+			ServiceAccountName: cr.Spec.ServiceAccount,
 			Containers: []corev1.Container{
 				{
 					Name:            cr.Name,
@@ -195,3 +196,42 @@
 	}
 	return nil
 }
+
+func (r *DSMasterReconciler) createRole(cluster *dsv1alpha1.DSMaster) *v1.Role {
+	role := v1.Role{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      dsv1alpha1.DsRole,
+			Namespace: cluster.Namespace,
+			Labels:    map[string]string{dsv1alpha1.DsAppName: dsv1alpha1.DsRole},
+		},
+		Rules: []v1.PolicyRule{
+			{
+				Verbs:     []string{"get", "watch"},
+				Resources: []string{"configmaps"},
+				APIGroups: []string{""},
+			}},
+	}
+	return &role
+}
+
+func (r *DSMasterReconciler) createRoleBinding(cluster *dsv1alpha1.DSMaster) *v1.RoleBinding {
+	roleBinding := v1.RoleBinding{
+		TypeMeta: metav1.TypeMeta{},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      dsv1alpha1.DsRole,
+			Namespace: cluster.Namespace,
+			Labels:    map[string]string{dsv1alpha1.DsAppName: dsv1alpha1.DsRoleBinding},
+		},
+		Subjects: []v1.Subject{{
+			Kind:      "ServiceAccount",
+			Name:      dsv1alpha1.DsServiceAccount,
+			Namespace: cluster.Namespace,
+		}},
+		RoleRef: v1.RoleRef{
+			APIGroup: "rbac.authorization.k8s.io",
+			Kind:     "Role",
+			Name:     dsv1alpha1.DsRole,
+		},
+	}
+	return &roleBinding
+}
diff --git a/controllers/worker_reconcile.go b/controllers/worker_reconcile.go
index a036f3f..f786d6d 100644
--- a/controllers/worker_reconcile.go
+++ b/controllers/worker_reconcile.go
@@ -62,13 +62,14 @@
 			Name:      podName,
 			Namespace: cr.Namespace,
 			Labels: map[string]string{dsv1alpha1.DsAppName: dsv1alpha1.DsWorkerLabel,
-				dsv1alpha1.DsVersionLabel: ImageName(cr.Spec.Repository, cr.Spec.Version),
+				dsv1alpha1.DsVersionLabel: cr.Spec.Version,
 				dsv1alpha1.DsServiceLabel: dsv1alpha1.DsServiceLabelValue,
 			},
 		},
 		Spec: corev1.PodSpec{
-			Hostname:  podName,
-			Subdomain: dsv1alpha1.DsServiceLabelValue,
+			Hostname:           podName,
+			Subdomain:          dsv1alpha1.DsServiceLabelValue,
+			ServiceAccountName: cr.Spec.ServiceAccount,
 			Containers: []corev1.Container{
 				{
 					Name:            cr.Name,
diff --git a/main.go b/main.go
index 8eaf1ef..dc83e5a 100644
--- a/main.go
+++ b/main.go
@@ -107,12 +107,12 @@
 		setupLog.Error(err, "unable to create controller", "controller", "DSApi")
 		os.Exit(1)
 	}
-	if os.Getenv("ENABLE_WEBHOOKS") != "false" {
-		if err = (&dsv1alpha1.DSMaster{}).SetupWebhookWithManager(mgr); err != nil {
-			setupLog.Error(err, "unable to create webhook", "webhook", "DSMaster")
-			os.Exit(1)
-		}
-	}
+	//if os.Getenv("ENABLE_WEBHOOKS") != "false" {
+	//	if err = (&dsv1alpha1.DSMaster{}).SetupWebhookWithManager(mgr); err != nil {
+	//		setupLog.Error(err, "unable to create webhook", "webhook", "DSMaster")
+	//		os.Exit(1)
+	//	}
+	//}
 	//+kubebuilder:scaffold:builder
 
 	if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {