Repository: k8ssandra/k8ssandra-operator (go)

Issue:
Service account of Cassandra pods should be configurable
<!--

Feel free to ask questions on Discord at https://discord.com/invite/y4s64xC9 or in the forum at https://forum.k8ssandra.io/. 
-->

**What is missing?**
It currently isn't possible to specify a pre-existing service account to use for Cassandra pods. 

The CassandraDatacenter spec has a `ServiceAccount` field. We need to expose that in `DatacenterOptions`.

**Why do we need it?**
This is needed for improved security and configuration. Actually the default service really shouldn't ever be used but that is a larger effort which should be addressed separately.

**Environment**

* K8ssandra Operator version:

    `1.4.0`
    <!-- Try kubectl describe deployment k8ssandra-operator -->
    <!-- Note: please provide operator version and not a helm chart version -->

**Anything else we need to know?**:


Pull request description:
Make ServiceAccount of Cassandra pods configurable (fixes #783)
**What this PR does**:
Make ServiceAccount of Cassandra pods configurable

**Which issue(s) this PR fixes**:
Fixes #783

**Checklist**
- [x] Changes manually tested
- [x] Automated Tests added/updated
- [ ] Documentation added/updated
- [x] CHANGELOG.md updated (not required for documentation PRs)
- [x] CLA Signed:  [DataStax CLA](https://cla.datastax.com/)


Patch:
diff --git a/CHANGELOG/CHANGELOG-1.5.md b/CHANGELOG/CHANGELOG-1.5.md
index 3c0f0062..c7ebc4de 100644
--- a/CHANGELOG/CHANGELOG-1.5.md
+++ b/CHANGELOG/CHANGELOG-1.5.md
@@ -25,6 +25,7 @@ When cutting a new release, update the `unreleased` heading to the tag being gen
 * [ENHANCEMENT] [#770](https://github.com/k8ssandra/k8ssandra-operator/issues/770) Update to Go 1.19 and Operator SDK 1.25.2
 * [ENHANCEMENT] [#525](https://github.com/k8ssandra/k8ssandra-operator/issues/525) Deep-merge cluster- and dc-level templates
 * [ENHANCEMENT] [#781](https://github.com/k8ssandra/k8ssandra-operator/issues/781) Expose perNodeConfigInitContainer.Image through CRD
+* [ENHANCEMENT] [#783](https://github.com/k8ssandra/k8ssandra-operator/issues/783) Make ServiceAccount of Cassandra pods configurable
 * [BUGFIX] [#726](https://github.com/k8ssandra/k8ssandra-operator/issues/726) Don't propagate Cassandra tolerations to the Stargate deployment
 * [BUGFIX] [#778](https://github.com/k8ssandra/k8ssandra-operator/issues/778) Fix Stargate deployments on k8s clusters using a custom domain name
 * [TESTING] [#761](https://github.com/k8ssandra/k8ssandra-operator/issues/761) Stabilize integration and e2e tests
diff --git a/apis/k8ssandra/v1alpha1/k8ssandracluster_types.go b/apis/k8ssandra/v1alpha1/k8ssandracluster_types.go
index 2eaf9ff3..3552585d 100644
--- a/apis/k8ssandra/v1alpha1/k8ssandracluster_types.go
+++ b/apis/k8ssandra/v1alpha1/k8ssandracluster_types.go
@@ -419,6 +419,9 @@ type DatacenterOptions struct {
 	// +optional
 	// +kubebuilder:default="mikefarah/yq:4"
 	PerNodeConfigInitContainerImage string `json:"perNodeConfigInitContainerImage,omitempty"`
+
+	// The k8s service account to use for the Cassandra pods
+	ServiceAccount string `json:"serviceAccount,omitempty"`
 }
 
 // NetworkingConfig is a copy of cass-operator's NetworkingConfig struct. It is copied here to
diff --git a/config/crd/bases/k8ssandra.io_k8ssandraclusters.yaml b/config/crd/bases/k8ssandra.io_k8ssandraclusters.yaml
index e9124993..194ba82d 100644
--- a/config/crd/bases/k8ssandra.io_k8ssandraclusters.yaml
+++ b/config/crd/bases/k8ssandra.io_k8ssandraclusters.yaml
@@ -7748,6 +7748,10 @@ spec:
                             and 4.0.X - DSE: 6.8.X'
                           pattern: (6\.8\.\d+)|(3\.11\.\d+)|(4\.\d+\.\d+)
                           type: string
+                        serviceAccount:
+                          description: The k8s service account to use for the Cassandra
+                            pods
+                          type: string
                         size:
                           description: Size is the number Cassandra pods to deploy
                             in this datacenter. This number does not include Stargate
@@ -15753,6 +15757,10 @@ spec:
                       - DSE: 6.8.X'
                     pattern: (6\.8\.\d+)|(3\.11\.\d+)|(4\.\d+\.\d+)
                     type: string
+                  serviceAccount:
+                    description: The k8s service account to use for the Cassandra
+                      pods
+                    type: string
                   softPodAntiAffinity:
                     description: SoftPodAntiAffinity sets whether multiple Cassandra
                       instances can be scheduled on the same node. This should normally
diff --git a/pkg/cassandra/datacenter.go b/pkg/cassandra/datacenter.go
index 388054fa..f877d726 100644
--- a/pkg/cassandra/datacenter.go
+++ b/pkg/cassandra/datacenter.go
@@ -114,6 +114,7 @@ type DatacenterConfig struct {
 	ManagementApiAuth         *cassdcapi.ManagementApiAuthConfig
 	PerNodeConfigMapRef       corev1.LocalObjectReference
 	PerNodeInitContainerImage string
+	ServiceAccount            string
 	ExternalSecrets           bool
 	McacEnabled               bool
 
@@ -173,6 +174,7 @@ func NewDatacenter(klusterKey types.NamespacedName, template *DatacenterConfig)
 			PodTemplateSpec:     &template.PodTemplateSpec,
 			CDC:                 template.CDC,
 			DseWorkloads:        template.DseWorkloads,
+			ServiceAccount:      template.ServiceAccount,
 		},
 	}
 
@@ -339,6 +341,7 @@ func Coalesce(clusterName string, clusterTemplate *api.CassandraClusterTemplate,
 	dcConfig.ManagementApiAuth = mergedOptions.ManagementApiAuth
 	dcConfig.PodTemplateSpec.Spec.SecurityContext = mergedOptions.PodSecurityContext
 	dcConfig.PerNodeInitContainerImage = mergedOptions.PerNodeConfigInitContainerImage
+	dcConfig.ServiceAccount = mergedOptions.ServiceAccount
 
 	dcConfig.Meta.Metadata = goalesceutils.MergeCRs(clusterTemplate.Meta, dcTemplate.Meta.Metadata)
 


Test patch:
diff --git a/pkg/cassandra/datacenter_test.go b/pkg/cassandra/datacenter_test.go
index bd5d7141..dd7bf1d5 100644
--- a/pkg/cassandra/datacenter_test.go
+++ b/pkg/cassandra/datacenter_test.go
@@ -1304,6 +1304,28 @@ func TestCoalesce(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "Override service account",
+			clusterTemplate: &api.CassandraClusterTemplate{
+				DatacenterOptions: api.DatacenterOptions{
+					ServiceAccount: "cluster_account",
+				},
+			},
+			dcTemplate: &api.CassandraDatacenterTemplate{
+				DatacenterOptions: api.DatacenterOptions{
+					ServiceAccount: "dc_account",
+				},
+			},
+			want: &DatacenterConfig{
+				ServiceAccount: "dc_account",
+				McacEnabled:    true,
+				PodTemplateSpec: corev1.PodTemplateSpec{
+					Spec: corev1.PodSpec{
+						Containers: []corev1.Container{{Name: "cassandra"}},
+					},
+				},
+			},
+		},
 	}
 
 	for _, tc := range tests {
@@ -1388,6 +1410,17 @@ func TestNewDatacenter_Tolerations(t *testing.T) {
 	assert.Equal(t, template.Tolerations, dc.Spec.Tolerations)
 }
 
+func TestNewDatacenter_ServiceAccount(t *testing.T) {
+	template := GetDatacenterConfig()
+	template.ServiceAccount = "svc"
+	dc, err := NewDatacenter(
+		types.NamespacedName{Name: "testdc", Namespace: "test-namespace"},
+		&template,
+	)
+	assert.NoError(t, err)
+	assert.Equal(t, template.ServiceAccount, dc.Spec.ServiceAccount)
+}
+
 // TestValidateCoalesced_Fail_NoStorageConfig tests that NewDatacenter fails when no storage config is provided.
 func TestValidateDatacenterConfig_Fail_NoStorageConfig(t *testing.T) {
 	template := GetDatacenterConfig()