From 640a4db58314088b1ccee3b8f7db2fb6c90e2f56 Mon Sep 17 00:00:00 2001 From: Michael Burman Date: Fri, 30 Jun 2023 14:35:42 +0300 Subject: [PATCH] Add OperatorConfig option to indicate deployment with OLM (#547) * Modify bundle creation to set olmDeployment = true in the OperatorConfig. If cass-operator has this configuration parameter set, it will set a default serviceAccount to cass-operator-cassandra-default-sa * Wire olmDeployment config property to mean deployed in Openshift to the ReconciliationContext * Apply defaultServiceAccount when creating StatefulSet when running under OpenShift * Enable in main.go the OperatorConfig --- CHANGELOG.md | 1 + Makefile | 3 +- apis/config/v1beta1/operatorconfig_types.go | 3 + cmd/main.go | 9 +-- .../webhook/controller_manager_config.yaml | 1 + config/manager/controller_manager_config.yaml | 1 + config/rbac/nonroot_role.yaml | 25 ++++++++ config/rbac/service_account_nonroot.yaml | 4 ++ ...xample-cassdc-three-nodes-single-rack.yaml | 1 - .../cassandradatacenter_controller.go | 6 +- .../construct_podtemplatespec.go | 6 +- .../construct_podtemplatespec_test.go | 60 +++++++++++++++---- pkg/reconciliation/construct_statefulset.go | 5 +- .../construct_statefulset_test.go | 43 ++++++++++--- pkg/reconciliation/context.go | 14 +++-- pkg/reconciliation/reconcile_racks.go | 6 +- pkg/reconciliation/reconcile_racks_test.go | 32 +++++----- scripts/postprocess-bundle.sh | 2 - scripts/preprocess-bundle.sh | 9 +++ 19 files changed, 173 insertions(+), 58 deletions(-) create mode 100644 config/rbac/nonroot_role.yaml create mode 100644 config/rbac/service_account_nonroot.yaml create mode 100755 scripts/preprocess-bundle.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 714073d8..7669a653 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Changelog for Cass Operator, new PRs should update the `main / unreleased` secti * [CHANGE] [#542](https://github.com/k8ssandra/cass-operator/issues/542) Support 7.x.x version numbers for DSE and 5.x.x for Cassandra * [CHANGE] [#531](https://github.com/k8ssandra/cass-operator/issues/531) Update to Kubebuilder gov4-alpha layout structure * [ENHANCEMENT] [#523](https://github.com/k8ssandra/cass-operator/issues/516) Spec.ServiceAccountName is introduced as replacements to Spec.ServiceAccount (to account for naming changes in Kubernetes itself), also PodTemplateSpec.Spec.ServiceAccountName is supported. Precendence order is: Spec.ServiceAccountName > Spec.ServiceAccount > PodTemplateSpec. +* [ENHANCEMENT] [#541](https://github.com/k8ssandra/cass-operator/issues/541) When deployed through OLM, add serviceAccount to Cassandra pods that use nonroot priviledge * [CHANGE] [#516](https://github.com/k8ssandra/cass-operator/issues/516) Modify sidecar default CPU and memory limits. * [CHANGE] [#495](https://github.com/k8ssandra/cass-operator/issues/495) Remove all the VMware PSP specific code from the codebase. This has been inoperational since 1.8.0 * [CHANGE] [#494](https://github.com/k8ssandra/cass-operator/issues/494) Remove deprecated generated clientsets. diff --git a/Makefile b/Makefile index 4a2d3e88..3630065f 100644 --- a/Makefile +++ b/Makefile @@ -318,7 +318,8 @@ endif bundle: manifests kustomize operator-sdk ## Generate bundle manifests and metadata, then validate generated files. $(OPSDK) generate kustomize manifests -q cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) - $(KUSTOMIZE) build --load-restrictor LoadRestrictionsNone config/manifests | $(OPSDK) generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) + scripts/preprocess-bundle.sh + $(KUSTOMIZE) build --load-restrictor LoadRestrictionsNone config/manifests | $(OPSDK) generate bundle -q --overwrite --extra-service-accounts cass-operator-cassandra-default-sa --version $(VERSION) $(BUNDLE_METADATA_OPTS) scripts/postprocess-bundle.sh $(OPSDK) bundle validate ./bundle --select-optional suite=operatorframework diff --git a/apis/config/v1beta1/operatorconfig_types.go b/apis/config/v1beta1/operatorconfig_types.go index 7dc087d2..b06b6192 100644 --- a/apis/config/v1beta1/operatorconfig_types.go +++ b/apis/config/v1beta1/operatorconfig_types.go @@ -38,6 +38,9 @@ type OperatorConfig struct { // ImageConfigFile indicates the path where to load the imageConfig from ImageConfigFile string `json:"imageConfigFile,omitempty"` + + // OLMDeployed is set to true when operator is deployed through OLM. This will activate additional Openshift features + OLMDeployed bool `json:"olmDeployment,omitempty"` } func init() { diff --git a/cmd/main.go b/cmd/main.go index 7bf7f2be..c2fb27ab 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -115,10 +115,11 @@ func main() { } if err = (&controllers.CassandraDatacenterReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("CassandraDatacenter"), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("cass-operator"), + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("CassandraDatacenter"), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("cass-operator"), + OperatorConfig: &operConfig, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "CassandraDatacenter") os.Exit(1) diff --git a/config/components/webhook/controller_manager_config.yaml b/config/components/webhook/controller_manager_config.yaml index 89e40ac8..4a4490f6 100644 --- a/config/components/webhook/controller_manager_config.yaml +++ b/config/components/webhook/controller_manager_config.yaml @@ -13,3 +13,4 @@ leaderElection: resourceName: b569adb7.cassandra.datastax.com disableWebhooks: false imageConfigFile: /configs/image_config.yaml +olmDeployment: false diff --git a/config/manager/controller_manager_config.yaml b/config/manager/controller_manager_config.yaml index 095232db..9a3a5f47 100644 --- a/config/manager/controller_manager_config.yaml +++ b/config/manager/controller_manager_config.yaml @@ -13,3 +13,4 @@ leaderElection: resourceName: b569adb7.cassandra.datastax.com disableWebhooks: true imageConfigFile: /configs/image_config.yaml +olmDeployment: false \ No newline at end of file diff --git a/config/rbac/nonroot_role.yaml b/config/rbac/nonroot_role.yaml new file mode 100644 index 00000000..ff2fd7c0 --- /dev/null +++ b/config/rbac/nonroot_role.yaml @@ -0,0 +1,25 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: cassandra-nonroot +rules: +- apiGroups: + - security.openshift.io + resourceNames: + - nonroot + resources: + - securitycontextconstraints + verbs: + - use +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cassandra-nonroot-rolebinding +subjects: + - kind: ServiceAccount + name: cassandra-default-sa +roleRef: + kind: Role + name: cassandra-nonroot + apiGroup: rbac.authorization.k8s.io diff --git a/config/rbac/service_account_nonroot.yaml b/config/rbac/service_account_nonroot.yaml new file mode 100644 index 00000000..d53219b1 --- /dev/null +++ b/config/rbac/service_account_nonroot.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cassandra-default-sa diff --git a/config/samples/example-cassdc-three-nodes-single-rack.yaml b/config/samples/example-cassdc-three-nodes-single-rack.yaml index 9f93efb0..2a3a3ccf 100644 --- a/config/samples/example-cassdc-three-nodes-single-rack.yaml +++ b/config/samples/example-cassdc-three-nodes-single-rack.yaml @@ -17,7 +17,6 @@ spec: resources: requests: storage: 10Gi - dockerImageRunsAsCassandra: false resources: requests: memory: 2Gi diff --git a/internal/controllers/cassandra/cassandradatacenter_controller.go b/internal/controllers/cassandra/cassandradatacenter_controller.go index 5e5ac47a..d4ce6fd2 100644 --- a/internal/controllers/cassandra/cassandradatacenter_controller.go +++ b/internal/controllers/cassandra/cassandradatacenter_controller.go @@ -44,6 +44,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" api "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" + configv1beta1 "github.com/k8ssandra/cass-operator/apis/config/v1beta1" ) // datastax.com groups @@ -70,6 +71,9 @@ type CassandraDatacenterReconciler struct { // during reconciliation where we update the mappings for the watches. // Putting it here allows us to get it to both places. SecretWatches dynamicwatch.DynamicWatches + + // OperatorConfig allows Reconciler to access generic configuration properties + OperatorConfig *configv1beta1.OperatorConfig } // Reconcile reads that state of the cluster for a Datacenter object @@ -100,7 +104,7 @@ func (r *CassandraDatacenterReconciler) Reconcile(ctx context.Context, request c logger.Info("======== handler::Reconcile has been called") - rc, err := reconciliation.CreateReconciliationContext(ctx, &request, r.Client, r.Scheme, r.Recorder, r.SecretWatches) + rc, err := reconciliation.CreateReconciliationContext(ctx, &request, r.Client, r.Scheme, r.Recorder, r.SecretWatches, r.OperatorConfig.OLMDeployed) if err != nil { if errors.IsNotFound(err) { diff --git a/pkg/reconciliation/construct_podtemplatespec.go b/pkg/reconciliation/construct_podtemplatespec.go index 24ced444..ca87583b 100644 --- a/pkg/reconciliation/construct_podtemplatespec.go +++ b/pkg/reconciliation/construct_podtemplatespec.go @@ -31,6 +31,7 @@ const ( CassandraContainerName = "cassandra" PvcName = "server-data" SystemLoggerContainerName = "server-system-logger" + OpenShiftPodServiceAccount = "cass-operator-cassandra-default-sa" ) // calculateNodeAffinity provides a way to decide where to schedule pods within a statefulset based on labels @@ -663,7 +664,7 @@ func buildContainers(dc *api.CassandraDatacenter, baseTemplate *corev1.PodTempla return nil } -func buildPodTemplateSpec(dc *api.CassandraDatacenter, rack api.Rack, addLegacyInternodeMount bool) (*corev1.PodTemplateSpec, error) { +func buildPodTemplateSpec(dc *api.CassandraDatacenter, rack api.Rack, addLegacyInternodeMount, openShift bool) (*corev1.PodTemplateSpec, error) { baseTemplate := dc.Spec.PodTemplateSpec.DeepCopy() @@ -672,6 +673,9 @@ func buildPodTemplateSpec(dc *api.CassandraDatacenter, rack api.Rack, addLegacyI } // Service Account + if openShift { + baseTemplate.Spec.ServiceAccountName = OpenShiftPodServiceAccount + } if dc.Spec.ServiceAccountName != "" { baseTemplate.Spec.ServiceAccountName = dc.Spec.ServiceAccountName diff --git a/pkg/reconciliation/construct_podtemplatespec_test.go b/pkg/reconciliation/construct_podtemplatespec_test.go index 1635fb2c..6c30dd23 100644 --- a/pkg/reconciliation/construct_podtemplatespec_test.go +++ b/pkg/reconciliation/construct_podtemplatespec_test.go @@ -570,7 +570,7 @@ func TestCassandraDatacenter_buildPodTemplateSpec_containers_merge(t *testing.T) }, }, } - got, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false) + got, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false, false) assert.NoError(t, err, "should not have gotten error when building podTemplateSpec") assert.Equal(t, 3, len(got.Spec.Containers)) @@ -604,7 +604,7 @@ func TestCassandraDatacenter_buildPodTemplateSpec_initcontainers_merge(t *testin ConfigBuilderResources: testContainer.Resources, }, } - got, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false) + got, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false, false) assert.NoError(t, err, "should not have gotten error when building podTemplateSpec") assert.Equal(t, 2, len(got.Spec.InitContainers)) @@ -647,7 +647,7 @@ func TestCassandraDatacenter_buildPodTemplateSpec_add_initContainer_after_config }, } - podTemplateSpec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false) + podTemplateSpec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false, false) assert.NoError(t, err, "should not have gotten error when building podTemplateSpec") @@ -708,7 +708,7 @@ func TestCassandraDatacenter_buildPodTemplateSpec_add_initContainer_with_volumes }, } - podTemplateSpec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], true) + podTemplateSpec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], true, false) assert.NoError(t, err, "should not have gotten error when building podTemplateSpec") @@ -817,7 +817,7 @@ func TestCassandraDatacenter_buildPodTemplateSpec_add_container_with_volumes(t * }, } - podTemplateSpec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], true) + podTemplateSpec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], true, false) assert.NoError(t, err, "should not have gotten error when building podTemplateSpec") @@ -873,7 +873,7 @@ func TestCassandraDatacenter_buildPodTemplateSpec_add_container_with_volumes(t * testZoneRack := dc.Spec.Racks[0] testZoneRack.NodeAffinityLabels = map[string]string{zoneLabel: "testzone"} dc.Spec.Racks[0] = testZoneRack - podTemplateSpec, err = buildPodTemplateSpec(dc, testZoneRack, false) + podTemplateSpec, err = buildPodTemplateSpec(dc, testZoneRack, false, false) assert.NoError(t, err, "should not have gotten error when building podTemplateSpec") volumes = podTemplateSpec.Spec.Volumes @@ -973,7 +973,7 @@ func TestCassandraDatacenter_buildPodTemplateSpec_labels_merge(t *testing.T) { } dc.Spec.PodTemplateSpec.Labels = map[string]string{"abc": "123"} - spec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false) + spec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false, false) got := spec.Labels expected := dc.GetRackLabels("testrack") @@ -1007,7 +1007,7 @@ func TestCassandraDatacenter_buildContainers_additional_labels(t *testing.T) { } dc.Spec.PodTemplateSpec.Labels = map[string]string{"abc": "123"} - spec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false) + spec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false, false) got := spec.Labels expected := dc.GetRackLabels("testrack") @@ -1048,7 +1048,7 @@ func TestCassandraDatacenter_buildPodTemplateSpec_overrideSecurityContext(t *tes }, } - spec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false) + spec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false, false) assert.NoError(t, err, "should not have gotten an error when building podTemplateSpec") assert.NotNil(t, spec) @@ -1100,7 +1100,7 @@ func TestCassandraDatacenter_buildPodTemplateSpec_do_not_propagate_volumes(t *te }, } - spec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], true) + spec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], true, false) assert.NoError(t, err, "should not have gotten error when building podTemplateSpec") initContainers := spec.Spec.InitContainers @@ -1133,6 +1133,40 @@ func TestCassandraDatacenter_buildPodTemplateSpec_do_not_propagate_volumes(t *te assert.True(t, volumeMountsContains(systemLoggerVolumeMounts, volumeMountNameMatcher("server-logs"))) } +func TestCassandraDatacenter_buildPodTemplateSpec_openShift(t *testing.T) { + assert := assert.New(t) + + dc := &api.CassandraDatacenter{ + Spec: api.CassandraDatacenterSpec{ + ClusterName: "bob", + ServerType: "cassandra", + ServerVersion: "4.1.2", + Racks: []api.Rack{ + { + Name: "default", + }, + }, + }, + } + + spec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], true, false) + assert.NoError(err, "should not have gotten error when building podTemplateSpec") + + assert.Equal(spec.Spec.ServiceAccountName, "", "no default serviceAccount is set") + + spec, err = buildPodTemplateSpec(dc, dc.Spec.Racks[0], true, true) + assert.NoError(err, "should not have gotten error when building podTemplateSpec") + + assert.Equal(spec.Spec.ServiceAccountName, OpenShiftPodServiceAccount, "missing serviceAccount when running under OLM") + + dc.Spec.ServiceAccountName = "overrideSA" + + spec, err = buildPodTemplateSpec(dc, dc.Spec.Racks[0], true, true) + assert.NoError(err, "should not have gotten error when building podTemplateSpec") + + assert.Equal(spec.Spec.ServiceAccountName, "overrideSA", "under OLM the serviceAccountName must be overwritable") +} + func TestCassandraDatacenter_buildContainers_DisableSystemLoggerSidecar(t *testing.T) { dc := &api.CassandraDatacenter{ Spec: api.CassandraDatacenterSpec{ @@ -1323,7 +1357,7 @@ func TestTolerations(t *testing.T) { }, } - spec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false) + spec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false, false) assert.NoError(t, err, "failed to build PodTemplateSpec") // using ElementsMatch instead of Equal because we do not really care about ordering. @@ -1360,7 +1394,7 @@ func TestTolerations(t *testing.T) { }, } - spec, err = buildPodTemplateSpec(dc, dc.Spec.Racks[0], false) + spec, err = buildPodTemplateSpec(dc, dc.Spec.Racks[0], false, false) assert.NoError(t, err, "failed to build PodTemplateSpec") // using ElementsMatch instead of Equal because we do not really care about ordering. @@ -1580,7 +1614,7 @@ func TestServiceAccountPrecedence(t *testing.T) { } for _, test := range tests { - pds, err := buildPodTemplateSpec(test.dc, test.dc.Spec.Racks[0], false) + pds, err := buildPodTemplateSpec(test.dc, test.dc.Spec.Racks[0], false, false) assert.NoError(err) assert.Equal(test.accountName, pds.Spec.ServiceAccountName) } diff --git a/pkg/reconciliation/construct_statefulset.go b/pkg/reconciliation/construct_statefulset.go index 3309446e..12d43e07 100644 --- a/pkg/reconciliation/construct_statefulset.go +++ b/pkg/reconciliation/construct_statefulset.go @@ -78,7 +78,8 @@ func newStatefulSetForCassandraDatacenter( sts *appsv1.StatefulSet, rackName string, dc *api.CassandraDatacenter, - replicaCount int) (*appsv1.StatefulSet, error) { + replicaCount int, + openShift bool) (*appsv1.StatefulSet, error) { replicaCountInt32 := int32(replicaCount) @@ -126,7 +127,7 @@ func newStatefulSetForCassandraDatacenter( nsName := newNamespacedNameForStatefulSet(dc, rackName) - template, err := buildPodTemplateSpec(dc, rack, legacyInternodeMount(dc, sts)) + template, err := buildPodTemplateSpec(dc, rack, legacyInternodeMount(dc, sts), openShift) if err != nil { return nil, err } diff --git a/pkg/reconciliation/construct_statefulset_test.go b/pkg/reconciliation/construct_statefulset_test.go index 888303e8..4c71f79e 100644 --- a/pkg/reconciliation/construct_statefulset_test.go +++ b/pkg/reconciliation/construct_statefulset_test.go @@ -49,7 +49,7 @@ func Test_newStatefulSetForCassandraDatacenter(t *testing.T) { } for _, tt := range tests { t.Log(tt.name) - got, err := newStatefulSetForCassandraDatacenter(nil, tt.args.rackName, tt.args.dc, tt.args.replicaCount) + got, err := newStatefulSetForCassandraDatacenter(nil, tt.args.rackName, tt.args.dc, tt.args.replicaCount, false) assert.NoError(t, err, "newStatefulSetForCassandraDatacenter should not have errored") assert.NotNil(t, got, "newStatefulSetForCassandraDatacenter should not have returned a nil statefulset") assert.Equal(t, map[string]string{"dedicated": "cassandra"}, got.Spec.Template.Spec.NodeSelector) @@ -108,7 +108,7 @@ func Test_newStatefulSetForCassandraDatacenter_additionalLabels(t *testing.T) { } statefulset, newStatefulSetForCassandraDatacenterError := newStatefulSetForCassandraDatacenter(nil, - "rack1", dc, 1) + "rack1", dc, 1, false) assert.NoError(t, newStatefulSetForCassandraDatacenterError, "should not have gotten error when creating the new statefulset") @@ -168,7 +168,7 @@ func Test_newStatefulSetForCassandraDatacenter_ServiceName(t *testing.T) { }, } - sts, err := newStatefulSetForCassandraDatacenter(&appsv1.StatefulSet{}, "default", dc, 1) + sts, err := newStatefulSetForCassandraDatacenter(&appsv1.StatefulSet{}, "default", dc, 1, false) require.NoError(t, err) assert.Equal(t, dc.GetAllPodsServiceName(), sts.Spec.ServiceName) @@ -205,7 +205,7 @@ func TestStatefulSetWithAdditionalVolumesFromSource(t *testing.T) { }, } - sts, err := newStatefulSetForCassandraDatacenter(nil, "r1", dc, 3) + sts, err := newStatefulSetForCassandraDatacenter(nil, "r1", dc, 3, false) assert.NoError(err) assert.Equal(3, len(sts.Spec.Template.Spec.Volumes)) @@ -268,7 +268,7 @@ func TestStatefulSetWithAdditionalVolumesFromSource(t *testing.T) { }, } - sts, err = newStatefulSetForCassandraDatacenter(nil, "r1", dc, 3) + sts, err = newStatefulSetForCassandraDatacenter(nil, "r1", dc, 3, false) assert.NoError(err) assert.Equal(3, len(sts.Spec.VolumeClaimTemplates)) @@ -367,7 +367,7 @@ func Test_newStatefulSetForCassandraDatacenterWithAdditionalVolumes(t *testing.T } for _, tt := range tests { t.Log(tt.name) - got, err := newStatefulSetForCassandraDatacenter(nil, tt.args.rackName, tt.args.dc, tt.args.replicaCount) + got, err := newStatefulSetForCassandraDatacenter(nil, tt.args.rackName, tt.args.dc, tt.args.replicaCount, false) assert.NoError(t, err, "newStatefulSetForCassandraDatacenter should not have errored") assert.NotNil(t, got, "newStatefulSetForCassandraDatacenter should not have returned a nil statefulset") @@ -555,7 +555,7 @@ func Test_newStatefulSetForCassandraPodSecurityContext(t *testing.T) { } for _, tt := range tests { t.Log(tt.name) - statefulSet, err := newStatefulSetForCassandraDatacenter(nil, rack, tt.dc, replicas) + statefulSet, err := newStatefulSetForCassandraDatacenter(nil, rack, tt.dc, replicas, false) assert.NoError(t, err, fmt.Sprintf("%s: failed to create new statefulset", tt.name)) assert.NotNil(t, statefulSet, fmt.Sprintf("%s: statefulset is nil", tt.name)) @@ -655,7 +655,7 @@ func Test_newStatefulSetForCassandraDatacenter_dcNameOverride(t *testing.T) { } statefulset, newStatefulSetForCassandraDatacenterError := newStatefulSetForCassandraDatacenter(nil, - "rack1", dc, 1) + "rack1", dc, 1, false) assert.NoError(t, newStatefulSetForCassandraDatacenterError, "should not have gotten error when creating the new statefulset") @@ -668,6 +668,33 @@ func Test_newStatefulSetForCassandraDatacenter_dcNameOverride(t *testing.T) { } } +func Test_newStatefulSetForCassandraDatacenter_OpenShift(t *testing.T) { + assert := assert.New(t) + dc := &api.CassandraDatacenter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dc1", + }, + Spec: api.CassandraDatacenterSpec{ + ClusterName: "cluster1", + ServerType: "cassandra", + ServerVersion: "4.1.2", + PodTemplateSpec: &corev1.PodTemplateSpec{}, + StorageConfig: api.StorageConfig{ + CassandraDataVolumeClaimSpec: &corev1.PersistentVolumeClaimSpec{}, + }, + Racks: []api.Rack{ + { + Name: "rack1", + }, + }, + }, + } + + statefulset, err := newStatefulSetForCassandraDatacenter(nil, dc.Spec.Racks[0].Name, dc, 1, true) + assert.NoError(err) + assert.Equal(OpenShiftPodServiceAccount, statefulset.Spec.Template.Spec.ServiceAccountName) +} + func int64Ptr(n int64) *int64 { return &n } diff --git a/pkg/reconciliation/context.go b/pkg/reconciliation/context.go index c3e7dbb0..1d4e6f1c 100644 --- a/pkg/reconciliation/context.go +++ b/pkg/reconciliation/context.go @@ -14,7 +14,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" - runtimeClient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -27,13 +26,14 @@ import ( // ReconciliationContext contains all of the input necessary to calculate a list of ReconciliationActions type ReconciliationContext struct { Request *reconcile.Request - Client runtimeClient.Client + Client client.Client Scheme *runtime.Scheme Datacenter *api.CassandraDatacenter NodeMgmtClient httphelper.NodeMgmtClient Recorder record.EventRecorder ReqLogger logr.Logger SecretWatches dynamicwatch.DynamicWatches + OpenShift bool // According to golang recommendations the context should not be stored in a struct but given that // this is passed around as a parameter we feel that its a fair compromise. For further discussion @@ -52,10 +52,11 @@ type ReconciliationContext struct { func CreateReconciliationContext( ctx context.Context, req *reconcile.Request, - cli runtimeClient.Client, + cli client.Client, scheme *runtime.Scheme, rec record.EventRecorder, - secretWatches dynamicwatch.DynamicWatches) (*ReconciliationContext, error) { + secretWatches dynamicwatch.DynamicWatches, + runningInOpenshift bool) (*ReconciliationContext, error) { reqLogger := log.FromContext(ctx) rc := &ReconciliationContext{} @@ -66,6 +67,7 @@ func CreateReconciliationContext( rc.SecretWatches = secretWatches rc.ReqLogger = reqLogger rc.Ctx = ctx + rc.OpenShift = runningInOpenshift rc.ReqLogger = rc.ReqLogger. WithValues("namespace", req.Namespace) @@ -119,7 +121,7 @@ func (rc *ReconciliationContext) GetLogger() logr.Logger { return rc.ReqLogger } -func (rc *ReconciliationContext) GetClient() runtimeClient.Client { +func (rc *ReconciliationContext) GetClient() client.Client { return rc.Client } @@ -164,7 +166,7 @@ func (rc *ReconciliationContext) validateDatacenterNameOverride() []error { return errs } else { if *dc.Status.DatacenterName != dc.Spec.DatacenterName { - errs = append(errs, fmt.Errorf("datacenter %s name override '%s' cannot be changed after creation to '%s'.", dc.Name, dc.Spec.DatacenterName, *dc.Status.DatacenterName)) + errs = append(errs, fmt.Errorf("datacenter %s name override '%s' cannot be changed after creation to '%s'", dc.Name, dc.Spec.DatacenterName, *dc.Status.DatacenterName)) } } diff --git a/pkg/reconciliation/reconcile_racks.go b/pkg/reconciliation/reconcile_racks.go index 64f6d4fa..de41811e 100644 --- a/pkg/reconciliation/reconcile_racks.go +++ b/pkg/reconciliation/reconcile_racks.go @@ -179,7 +179,7 @@ func (rc *ReconciliationContext) CheckRackPodTemplate() result.ReconcileResult { } statefulSet := rc.statefulSets[idx] - desiredSts, err := newStatefulSetForCassandraDatacenter(statefulSet, rackName, dc, int(*statefulSet.Spec.Replicas)) + desiredSts, err := newStatefulSetForCassandraDatacenter(statefulSet, rackName, dc, int(*statefulSet.Spec.Replicas), rc.OpenShift) if err != nil { logger.Error(err, "error calling newStatefulSetForCassandraDatacenter") @@ -330,7 +330,7 @@ func (rc *ReconciliationContext) CheckRackForceUpgrade() result.ReconcileResult // have to use zero here, because each statefulset is created with no replicas // in GetStatefulSetForRack() - desiredSts, err := newStatefulSetForCassandraDatacenter(statefulSet, rackName, dc, nextRack.NodeCount) + desiredSts, err := newStatefulSetForCassandraDatacenter(statefulSet, rackName, dc, nextRack.NodeCount, false) if err != nil { logger.Error(err, "error calling newStatefulSetForCassandraDatacenter") return result.Error(err) @@ -1378,7 +1378,7 @@ func (rc *ReconciliationContext) GetStatefulSetForRack( currentStatefulSet, nextRack.RackName, rc.Datacenter, - nextRack.NodeCount) + nextRack.NodeCount, false) if err != nil { return nil, false, err } diff --git a/pkg/reconciliation/reconcile_racks_test.go b/pkg/reconciliation/reconcile_racks_test.go index 6db7197d..b0e6e93a 100644 --- a/pkg/reconciliation/reconcile_racks_test.go +++ b/pkg/reconciliation/reconcile_racks_test.go @@ -210,7 +210,7 @@ func TestReconcileRacks_ReconcilePods(t *testing.T) { nil, "default", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") desiredStatefulSet.Spec.Replicas = &one @@ -389,7 +389,7 @@ func TestReconcilePods(t *testing.T) { nil, "default", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") statefulSet.Status.Replicas = int32(1) @@ -407,7 +407,7 @@ func TestReconcilePods_WithVolumes(t *testing.T) { nil, "default", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") statefulSet.Status.Replicas = int32(1) @@ -465,7 +465,7 @@ func TestReconcileNextRack(t *testing.T) { nil, "default", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") err = rc.ReconcileNextRack(statefulSet) @@ -489,7 +489,7 @@ func TestReconcileNextRack_CreateError(t *testing.T) { nil, "default", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") mockClient := mocks.NewClient(t) @@ -566,7 +566,7 @@ func TestReconcileRacks(t *testing.T) { nil, "default", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") trackObjects := []runtime.Object{ @@ -639,7 +639,7 @@ func TestReconcileRacks_WaitingForReplicas(t *testing.T) { nil, "default", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") trackObjects := []runtime.Object{ @@ -681,7 +681,7 @@ func TestReconcileRacks_NeedMoreReplicas(t *testing.T) { nil, "default", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") trackObjects := []runtime.Object{ @@ -716,7 +716,7 @@ func TestReconcileRacks_DoesntScaleDown(t *testing.T) { nil, "default", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") trackObjects := []runtime.Object{ @@ -757,7 +757,7 @@ func TestReconcileRacks_NeedToPark(t *testing.T) { nil, "default", rc.Datacenter, - 3) + 3, false) assert.NoErrorf(t, err, "error occurred creating statefulset") trackObjects := []runtime.Object{ @@ -802,7 +802,7 @@ func TestReconcileRacks_AlreadyReconciled(t *testing.T) { nil, "default", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") desiredStatefulSet.Status.ReadyReplicas = 2 @@ -843,7 +843,7 @@ func TestReconcileStatefulSet_ImmutableSpec(t *testing.T) { nil, "rack0", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(err, "error occurred creating statefulset") assert.NotEqual("immutable-service", origStatefulSet.Spec.ServiceName) @@ -853,7 +853,7 @@ func TestReconcileStatefulSet_ImmutableSpec(t *testing.T) { origStatefulSet, "rack0", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(err, "error occurred creating statefulset") assert.Equal("immutable-service", modifiedStatefulSet.Spec.ServiceName) @@ -869,7 +869,7 @@ func TestReconcileRacks_FirstRackAlreadyReconciled(t *testing.T) { nil, "rack0", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") desiredStatefulSet.Status.ReadyReplicas = 2 @@ -878,7 +878,7 @@ func TestReconcileRacks_FirstRackAlreadyReconciled(t *testing.T) { nil, "rack1", rc.Datacenter, - 1) + 1, false) assert.NoErrorf(t, err, "error occurred creating statefulset") secondDesiredStatefulSet.Status.ReadyReplicas = 1 @@ -980,7 +980,7 @@ func TestReconcileRacks_UpdateConfig(t *testing.T) { nil, "rack0", rc.Datacenter, - 2) + 2, false) assert.NoErrorf(t, err, "error occurred creating statefulset") desiredStatefulSet.Status.ReadyReplicas = 2 diff --git a/scripts/postprocess-bundle.sh b/scripts/postprocess-bundle.sh index 844e054e..91e38117 100755 --- a/scripts/postprocess-bundle.sh +++ b/scripts/postprocess-bundle.sh @@ -5,13 +5,11 @@ cat <> bundle.Dockerfile # Certified Openshift required labels LABEL com.redhat.openshift.versions="v4.9" LABEL com.redhat.delivery.operator.bundle=true -LABEL com.redhat.delivery.backport=true EOF # Add them to the bundle metadata also yq eval -i '.annotations."com.redhat.openshift.versions" = "v4.9"' bundle/metadata/annotations.yaml yq eval -i '.annotations."com.redhat.delivery.operator.bundle" = true' bundle/metadata/annotations.yaml -yq eval -i '.annotations."com.redhat.delivery.backport" = true' bundle/metadata/annotations.yaml yq eval -i '.annotations."com.redhat.openshift.versions" headComment = "Certified Openshift required labels"' bundle/metadata/annotations.yaml # This file is extra from creation process on config/manifests, should not be in the bundle itself diff --git a/scripts/preprocess-bundle.sh b/scripts/preprocess-bundle.sh new file mode 100755 index 00000000..d4c1d20b --- /dev/null +++ b/scripts/preprocess-bundle.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +cat << EOF >> config/rbac/kustomization.yaml +# Add Openshift nonroot Role and ServiceAccount +- nonroot_role.yaml +- service_account_nonroot.yaml +EOF + +yq eval -i '.olmDeployment = true' config/components/webhook/controller_manager_config.yaml