Repository: rook/rook
Pull request #15496 (2025-03-04 09:23:27)

exporter: add missing rook-ceph-exporter container port definition
Adds the missing exporter container port to enable proper Prometheus scraping and filtering.

Addresses https://github.com/rook/rook/issues/15446

<!-- Thank you for contributing to Rook! -->

<!-- STEPS TO FOLLOW:
  1. Add a description of the changes (frequently the same as the commit description)
  2. Enter the issue number next to "Resolves #" below (if there is no tracking issue resolved, **remove that section**)
  3. Review our Contributing documentation at https://rook.io/docs/rook/latest/Contributing/development-flow/
  4. Follow the steps in the checklist below, starting with the **Commit Message Formatting**.
-->

<!-- Uncomment this section with the issue number if an issue is being resolved
**Issue resolved by this Pull Request:**
Resolves #
--->

**Checklist:**

- [ ] **Commit Message Formatting**: Commit titles and messages follow guidelines in the [developer guide](https://rook.io/docs/rook/latest/Contributing/development-flow/#commit-structure).
- [ ] Reviewed the developer guide on [Submitting a Pull Request](https://rook.io/docs/rook/latest/Contributing/development-flow/#submitting-a-pull-request)
- [ ] [Pending release notes](https://github.com/rook/rook/blob/master/PendingReleaseNotes.md) updated with breaking and/or notable changes for the next minor release.
- [ ] Documentation has been updated, if necessary.
- [ ] Unit tests have been added, if necessary.
- [ ] Integration tests have been added, if necessary.



Patch:
diff --git a/.github/workflows/linters.yaml b/.github/workflows/linters.yaml
index 970163609..db72d3c18 100644
--- a/.github/workflows/linters.yaml
+++ b/.github/workflows/linters.yaml
@@ -61,4 +61,4 @@ jobs:
         run: make pylint
 
       - name: Setup black for py
-        uses: psf/black@8a737e727ac5ab2f1d4cf5876720ed276dc8dc4b # stable
+        uses: psf/black@1b2427a2b785cc4aac97c19bb4b9a0de063f9547 # stable
diff --git a/.github/workflows/push-build.yaml b/.github/workflows/push-build.yaml
index af40e27ac..25c9dc10c 100644
--- a/.github/workflows/push-build.yaml
+++ b/.github/workflows/push-build.yaml
@@ -14,7 +14,6 @@ defaults:
 
 permissions:
   contents: read
-  packages: write
 
 jobs:
   push-image-to-container-registry:
@@ -42,20 +41,6 @@ jobs:
           username: ${{ secrets.DOCKER_USERNAME }}
           password: ${{ secrets.DOCKER_PASSWORD }}
 
-      - name: Login to Quay.io
-        uses: docker/login-action@v3
-        with:
-          registry: quay.io
-          username: ${{ secrets.QUAY_USERNAME }}
-          password: ${{ secrets.QUAY_ROBOT_TOKEN }}
-
-      - name: Login to GitHub Container Registry
-        uses: docker/login-action@v3
-        with:
-          registry: ghcr.io
-          username: ${{ github.actor }}
-          password: ${{ secrets.GITHUB_TOKEN }}
-
       - name: Configure AWS Credentials
         uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 # v4.1.0
         with:
@@ -69,10 +54,6 @@ jobs:
           echo "BRANCH_NAME=${GITHUB_REF##*/}" >> $GITHUB_ENV
           echo "GITHUB_REF"=${GITHUB_REF} >> $GITHUB_ENV
 
-      - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
-        with:
-          python-version: 3.13
-
       - name: Install dependencies
         run: |
           python3 -m pip install --upgrade pip setuptools wheel
diff --git a/Documentation/CRDs/Cluster/ceph-cluster-crd.md b/Documentation/CRDs/Cluster/ceph-cluster-crd.md
index eea150e38..51b4e4fc1 100755
--- a/Documentation/CRDs/Cluster/ceph-cluster-crd.md
+++ b/Documentation/CRDs/Cluster/ceph-cluster-crd.md
@@ -150,13 +150,6 @@ A specific will contain a specific release of Ceph as well as security fixes fro
 * `failureDomainLabel`: The label that is expected on each node where the mons
     are expected to be deployed. The labels must be found in the list of
     well-known [topology labels](#osd-topology).
-* `externalMonIDs`: ID list of external mons deployed outside of Rook cluster
-    and not managed by Rook. If set, Rook will not remove external mons from quorum
-    and populate external mons addresses to mon endpoints for CSI.
-    This parameter is supported only for local Rook cluster running in normal mode,
-    meaning that it will be ignored for external cluster (`spec.external.enabled: true`)
-    or for `stretchedCluster`.
-    For more details see [external mons](../../Storage-Configuration/Advanced/ceph-mon-health.md#external-monitors).
 * `zones`: The failure domain names where the Mons are expected to be deployed.
     There must be **at least three zones** specified in the list. Each zone can be
     backed by a different storage class by specifying the `volumeClaimTemplate`.
diff --git a/Documentation/CRDs/Object-Storage/ceph-object-store-crd.md b/Documentation/CRDs/Object-Storage/ceph-object-store-crd.md
index f04c71013..cd8839836 100644
--- a/Documentation/CRDs/Object-Storage/ceph-object-store-crd.md
+++ b/Documentation/CRDs/Object-Storage/ceph-object-store-crd.md
@@ -390,10 +390,6 @@ spec:
       debug_rgw: "10" # int
       # debug-rgw: "20" # equivalent config keys can have dashes or underscores
       rgw_s3_auth_use_ldap: "true" # bool
-    rgwConfigFromSecret:
-      rgw_keystone_barbican_password: # name of rgw option with secret value
-        name: "barbican-secret" # name of K8s secret 
-        key: "password" # key of secret value in K8s secret.Data map[string]string
     rgwCommandFlags:
       rgw_dmclock_auth_res: "100.0" # float
       rgw_d4n_l1_datacache_persistent_path: /var/log/rook/rgwd4ncache # string
@@ -401,13 +397,12 @@ spec:
 ```
 
 * `rgwConfig` - These configurations are applied and modified at runtime, without RGW restart.
-* `rgwConfigFromSecret` - same as `rgwConfig` but config value is referenced from k8s secret.
 * `rgwCommandFlags` - These configurations are applied as CLI arguments and result in RGW daemons
     restarting when updates are applied. Restarts are desired behavior for some RGW configs.
 
 !!! note
-    Once an `rgwConfig` or `rgwConfigFromSecret` is set, it will not be removed from Ceph's central config store when removed
-    from the `rgwConfig` or `rgwConfigFromSecret` spec. Be sure to specifically set values back to their defaults once done.
+    Once an `rgwConfig` is set, it will not be removed from Ceph's central config store when removed
+    from the `rgwConfig` spec. Be sure to specifically set values back to their defaults once done.
     With this in mind, `rgwCommandFlags` may be a better choice for temporary config values like
     debug levels.
 
@@ -460,34 +455,6 @@ data:
     "bindpass.secret": aGVsbG8ud29ybGQK # hello.world
 ```
 
-### Example - usage with `rgwConfigFromSecret`
-
-The sample configuration below demonstrates how to securely handle secret configuration parameters when setting up RGW for [Barbican integration](https://docs.ceph.com/en/latest/radosgw/config-ref/#barbican-settings).
-
-```yaml
-  # ...
-  gateway:
-    # ...
-    rgwConfig:
-      rgw_barbican_url: "http://barbican.example.com:9311"
-      rgw_keystone_barbican_domain: "domain"
-      rgw_keystone_barbican_project: "project" 
-      rgw_keystone_barbican_user: "user"
-    rgwConfigFromSecret:
-      rgw_keystone_barbican_password:
-        name: "barbican-secret"
-        key: "password"
----
-apiVersion: v1
-kind: Secret
-metadata:
-  name: barbican-secret
-  namespace: rook-ceph
-type: Opaque
-data:
-  "password": aGVsbG8ud29ybGQK # hello.world
-```
-
 ## Deleting a CephObjectStore
 
 During deletion of a CephObjectStore resource, Rook protects against accidental or premature
diff --git a/Documentation/CRDs/specification.md b/Documentation/CRDs/specification.md
index 781234732..99e2000fd 100644
--- a/Documentation/CRDs/specification.md
+++ b/Documentation/CRDs/specification.md
@@ -7203,23 +7203,6 @@ applied. Use with caution.</p>
 </tr>
 <tr>
 <td>
-<code>rgwConfigFromSecret</code><br/>
-<em>
-<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#secretkeyselector-v1-core">
-map[string]k8s.io/api/core/v1.SecretKeySelector
-</a>
-</em>
-</td>
-<td>
-<em>(Optional)</em>
-<p>RgwConfigFromSecret works exactly like RgwConfig but takes config value from Secret Key reference.
-Values are modified at runtime without RGW restart.
-This feature is intended for advanced users. It allows breaking configurations to be easily
-applied. Use with caution.</p>
-</td>
-</tr>
-<tr>
-<td>
 <code>rgwCommandFlags</code><br/>
 <em>
 map[string]string
@@ -8794,22 +8777,6 @@ VolumeClaimTemplate
 <p>VolumeClaimTemplate is the PVC definition</p>
 </td>
 </tr>
-<tr>
-<td>
-<code>externalMonIDs</code><br/>
-<em>
-[]string
-</em>
-</td>
-<td>
-<em>(Optional)</em>
-<p>ExternalMonIDs - optional list of monitor IDs which are deployed externally and not managed by Rook.
-If set, Rook will not remove mons with given IDs from quorum.
-This parameter is used only for local Rook cluster running in normal mode
-and will be ignored if external or stretched mode is used.
-leading</p>
-</td>
-</tr>
 </tbody>
 </table>
 <h3 id="ceph.rook.io/v1.MonZoneSpec">MonZoneSpec
diff --git a/Documentation/Contributing/development-flow.md b/Documentation/Contributing/development-flow.md
index b3418016a..60714b2ad 100644
--- a/Documentation/Contributing/development-flow.md
+++ b/Documentation/Contributing/development-flow.md
@@ -207,11 +207,6 @@ All pull requests must pass all continuous integration (CI) tests before they ca
 automatically run against every pull request. The results of these tests along with code review feedback determine whether
 your request will be merged.
 
-For developers who would like to locally test their changes
-as much as possible before pushing
-(a behavior that is not required but highly encouraged),
-the unit tests and various linters can easily be run locally.
-
 ## Unit Tests
 
 From the root of your local Rook repo execute the following to run all of the unit tests:
@@ -248,21 +243,6 @@ Common cases that may need tests:
 * an input is specified incorrectly, for each input
 * a resource the code relies on doesn't exist, for each dependency
 
-## Linting
-
-Run `make lint` to run all linters. This will require the following CLI tools to be installed on the system:
-
-* [yamllint](https://www.yamllint.com/)
-* [shellcheck](https://www.shellcheck.net/)
-* [pylint](https://www.pylint.org/)
-* [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2)
-
-Many of those tools are available via `brew` for MacOS or as Linux distribution packages.
-Otherwise refer to the respective project websites for installation instructions.
-
-!!! tip
-    Run `make help` to see a list of all possible make targets
-
 ## Integration Tests
 
 Rook's upstream continuous integration (CI) tests will run integration tests against your changes
@@ -279,7 +259,7 @@ See the action details for an ssh connection to the Github runner.
 
 ## Commit structure
 
-Rook maintainers value clear and detailed commit messages.
+Rook maintainers value clear, lengthy and explanatory commit messages.
 
 Requirements for commits:
 
diff --git a/Documentation/Helm-Charts/operator-chart.md b/Documentation/Helm-Charts/operator-chart.md
index 4307fc083..6b97e20c9 100644
--- a/Documentation/Helm-Charts/operator-chart.md
+++ b/Documentation/Helm-Charts/operator-chart.md
@@ -67,9 +67,7 @@ The following table lists the configurable parameters of the rook-operator chart
 | `csi.csiAddons.enabled` | Enable CSIAddons | `false` |
 | `csi.csiAddons.repository` | CSIAddons sidecar image repository | `"quay.io/csiaddons/k8s-sidecar"` |
 | `csi.csiAddons.tag` | CSIAddons sidecar image tag | `"v0.11.0"` |
-| `csi.csiAddonsCephFSProvisionerPort` | CSI Addons server port for the Ceph FS provisioner | `9070` |
 | `csi.csiAddonsPort` | CSI Addons server port | `9070` |
-| `csi.csiAddonsRBDProvisionerPort` | CSI Addons server port for the RBD provisioner | `9070` |
 | `csi.csiCephFSPluginResource` | CEPH CSI CephFS plugin resource requirement list | see values.yaml |
 | `csi.csiCephFSPluginVolume` | The volume of the CephCSI CephFS plugin DaemonSet | `nil` |
 | `csi.csiCephFSPluginVolumeMount` | The volume mounts of the CephCSI CephFS plugin DaemonSet | `nil` |
diff --git a/Documentation/Storage-Configuration/Advanced/ceph-mon-health.md b/Documentation/Storage-Configuration/Advanced/ceph-mon-health.md
index 5e69b9ac2..f39c931b3 100644
--- a/Documentation/Storage-Configuration/Advanced/ceph-mon-health.md
+++ b/Documentation/Storage-Configuration/Advanced/ceph-mon-health.md
@@ -124,43 +124,3 @@ CephCluster CR:
 - `spec.network.hostNetwork`: When enabled or disabled, Rook fails over all monitors, configuring them to enable or disable host networking.
 - `spec.network.Provider` : When updated from being empty to "host", Rook fails over all monitors, configuring them to enable or disable host networking.
 - `spec.network.multiClusterService`: When enabled or disabled, Rook fails over all monitors, configuring them to start (or stop) using service IPs compatible with the multi-cluster service.
-
-## External Monitors
-
-!!! attention
-    This feature is experimental.
-
-It is possible to have both Rook-managed and external monitors in the same Rook cluster.
-One use case for this is 2 datacenter aka 2-AZ (Availability Zone) setup. For 2-AZ setup, Zone outage will lead to loss of the k8s control plane and half of the worker nodes hosting Rook mons.
-In this case remaining half of the Rook cluster will not be able to form quorum and will be in a stuck state even if the other half of worker nodes are still up.
-To avoid this situation, external mons can be used to form quorum and keep the cluster running.
-
-If there are external monitors, Rook must be aware of them, otherwise Rook will remove the unknown mons from the quorum. This is done by setting the `mon.externalMonIDs` field in the CephCluster CR. The `mon.count` ignores the number of `mon.externalMonIDs`. For example, if `mon.count = 2`, Rook will create two internal mons no matter how many external mons are in the cluster and no matter what their health state might be. External monitors are supported only for the local Rook cluster running in normal mode. The external mons will be ignored for external clusters and stretch clusters.
-
-Here is a step-by-step guide on how to add external monitors to a Rook cluster:
-
-1. Create a CephCluster CR with the `mon.externalMonIDs` field set to the external monitor IDs. For example:
-
-    ```yaml
-    spec:
-      mon:
-        # Spawn 2 Mons - one Mon in each AZ managed by Rook
-        count: 2
-        allowMultiplePerNode: false
-        # ID of external Mon
-        externalMonIDs:
-        - ext-mon-1 
-    ```
-
-    This will tell Rook to create two internal monitors in the cluster and to keep the external monitor with the ID `ext-mon-1` if it is found in the quorum.
-    It is also possible to add `externalMonIDs` to an existing Cluster.
-2. Wait until the Rook cluster and internal mons are up and running.
-3. Manually deploy an external monitor with the ID `ext-mon-1` outside of the Rook cluster and its availability zones. See [ceph guide](https://docs.ceph.com/en/latest/rados/operations/add-or-rm-mons/#adding-removing-monitors) on deploying monitors.
-4. Move the external Mon to [disallow-mode](https://docs.ceph.com/en/reef/rados/operations/change-mon-elections/#rados-operations-disallow-mode) to make sure that it won't be elected as a leader. The purpose of external mode is maintaining quorum to elect a new leader in case of a zone outage. The external Mon will likely have higher latency to the cluster so it should not be elected.
-5. Check that the external monitor is in the quorum by running `ceph status` or `ceph quorum_status` from the toolbox.
-6. Check that the external mon is added to the Rook mon endpoints:
-
-    ```console
-    $ kubectl -n rook-ceph get cm rook-ceph-mon-endpoints -o jsonpath='{.data.data}'
-    a=10.100.68.61:6789,b=10.103.201.172:6789,ext-mon-1=10.102.136.102:6789
-    ```
diff --git a/Documentation/Upgrade/node-maintenance.md b/Documentation/Upgrade/node-maintenance.md
deleted file mode 100644
index 5aa2d0646..000000000
--- a/Documentation/Upgrade/node-maintenance.md
+++ /dev/null
@@ -1,162 +0,0 @@
----
-title: Node Maintenance
----
-
-This guide outlines a comprehensive procedure for safely shutting down, maintaining, and restarting a nodes in the Rook Ceph cluster.
-
-Although updating nodes by failure domain (such as zones or racks) is preferred to minimize downtime, this guide describes the full shutdown process if downtime is acceptable.
-
-## Overview
-
-The basic maintenance procedure involves the following steps:
-
-1. Stop I/O: Scale down all applications to stop any I/O activity to Ceph.
-2. Set Ceph Flags: Optionally set the noout (and related) flags to prevent Ceph from marking OSDs as out.
-3. Shutdown Ceph Components: Scale down all Ceph deployments (with mons being the last) while ensuring the operator is scaled down first.
-4. Perform Maintenance: Conduct the necessary maintenance on your nodes.
-5. Restart Ceph Components: Scale up Ceph deployments (starting with mons) and then bring up the operator.
-6. Unset Ceph Flags: Remove any flags that were set before the shutdown.
-7. Restart Applications: Scale applications back up to resume operations.
-
-## Pre-Maintenance Steps
-
-Backup & Notifications: Always ensure you have a recent backup and inform stakeholders of the planned downtime.
-
-Update Strategy: Whenever possible, plan node updates by failure domain to avoid full cluster downtime.
-
-### Environment
-
-These instructions will work for as long the environment is parameterized correctly.
-Set the following environment variables, which will be used throughout this document.
-
-```console
-# Parameterize the environment
-export ROOK_OPERATOR_NAMESPACE=rook-ceph
-export ROOK_CLUSTER_NAMESPACE=rook-ceph
-```
-
-## Shutdown Procedure
-
-### **1. Scale Down Applications**
-
-Before any Ceph-specific actions, scale down all applications interacting with Ceph to ensure no new I/O is initiated.
-
-### **2. Set Ceph Flags**
-
-Setting the noout flag (and any additional flags) can help prevent Ceph from marking OSDs as out during the maintenance window.
-
-```console
-kubectl -n $ROOK_CLUSTER_NAMESPACE exec -it deploy/rook-ceph-tools -- bash
-ceph osd set noout
-# Optionally, set other flags as needed (e.g., norebalance, nodown)
-```
-
-!!! tip
-    The ordering of Ceph flags is not critical. You may set them in any order during shutdown and unset them in reverse order during startup.
-
-### **3. Scale Down the Operator**
-
-Scale down the rook-ceph-operator first to avoid it automatically restarting deployments you are scaling down.
-
-```console
-kubectl -n $ROOK_OPERATOR_NAMESPACE scale deployment rook-ceph-operator --replicas=0
-```
-
-### **4. Scale Down Ceph Deployments**
-
-Scale down the Ceph components in a controlled order. For example, you may choose the following order:
-
-```console
-RGW deployments > CSI CephFS plugin provisioners > CSI RBD plugin provisioners > OSD deployments > MON deployments > MGR deployments
-```
-
-A script to scale these down looks like:
-
-```console
-for _category in rook-ceph-rgw csi-cephfsplugin-provisioner csi-rbdplugin-provisioner rook-ceph-osd rook-ceph-mon rook-ceph-mgr rook-ceph-exporter rook-ceph-crashcollector; do
-    for _item in $(kubectl get deployment -n rook-ceph | awk '/^'"${_category}"'/{print $1}'); do
-        kubectl -n rook-ceph scale deployment ${_item} --replicas=0;
-        while [[ $(kubectl get deployment -n rook-ceph ${_item} -o jsonpath='{.status.readyReplicas}') != "" ]]; do
-            sleep 5;
-        done;
-    done;
-done
-```
-
-### **5. Node Maintenance**
-
-With all Ceph components safely scaled down, you can now perform your node maintenance tasks such as OS upgrades, hardware replacements, or other planned updates.
-
-## Startup Procedure
-
-### **1. Scale Up Ceph Components**
-
-Bring up the Ceph deployments in the following order to help ensure a healthy state before the operator takes over:
-
-Scale up MON deployments first:
-
-```console
-for _item in $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE | awk '/^rook-ceph-mon/{print $1}'); do
-    kubectl -n $ROOK_CLUSTER_NAMESPACE scale deployment ${_item} --replicas=1;
-    while [[ $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE ${_item} -o jsonpath='{.status.replicas}') != "1" ]]; do
-        sleep 5;
-    done;
-done
-```
-
-Then scale up OSD and MGR deployments:
-
-```console
-for _category in rook-ceph-mgr rook-ceph-osd; do
-    for _item in $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE | awk '/^'"${_category}"'/{print $1}'); do
-        kubectl -n $ROOK_CLUSTER_NAMESPACE scale deployment ${_item} --replicas=1;
-        while [[ $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE ${_item} -o jsonpath='{.status.replicas}') != "1" ]]; do
-            sleep 5;
-        done;
-    done;
-done
-```
-
-Finally, scale up other deployments:
-
-```console
-for _category in rook-ceph-exporter rook-ceph-crashcollector; do
-    for _item in $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE | awk '/^'"${_category}"'/{print $1}'); do
-        kubectl -n $ROOK_CLUSTER_NAMESPACE scale deployment ${_item} --replicas=1;
-        while [[ $(kubectl get deployment -n $ROOK_CLUSTER_NAMESPACE ${_item} -o jsonpath='{.status.replicas}') != "1" ]]; do
-            sleep 5;
-        done;
-    done;
-done
-```
-
-### **2. Scale Up the Operator**
-
-Once the Ceph components are stable and healthy, scale the operator back up to allow it to reconcile any remaining changes.
-
-```console
-kubectl -n $ROOK_OPERATOR_NAMESPACE scale deployment rook-ceph-operator --replicas=1
-```
-
-!!! tip
-    Bringing the operator up after the core components ensures a smoother reconciliation process.
-
-### **3. Unset Ceph Flags**
-
-After the Ceph cluster is back online and healthy, unset any flags that were previously set.
-
-```console
-kubectl -n $ROOK_CLUSTER_NAMESPACE exec -it deploy/rook-ceph-tools -- bash
-ceph osd unset noout
-# Unset any additional flags as necessary
-```
-
-### **4. Scale Up Applications**
-
-Finally, scale your applications back to their original replica count to resume normal operations.
-
-### CSI Plugins Behavior
-
-Observation: Even when the csi-cephfsplugin-provisioner and csi-rbdplugin-provisioner deployments are scaled to 0 replicas, the corresponding CSI plugin pods (from DaemonSets) remain running.
-
-Explanation: Scaling down the csi-cephfsplugin-provisioner and csi-rbdplugin-provisioner deployments prevents new provisioning requests, but the CSI plugin pods persist because they are managed by DaemonSets. Since DaemonSets are stateless, they can be deleted if needed, and the Rook operator will automatically recreate them when restarted. However, deletion is not required to stop provisioning.
diff --git a/Makefile b/Makefile
index f5eb211e6..da2bbbd2a 100644
--- a/Makefile
+++ b/Makefile
@@ -157,20 +157,12 @@ fmt: ## Check formatting of go sources.
 	@$(MAKE) go.init
 	@$(MAKE) go.fmt
 
-.PHONY: markdownlint
-markdownlint: ## Check formatting of documentation sources
-	markdownlint-cli2 "Documentation/**/**.md" "#Documentation/Helm-Charts/**" --config .markdownlint-cli2.cjs
-
 .PHONY: yamllint
 yamllint:
 	yamllint -c .yamllint deploy/examples/ --no-warnings
 
-.PHONY: golangci-lint
-golangci-lint:
-	go run github.com/golangci/golangci-lint/cmd/golangci-lint@latest run
-
 .PHONY: lint
-lint: yamllint pylint shellcheck vet markdownlint golangci-lint ## Run various linters
+lint: yamllint pylint shellcheck vet ## Run various linters
 
 .PHONY: pylint
 pylint:
diff --git a/PendingReleaseNotes.md b/PendingReleaseNotes.md
index faf45c2b6..252f70b28 100644
--- a/PendingReleaseNotes.md
+++ b/PendingReleaseNotes.md
@@ -13,5 +13,3 @@ Object:
     See https://github.com/rook/rook/pull/15376 for more information.
 
 ## Features
-
-- Support external mons for local Rook cluster (see [#14733](https://github.com/rook/rook/issues/14733)).
diff --git a/build/release/Makefile b/build/release/Makefile
index efb068736..e8b592306 100644
--- a/build/release/Makefile
+++ b/build/release/Makefile
@@ -66,10 +66,8 @@ FLAVORS ?= output
 override BRANCH_NAME := pr/$(BRANCH_NAME)
 endif
 
-DOCKER_REGISTRY ?= docker.io/rook
-QUAY_REGISTRY ?= quay.io/rook
-GHCR_REGISTRY ?= ghcr.io/rook
-REGISTRIES ?= $(DOCKER_REGISTRY) $(QUAY_REGISTRY) $(GHCR_REGISTRY)
+DOCKER_REGISTRY ?= rook
+REGISTRIES ?= $(DOCKER_REGISTRY)
 IMAGE_ARCHS := $(subst linux_,,$(filter linux_%,$(PLATFORMS)))
 IMAGE_PLATFORMS := $(subst _,/,$(subst $(SPACE),$(COMMA),$(filter linux_%,$(PLATFORMS))))
 IMAGE_PLATFORMS_COMMA := $(shell echo "$(IMAGE_PLATFORMS)" | sed 's/ /,/g' | sed 's/.$$//')
@@ -193,14 +191,10 @@ $(foreach r,$(REGISTRIES), $(foreach i,$(IMAGES), $(foreach a,$(IMAGE_ARCHS),$(e
 
 publish.manifest.image.%: publish.all.images $(MANIFEST_TOOL)
 	$(MANIFEST_TOOL) push from-args --platforms $(IMAGE_PLATFORMS_COMMA) --template $(DOCKER_REGISTRY)/$*-ARCH:$(VERSION) --target $(DOCKER_REGISTRY)/$*:$(VERSION)
-	$(MANIFEST_TOOL) push from-args --platforms $(IMAGE_PLATFORMS_COMMA) --template $(QUAY_REGISTRY)/$*-ARCH:$(VERSION) --target $(QUAY_REGISTRY)/$*:$(VERSION)
-	$(MANIFEST_TOOL) push from-args --platforms $(IMAGE_PLATFORMS_COMMA) --template $(GHCR_REGISTRY)/$*-ARCH:$(VERSION) --target $(GHCR_REGISTRY)/$*:$(VERSION)
 
 # add the "master" tag to the master image, but do not add the "release" tag for the release channel
 promote.manifest.image.%: promote.all.images $(MANIFEST_TOOL)
 	[ "$(CHANNEL)" = "release" ] || $(MANIFEST_TOOL) push from-args --platforms $(IMAGE_PLATFORMS_COMMA) --template $(DOCKER_REGISTRY)/$*-ARCH:$(VERSION) --target $(DOCKER_REGISTRY)/$*:$(CHANNEL)
-	[ "$(CHANNEL)" = "release" ] || $(MANIFEST_TOOL) push from-args --platforms $(IMAGE_PLATFORMS_COMMA) --template $(QUAY_REGISTRY)/$*-ARCH:$(VERSION) --target $(QUAY_REGISTRY)/$*:$(CHANNEL)
-	[ "$(CHANNEL)" = "release" ] || $(MANIFEST_TOOL) push from-args --platforms $(IMAGE_PLATFORMS_COMMA) --template $(GHCR_REGISTRY)/$*-ARCH:$(VERSION) --target $(GHCR_REGISTRY)/$*:$(CHANNEL)
 
 build.images: build.all.images
 publish.images: $(addprefix publish.manifest.image.,$(IMAGES))
diff --git a/cmd/rook/ceph/mgr.go b/cmd/rook/ceph/mgr.go
index 8b8834de1..372beb449 100644
--- a/cmd/rook/ceph/mgr.go
+++ b/cmd/rook/ceph/mgr.go
@@ -79,7 +79,7 @@ func runMgrSidecar(cmd *cobra.Command, args []string) error {
 	}
 
 	context := createContext()
-	clusterInfo.InternalMonitors = opcontroller.ParseMonEndpoints(cfg.monEndpoints)
+	clusterInfo.Monitors = opcontroller.ParseMonEndpoints(cfg.monEndpoints)
 	rook.LogStartupInfo(mgrSidecarCmd.Flags())
 
 	ownerRef := opcontroller.ClusterOwnerRef(clusterName, ownerRefID)
diff --git a/cmd/rook/ceph/osd.go b/cmd/rook/ceph/osd.go
index c231c4b95..d4f0caf5f 100644
--- a/cmd/rook/ceph/osd.go
+++ b/cmd/rook/ceph/osd.go
@@ -344,7 +344,7 @@ func commonOSDInit(cmd *cobra.Command) {
 	rook.SetLogLevel()
 	rook.LogStartupInfo(cmd.Flags())
 
-	clusterInfo.InternalMonitors = opcontroller.ParseMonEndpoints(cfg.monEndpoints)
+	clusterInfo.Monitors = opcontroller.ParseMonEndpoints(cfg.monEndpoints)
 }
 
 // use zone/region/hostname labels in the crushmap
diff --git a/deploy/charts/rook-ceph-cluster/templates/ingress.yaml b/deploy/charts/rook-ceph-cluster/templates/ingress.yaml
index f915575f4..6d86c5f1d 100644
--- a/deploy/charts/rook-ceph-cluster/templates/ingress.yaml
+++ b/deploy/charts/rook-ceph-cluster/templates/ingress.yaml
@@ -10,7 +10,7 @@ metadata:
   {{- end }}
 spec:
   rules:
-    - host: {{ .Values.ingress.dashboard.host.name | quote }}
+    - host: {{ .Values.ingress.dashboard.host.name }}
       http:
         paths:
           - path: {{ .Values.ingress.dashboard.host.path | default "/" }}
diff --git a/deploy/charts/rook-ceph/templates/configmap.yaml b/deploy/charts/rook-ceph/templates/configmap.yaml
index 03e79640f..576764acc 100644
--- a/deploy/charts/rook-ceph/templates/configmap.yaml
+++ b/deploy/charts/rook-ceph/templates/configmap.yaml
@@ -208,12 +208,6 @@ data:
 {{- if .Values.csi.csiAddonsPort }}
   CSIADDONS_PORT: {{ .Values.csi.csiAddonsPort | quote }}
 {{- end }}
-{{- if .Values.csi.csiAddonsRBDProvisionerPort }}
-  CSIADDONS_RBD_PROVISIONER_PORT: {{ .Values.csi.csiAddonsRBDProvisionerPort | quote }}
-{{- end }}
-{{- if .Values.csi.csiAddonsCephFSProvisionerPort }}
-  CSIADDONS_CEPHFS_PROVISIONER_PORT: {{ .Values.csi.csiAddonsCephFSProvisionerPort | quote }}
-{{- end }}
 {{- if .Values.csi.forceCephFSKernelClient }}
   CSI_FORCE_CEPHFS_KERNEL_CLIENT: {{ .Values.csi.forceCephFSKernelClient | quote }}
 {{- end }}
diff --git a/deploy/charts/rook-ceph/templates/resources.yaml b/deploy/charts/rook-ceph/templates/resources.yaml
index d09b83f03..53a806f02 100644
--- a/deploy/charts/rook-ceph/templates/resources.yaml
+++ b/deploy/charts/rook-ceph/templates/resources.yaml
@@ -1789,16 +1789,6 @@ spec:
                       maximum: 9
                       minimum: 0
                       type: integer
-                    externalMonIDs:
-                      description: |-
-                        ExternalMonIDs - optional list of monitor IDs which are deployed externally and not managed by Rook.
-                        If set, Rook will not remove mons with given IDs from quorum.
-                        This parameter is used only for local Rook cluster running in normal mode
-                        and will be ignored if external or stretched mode is used.
-                        leading
-                      items:
-                        type: string
-                      type: array
                     failureDomainLabel:
                       type: string
                     stretchCluster:
@@ -11963,36 +11953,6 @@ spec:
                         applied. Use with caution.
                       nullable: true
                       type: object
-                    rgwConfigFromSecret:
-                      additionalProperties:
-                        description: SecretKeySelector selects a key of a Secret.
-                        properties:
-                          key:
-                            description: The key of the secret to select from.  Must be a valid secret key.
-                            type: string
-                          name:
-                            default: ""
-                            description: |-
-                              Name of the referent.
-                              This field is effectively required, but due to backwards compatibility is
-                              allowed to be empty. Instances of this type with an empty value here are
-                              almost certainly wrong.
-                              More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
-                            type: string
-                          optional:
-                            description: Specify whether the Secret or its key must be defined
-                            type: boolean
-                        required:
-                          - key
-                        type: object
-                        x-kubernetes-map-type: atomic
-                      description: |-
-                        RgwConfigFromSecret works exactly like RgwConfig but takes config value from Secret Key reference.
-                        Values are modified at runtime without RGW restart.
-                        This feature is intended for advanced users. It allows breaking configurations to be easily
-                        applied. Use with caution.
-                      nullable: true
-                      type: object
                     securePort:
                       description: The port the rgw service will be listening on (https)
                       format: int32
diff --git a/deploy/charts/rook-ceph/values.yaml b/deploy/charts/rook-ceph/values.yaml
index 4ce764357..16efe55a2 100644
--- a/deploy/charts/rook-ceph/values.yaml
+++ b/deploy/charts/rook-ceph/values.yaml
@@ -443,12 +443,6 @@ csi:
   # -- CSI Addons server port
   # @default -- `9070`
   csiAddonsPort:
-  # -- CSI Addons server port for the RBD provisioner
-  # @default -- `9070`
-  csiAddonsRBDProvisionerPort:
-  # -- CSI Addons server port for the Ceph FS provisioner
-  # @default -- `9070`
-  csiAddonsCephFSProvisionerPort:
 
   # -- Enable Ceph Kernel clients on kernel < 4.17. If your kernel does not support quotas for CephFS
   # you may want to disable this setting. However, this will cause an issue during upgrades
diff --git a/deploy/examples/crds.yaml b/deploy/examples/crds.yaml
index 3f206e8b2..25b576af4 100644
--- a/deploy/examples/crds.yaml
+++ b/deploy/examples/crds.yaml
@@ -1787,16 +1787,6 @@ spec:
                       maximum: 9
                       minimum: 0
                       type: integer
-                    externalMonIDs:
-                      description: |-
-                        ExternalMonIDs - optional list of monitor IDs which are deployed externally and not managed by Rook.
-                        If set, Rook will not remove mons with given IDs from quorum.
-                        This parameter is used only for local Rook cluster running in normal mode
-                        and will be ignored if external or stretched mode is used.
-                        leading
-                      items:
-                        type: string
-                      type: array
                     failureDomainLabel:
                       type: string
                     stretchCluster:
@@ -11954,36 +11944,6 @@ spec:
                         applied. Use with caution.
                       nullable: true
                       type: object
-                    rgwConfigFromSecret:
-                      additionalProperties:
-                        description: SecretKeySelector selects a key of a Secret.
-                        properties:
-                          key:
-                            description: The key of the secret to select from.  Must be a valid secret key.
-                            type: string
-                          name:
-                            default: ""
-                            description: |-
-                              Name of the referent.
-                              This field is effectively required, but due to backwards compatibility is
-                              allowed to be empty. Instances of this type with an empty value here are
-                              almost certainly wrong.
-                              More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
-                            type: string
-                          optional:
-                            description: Specify whether the Secret or its key must be defined
-                            type: boolean
-                        required:
-                          - key
-                        type: object
-                        x-kubernetes-map-type: atomic
-                      description: |-
-                        RgwConfigFromSecret works exactly like RgwConfig but takes config value from Secret Key reference.
-                        Values are modified at runtime without RGW restart.
-                        This feature is intended for advanced users. It allows breaking configurations to be easily
-                        applied. Use with caution.
-                      nullable: true
-                      type: object
                     securePort:
                       description: The port the rgw service will be listening on (https)
                       format: int32
diff --git a/deploy/examples/create-external-cluster-resources.py b/deploy/examples/create-external-cluster-resources.py
index a2f84eb50..38a567d8d 100644
--- a/deploy/examples/create-external-cluster-resources.py
+++ b/deploy/examples/create-external-cluster-resources.py
@@ -5,7 +5,7 @@ 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
+	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,
diff --git a/deploy/examples/operator.yaml b/deploy/examples/operator.yaml
index b1fcd62b3..5af25f6da 100644
--- a/deploy/examples/operator.yaml
+++ b/deploy/examples/operator.yaml
@@ -470,12 +470,7 @@ data:
   # CSI_CEPHFS_LIVENESS_METRICS_PORT: "9081"
   # Configure CSI RBD liveness metrics port
   # CSI_RBD_LIVENESS_METRICS_PORT: "9080"
-
-  # We can override the ports for each individual component that uses the CSIADDONS sidecar
-  # This is useful if we're running in hostNetwork, where ports may conflict on the same host
   # CSIADDONS_PORT: "9070"
-  # CSIADDONS_RBD_PROVISIONER_PORT: "9070"
-  # CSIADDONS_CEPHFS_PROVISIONER_PORT: "9070"
 
   # Set CephFS Kernel mount options to use https://docs.ceph.com/en/latest/man/8/mount.ceph/#options
   # Set to "ms_mode=secure" when connections.encrypted is enabled in CephCluster CR
@@ -577,9 +572,6 @@ data:
 
   # RevisionHistoryLimit value for all deployments created by rook.
   # ROOK_REVISION_HISTORY_LIMIT: "3"
-
-  #  Custom label to identify node hostname. If not set `kubernetes.io/hostname` will be used
-  ROOK_CUSTOM_HOSTNAME_LABEL: ""
 ---
 # OLM: BEGIN OPERATOR DEPLOYMENT
 apiVersion: apps/v1
@@ -660,6 +652,10 @@ spec:
             - name: ROOK_UNREACHABLE_NODE_TOLERATION_SECONDS
               value: "5"
 
+            #  Custom label to identify node hostname. If not set `kubernetes.io/hostname` will be used
+            - name: ROOK_CUSTOM_HOSTNAME_LABEL
+              value: ""
+
             # The name of the node to pass with the downward API
             - name: NODE_NAME
               valueFrom:
diff --git a/go.mod b/go.mod
index 9f0e2076b..bb6436432 100644
--- a/go.mod
+++ b/go.mod
@@ -20,23 +20,23 @@ require (
 	github.com/banzaicloud/k8s-objectmatcher v1.8.0
 	github.com/ceph/ceph-csi-operator/api v0.0.0-20241211130321-1c8ad98a7d6e
 	github.com/ceph/ceph-csi/api v0.0.0-20241216133622-88b7e0d6684f
-	github.com/ceph/go-ceph v0.32.0
+	github.com/ceph/go-ceph v0.31.0
 	github.com/coreos/pkg v0.0.0-20240122114842-bbd7aa9bf6fb
 	github.com/csi-addons/kubernetes-csi-addons v0.11.0
 	github.com/gemalto/kmip-go v0.0.10
 	github.com/go-ini/ini v1.67.0
-	github.com/google/go-cmp v0.7.0
+	github.com/google/go-cmp v0.6.0
 	github.com/google/uuid v1.6.0
-	github.com/hashicorp/vault/api v1.16.0
+	github.com/hashicorp/vault/api v1.15.0
 	github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.5
 	github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20221122204822-d1a8c34382f1
 	github.com/libopenstorage/secrets v0.0.0-20240416031220-a17cf7f72c6c
 	github.com/pkg/errors v0.9.1
-	github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.1
-	github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.1
+	github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.0
+	github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.0
 	github.com/rook/rook/pkg/apis v0.0.0-20241216163035-3170ac6a0c58
 	github.com/sethvargo/go-password v0.3.1
-	github.com/spf13/cobra v1.9.1
+	github.com/spf13/cobra v1.8.1
 	github.com/spf13/pflag v1.0.6
 	github.com/stretchr/testify v1.10.0
 	github.com/sykesm/zap-logfmt v0.0.4
@@ -68,9 +68,6 @@ require (
 	github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
 	github.com/ansel1/merry v1.8.0 // indirect
 	github.com/ansel1/merry/v2 v2.2.1 // indirect
-	github.com/aws/aws-sdk-go-v2 v1.36.0 // indirect
-	github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect
-	github.com/aws/smithy-go v1.22.2 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/blang/semver/v4 v4.0.0 // indirect
 	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
@@ -85,7 +82,7 @@ require (
 	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
 	github.com/gemalto/flume v0.13.1 // indirect
 	github.com/go-errors/errors v1.5.1 // indirect
-	github.com/go-jose/go-jose/v4 v4.0.5 // indirect
+	github.com/go-jose/go-jose/v4 v4.0.4 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/zapr v1.3.0 // indirect
 	github.com/go-openapi/jsonpointer v0.21.0 // indirect
diff --git a/go.sum b/go.sum
index f92a4e581..52bba797f 100644
--- a/go.sum
+++ b/go.sum
@@ -148,42 +148,6 @@ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:l
 github.com/aws/aws-sdk-go v1.44.164/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
 github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
 github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
-github.com/aws/aws-sdk-go-v2 v1.36.0 h1:b1wM5CcE65Ujwn565qcwgtOTT1aT4ADOHHgglKjG7fk=
-github.com/aws/aws-sdk-go-v2 v1.36.0/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc=
-github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg=
-github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVTsRbsT1Lra46Hv9ME=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7/go.mod h1:lvpyBGkZ3tZ9iSsUIcC2EWp+0ywa7aK3BLT+FwZi+mQ=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0 h1:SAfh4pNx5LuTafKKWR02Y+hL3A+3TX8cTKG1OIAJaBk=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w=
-github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
-github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
 github.com/banzaicloud/k8s-objectmatcher v1.8.0 h1:Nugn25elKtPMTA2br+JgHNeSQ04sc05MDPmpJnd1N2A=
 github.com/banzaicloud/k8s-objectmatcher v1.8.0/go.mod h1:p2LSNAjlECf07fbhDyebTkPUIYnU05G+WfGgkTmgeMg=
 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@@ -207,8 +171,8 @@ github.com/ceph/ceph-csi-operator/api v0.0.0-20241211130321-1c8ad98a7d6e h1:Qoca
 github.com/ceph/ceph-csi-operator/api v0.0.0-20241211130321-1c8ad98a7d6e/go.mod h1:odEUoarG26wXBCC2l4O4nMWhAz6VTKr2FRkv9yELgi8=
 github.com/ceph/ceph-csi/api v0.0.0-20241216133622-88b7e0d6684f h1:ja5xvJlv3Sq5dlQVQMeM/dlCwcUXgZXg/kTPmMMQhJ8=
 github.com/ceph/ceph-csi/api v0.0.0-20241216133622-88b7e0d6684f/go.mod h1:NfzKbdhaXhWh1PJCExML9dAXHCqEDppf26ueLWYdxlk=
-github.com/ceph/go-ceph v0.32.0 h1:iXRUGdPmH7h9Vf/WA1Dg3Wo1tgL7gcUbylfpbxrlGLs=
-github.com/ceph/go-ceph v0.32.0/go.mod h1:42eoJzyLS3VREzqrg2ot44NtuluQZi55hFRSoLF36GQ=
+github.com/ceph/go-ceph v0.31.0 h1:3yqMVsRN89VyMhJ+qiF91mQHw4RBjbWVVERGX7bYxkk=
+github.com/ceph/go-ceph v0.31.0/go.mod h1:VnMYp08oE2o2of29p/oGkxnM8VNF/+LJyVQ5pZSj3Wg=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
@@ -247,7 +211,7 @@ github.com/coreos/pkg v0.0.0-20240122114842-bbd7aa9bf6fb h1:GIzvVQ9UkUlOhSDlqmrQ
 github.com/coreos/pkg v0.0.0-20240122114842-bbd7aa9bf6fb/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
@@ -330,8 +294,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
 github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
 github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
 github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
-github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
-github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
+github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
+github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -477,8 +441,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
-github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -594,8 +558,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
 github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
-github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4=
-github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA=
+github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA=
+github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8=
 github.com/hashicorp/vault/api/auth/approle v0.5.0/go.mod h1:CHOQIA1AZACfjTzHggmyfiOZ+xCSKNRFqe48FTCzH0k=
 github.com/hashicorp/vault/api/auth/approle v0.8.0 h1:FuVtWZ0xD6+wz1x0l5s0b4852RmVXQNEiKhVXt6lfQY=
 github.com/hashicorp/vault/api/auth/approle v0.8.0/go.mod h1:NV7O9r5JUtNdVnqVZeMHva81AIdpG0WoIQohNt1VCPM=
@@ -819,11 +783,11 @@ github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4
 github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.44.1/go.mod h1:3WYi4xqXxGGXWDdQIITnLNmuDzO5n6wYva9spVhR4fg=
 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.46.0/go.mod h1:3WYi4xqXxGGXWDdQIITnLNmuDzO5n6wYva9spVhR4fg=
-github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.1 h1:DP+PUNVOc+Bkft8a4QunLzaZ0RspWuD3tBbcPHr2PeE=
-github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.1/go.mod h1:6x4x0t9BP35g4XcjkHE9EB3RxhyfxpdpmZKd/Qyk8+M=
+github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.0 h1:ckSycH7xHtpcvXsmEY/qEziRhDQKqKqbsHi9kX/BO7A=
+github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.0/go.mod h1:6x4x0t9BP35g4XcjkHE9EB3RxhyfxpdpmZKd/Qyk8+M=
 github.com/prometheus-operator/prometheus-operator/pkg/client v0.46.0/go.mod h1:k4BrWlVQQsvBiTcDnKEMgyh/euRxyxgrHdur/ZX/sdA=
-github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.1 h1:7/TPlTy7tkMXC1KwS2WdUdU8J220DszlSQ1+KHjXwHc=
-github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.1/go.mod h1:0lW4yp2XE7AKohpGB2UGdomjP08rbr4f8JwAN6sUDWU=
+github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.0 h1:TvrTp81Ra7c3n+P4tArjx/st9x92q1BlxfjhqeP3aSA=
+github.com/prometheus-operator/prometheus-operator/pkg/client v0.80.0/go.mod h1:G2GGhVurNB5qQh7DZVSAtdN4Szvp6IxWhc1+n2rclw4=
 github.com/prometheus/client_golang v0.9.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
@@ -897,8 +861,8 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
 github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
+github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
+github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
diff --git a/pkg/apis/ceph.rook.io/v1/types.go b/pkg/apis/ceph.rook.io/v1/types.go
index 538526634..de8c70cce 100755
--- a/pkg/apis/ceph.rook.io/v1/types.go
+++ b/pkg/apis/ceph.rook.io/v1/types.go
@@ -626,13 +626,6 @@ type MonSpec struct {
 	// +kubebuilder:pruning:PreserveUnknownFields
 	// +optional
 	VolumeClaimTemplate *VolumeClaimTemplate `json:"volumeClaimTemplate,omitempty"`
-	// ExternalMonIDs - optional list of monitor IDs which are deployed externally and not managed by Rook.
-	// If set, Rook will not remove mons with given IDs from quorum.
-	// This parameter is used only for local Rook cluster running in normal mode
-	// and will be ignored if external or stretched mode is used.
-	// leading
-	// +optional
-	ExternalMonIDs []string `json:"externalMonIDs,omitempty"`
 }
 
 // VolumeClaimTemplate is a simplified version of K8s corev1's PVC. It has no type meta or status.
@@ -1755,14 +1748,6 @@ type GatewaySpec struct {
 	// +optional
 	RgwConfig map[string]string `json:"rgwConfig,omitempty"`
 
-	// RgwConfigFromSecret works exactly like RgwConfig but takes config value from Secret Key reference.
-	// Values are modified at runtime without RGW restart.
-	// This feature is intended for advanced users. It allows breaking configurations to be easily
-	// applied. Use with caution.
-	// +nullable
-	// +optional
-	RgwConfigFromSecret map[string]v1.SecretKeySelector `json:"rgwConfigFromSecret,omitempty"`
-
 	// RgwCommandFlags sets Ceph RGW config values for the gateway clients that serve this object
 	// store. Values are modified at RGW startup, resulting in RGW pod restarts.
 	// This feature is intended for advanced users. It allows breaking configurations to be easily
diff --git a/pkg/apis/ceph.rook.io/v1/zz_generated.deepcopy.go b/pkg/apis/ceph.rook.io/v1/zz_generated.deepcopy.go
index b3ef8a9a4..90d36b973 100644
--- a/pkg/apis/ceph.rook.io/v1/zz_generated.deepcopy.go
+++ b/pkg/apis/ceph.rook.io/v1/zz_generated.deepcopy.go
@@ -2743,13 +2743,6 @@ func (in *GatewaySpec) DeepCopyInto(out *GatewaySpec) {
 			(*out)[key] = val
 		}
 	}
-	if in.RgwConfigFromSecret != nil {
-		in, out := &in.RgwConfigFromSecret, &out.RgwConfigFromSecret
-		*out = make(map[string]corev1.SecretKeySelector, len(*in))
-		for key, val := range *in {
-			(*out)[key] = *val.DeepCopy()
-		}
-	}
 	if in.RgwCommandFlags != nil {
 		in, out := &in.RgwCommandFlags, &out.RgwCommandFlags
 		*out = make(map[string]string, len(*in))
@@ -3353,11 +3346,6 @@ func (in *MonSpec) DeepCopyInto(out *MonSpec) {
 		*out = new(VolumeClaimTemplate)
 		(*in).DeepCopyInto(*out)
 	}
-	if in.ExternalMonIDs != nil {
-		in, out := &in.ExternalMonIDs, &out.ExternalMonIDs
-		*out = make([]string, len(*in))
-		copy(*out, *in)
-	}
 	return
 }
 
diff --git a/pkg/apis/go.mod b/pkg/apis/go.mod
index 93888c7e0..d8a915ccc 100644
--- a/pkg/apis/go.mod
+++ b/pkg/apis/go.mod
@@ -15,7 +15,7 @@ replace (
 )
 
 require (
-	github.com/hashicorp/vault/api v1.16.0
+	github.com/hashicorp/vault/api v1.15.0
 	github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.5
 	github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20221122204822-d1a8c34382f1
 	github.com/libopenstorage/secrets v0.0.0-20240416031220-a17cf7f72c6c
@@ -33,7 +33,7 @@ require (
 	github.com/emicklei/go-restful/v3 v3.12.1 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
 	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
-	github.com/go-jose/go-jose/v4 v4.0.5 // indirect
+	github.com/go-jose/go-jose/v4 v4.0.4 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-openapi/jsonpointer v0.21.0 // indirect
 	github.com/go-openapi/jsonreference v0.21.0 // indirect
@@ -41,7 +41,7 @@ require (
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/google/gnostic-models v0.6.9 // indirect
-	github.com/google/go-cmp v0.7.0 // indirect
+	github.com/google/go-cmp v0.6.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
diff --git a/pkg/apis/go.sum b/pkg/apis/go.sum
index a9c792c87..4f039e0ce 100644
--- a/pkg/apis/go.sum
+++ b/pkg/apis/go.sum
@@ -219,8 +219,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
-github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
-github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
+github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
+github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -359,8 +359,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
-github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -468,8 +468,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
 github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
-github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4=
-github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA=
+github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA=
+github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8=
 github.com/hashicorp/vault/api/auth/approle v0.5.0/go.mod h1:CHOQIA1AZACfjTzHggmyfiOZ+xCSKNRFqe48FTCzH0k=
 github.com/hashicorp/vault/api/auth/approle v0.8.0 h1:FuVtWZ0xD6+wz1x0l5s0b4852RmVXQNEiKhVXt6lfQY=
 github.com/hashicorp/vault/api/auth/approle v0.8.0/go.mod h1:NV7O9r5JUtNdVnqVZeMHva81AIdpG0WoIQohNt1VCPM=
diff --git a/pkg/daemon/ceph/client/config.go b/pkg/daemon/ceph/client/config.go
index 278c37445..f2cc6119b 100644
--- a/pkg/daemon/ceph/client/config.go
+++ b/pkg/daemon/ceph/client/config.go
@@ -249,7 +249,7 @@ func PopulateMonHostMembers(clusterInfo *ClusterInfo) ([]string, []string) {
 	var monMembers []string
 	var monHosts []string
 
-	for _, monitor := range clusterInfo.AllMonitors() {
+	for _, monitor := range clusterInfo.Monitors {
 		if monitor.OutOfQuorum {
 			logger.Warningf("skipping adding mon %q to config file, detected out of quorum", monitor.Name)
 			continue
diff --git a/pkg/daemon/ceph/client/info.go b/pkg/daemon/ceph/client/info.go
index 79d273471..70e49bc48 100644
--- a/pkg/daemon/ceph/client/info.go
+++ b/pkg/daemon/ceph/client/info.go
@@ -39,13 +39,10 @@ type ClusterInfo struct {
 	FSID          string
 	MonitorSecret string
 	CephCred      CephCred
-	// InternalMonitors - montiros managed by Rook or external monitors when Rook manages external cluster.
-	InternalMonitors map[string]*MonInfo
-	// ExternalMons - external montiros listed in CephCluster.spec.mon.externalMonIDs when Rook managing local cluster.
-	ExternalMons map[string]*MonInfo
-	CephVersion  cephver.CephVersion
-	Namespace    string
-	OwnerInfo    *k8sutil.OwnerInfo
+	Monitors      map[string]*MonInfo
+	CephVersion   cephver.CephVersion
+	Namespace     string
+	OwnerInfo     *k8sutil.OwnerInfo
 	// Hide the name of the cluster since in 99% of uses we want to use the cluster namespace.
 	// If the CR name is needed, access it through the NamespacedName() method.
 	name              string
@@ -62,20 +59,6 @@ type ClusterInfo struct {
 	Context context.Context
 }
 
-func (c *ClusterInfo) AllMonitors() map[string]*MonInfo {
-	if len(c.ExternalMons) == 0 {
-		return c.InternalMonitors
-	}
-	res := make(map[string]*MonInfo, len(c.InternalMonitors)+len(c.ExternalMons))
-	for id, mon := range c.InternalMonitors {
-		res[id] = mon
-	}
-	for id, mon := range c.ExternalMons {
-		res[id] = mon
-	}
-	return res
-}
-
 // MonInfo is a collection of information about a Ceph mon.
 type MonInfo struct {
 	Name     string `json:"name"`
diff --git a/pkg/daemon/ceph/client/mirror.go b/pkg/daemon/ceph/client/mirror.go
index a0795c892..d9c2dc8a8 100644
--- a/pkg/daemon/ceph/client/mirror.go
+++ b/pkg/daemon/ceph/client/mirror.go
@@ -441,7 +441,7 @@ func CreateRBDMirrorBootstrapPeerWithoutPool(context *clusterd.Context, clusterI
 	logger.Infof("successfully created rbd-mirror bootstrap peer token for cluster %q", clusterInfo.NamespacedName().Name)
 
 	mons := sets.New[string]()
-	for _, mon := range clusterInfo.AllMonitors() {
+	for _, mon := range clusterInfo.Monitors {
 		mons.Insert(mon.Endpoint)
 	}
 
diff --git a/pkg/operator/ceph/cluster/cluster_external.go b/pkg/operator/ceph/cluster/cluster_external.go
index b9b14121b..37f825263 100644
--- a/pkg/operator/ceph/cluster/cluster_external.go
+++ b/pkg/operator/ceph/cluster/cluster_external.go
@@ -109,7 +109,7 @@ func (c *ClusterController) configureExternalCephCluster(cluster *cluster) error
 	}
 
 	// update the msgr2 flag
-	for _, m := range cluster.ClusterInfo.InternalMonitors {
+	for _, m := range cluster.ClusterInfo.Monitors {
 		// m.Endpoint=10.1.115.104:3300
 		monPort := util.GetPortFromEndpoint(m.Endpoint)
 		if monPort == client.Msgr2port {
@@ -123,7 +123,7 @@ func (c *ClusterController) configureExternalCephCluster(cluster *cluster) error
 	}
 
 	// Save CSI configmap
-	monEndpoints := csi.MonEndpoints(cluster.ClusterInfo.InternalMonitors, cluster.Spec.RequireMsgr2())
+	monEndpoints := csi.MonEndpoints(cluster.ClusterInfo.Monitors, cluster.Spec.RequireMsgr2())
 	csiConfigEntry := &csi.CSIClusterConfigEntry{
 		Namespace: cluster.ClusterInfo.Namespace,
 		ClusterInfo: cephcsi.ClusterInfo{
diff --git a/pkg/operator/ceph/cluster/controller.go b/pkg/operator/ceph/cluster/controller.go
index f499cccce..11eecc632 100644
--- a/pkg/operator/ceph/cluster/controller.go
+++ b/pkg/operator/ceph/cluster/controller.go
@@ -187,7 +187,10 @@ func add(opManagerContext context.Context, mgr manager.Manager, r reconcile.Reco
 
 	// Watch for changes on the hotplug config map
 	// TODO: to improve, can we run this against the operator namespace only?
-	disableVal := k8sutil.GetOperatorSetting(disableHotplugEnv, "false")
+	disableVal, err := k8sutil.GetOperatorSetting(opManagerContext, context.Clientset, opcontroller.OperatorSettingConfigMapName, disableHotplugEnv, "false")
+	if err != nil {
+		logger.Errorf("failed to get %s setting %s. %v", disableHotplugEnv, opcontroller.OperatorSettingConfigMapName, err)
+	}
 	if disableVal != "true" {
 		logger.Info("enabling hotplug orchestration")
 		s := source.Kind[client.Object](
diff --git a/pkg/operator/ceph/cluster/mon/health.go b/pkg/operator/ceph/cluster/mon/health.go
index fe1042ae8..050d88f43 100644
--- a/pkg/operator/ceph/cluster/mon/health.go
+++ b/pkg/operator/ceph/cluster/mon/health.go
@@ -1,4 +1,5 @@
-/* Copyright 2018 The Rook Authors. All rights reserved.
+/*
+Copyright 2018 The Rook Authors. All rights reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -19,7 +20,6 @@ import (
 	"context"
 	"fmt"
 	"os"
-	"slices"
 	"strings"
 	"time"
 
@@ -202,12 +202,6 @@ func (c *Cluster) checkHealth(ctx context.Context) error {
 	}
 	logger.Debugf("Mon quorum status: %+v", quorumStatus)
 
-	// handle external Mons
-	quorumStatus, err = c.reconcileExternalMons(ctx, quorumStatus)
-	if err != nil {
-		return errors.Wrap(err, "failed to check external mons health")
-	}
-
 	// Use a local mon count in case the user updates the crd in another goroutine.
 	// We need to complete a health check with a consistent value.
 	desiredMonCount := c.spec.Mon.Count
@@ -215,7 +209,7 @@ func (c *Cluster) checkHealth(ctx context.Context) error {
 
 	// Source of truth of which mons should exist is our *clusterInfo*
 	monsNotFound := map[string]interface{}{}
-	for _, mon := range c.ClusterInfo.InternalMonitors {
+	for _, mon := range c.ClusterInfo.Monitors {
 		monsNotFound[mon.Name] = struct{}{}
 	}
 
@@ -320,7 +314,7 @@ func (c *Cluster) checkHealth(ctx context.Context) error {
 	// handle all mons that haven't been in the Ceph mon map
 	for mon := range monsNotFound {
 		logger.Warningf("mon %s NOT found in ceph mon map, failover", mon)
-		c.failMon(len(c.ClusterInfo.InternalMonitors), desiredMonCount, mon)
+		c.failMon(len(c.ClusterInfo.Monitors), desiredMonCount, mon)
 		// only deal with one "not found in ceph mon map" mon per health check
 		return nil
 	}
@@ -357,24 +351,24 @@ func (c *Cluster) checkHealth(ctx context.Context) error {
 
 	// failover mon if `multiClusterService` is enabled but mon service is not exported
 	if allMonsInQuorum && c.spec.Network.MultiClusterService.Enabled {
-		for _, mon := range c.ClusterInfo.InternalMonitors {
+		for _, mon := range c.ClusterInfo.Monitors {
 			monResourceName := resourceName(mon.Name)
 			isAlreadyExported, err := k8sutil.IsServiceExported(c.ClusterInfo.Context, c.context, monResourceName, c.ClusterInfo.Namespace)
 			if err != nil {
 				return errors.Wrapf(err, "failed to check if the service %q is already exported", mon.Name)
 			}
 			if !isAlreadyExported {
-				c.failMon(len(c.ClusterInfo.InternalMonitors), desiredMonCount, mon.Name)
+				c.failMon(len(c.ClusterInfo.Monitors), desiredMonCount, mon.Name)
 				return nil
 			}
 		}
 	}
 
 	// failover any mons present in the mon fail over list
-	for _, mon := range c.ClusterInfo.InternalMonitors {
+	for _, mon := range c.ClusterInfo.Monitors {
 		if c.monsToFailover.Has(mon.Name) {
 			logger.Infof("fail over mon %q from the mon fail over list", mon.Name)
-			c.failMon(len(c.ClusterInfo.InternalMonitors), desiredMonCount, mon.Name)
+			c.failMon(len(c.ClusterInfo.Monitors), desiredMonCount, mon.Name)
 			c.monsToFailover.Delete(mon.Name)
 			return nil
 		}
@@ -383,102 +377,12 @@ func (c *Cluster) checkHealth(ctx context.Context) error {
 	return nil
 }
 
-// reconcileExternalMons handling external monitors defined in CephCluster.spec.mon.externalMonIDs when Rook managing local cluster.
-func (c *Cluster) reconcileExternalMons(ctx context.Context, quorumStatus cephclient.MonStatusResponse) (cephclient.MonStatusResponse, error) {
-	if len(c.spec.Mon.ExternalMonIDs) != 0 {
-		if c.spec.External.Enable {
-			logger.Warning("ignore external mon IDs in cluster spec: cluster is running in external mode")
-			return quorumStatus, nil
-		}
-		if c.spec.IsStretchCluster() {
-			logger.Warning("ignore external mon IDs in cluster spec: cluster is running in stretch mode")
-			return quorumStatus, nil
-		}
-	}
-
-	extMonIDs := c.spec.Mon.ExternalMonIDs
-	if c.ClusterInfo.ExternalMons == nil {
-		c.ClusterInfo.ExternalMons = make(map[string]*cephclient.MonInfo, len(extMonIDs))
-	}
-
-	extMonsChanged := false
-	for extID := range c.ClusterInfo.ExternalMons {
-		if slices.Contains(extMonIDs, extID) {
-			continue
-		}
-		// existing external mon was removed from Cluster CRD spec:
-		// remove it from CLusterInfo
-		logger.Debugf("existing external mon %q was removed from spec: removing it", extID)
-		delete(c.ClusterInfo.ExternalMons, extID)
-		extMonsChanged = true
-	}
-
-	// handle external monitors if configured in cluster CRD:
-	logger.Debugf("external mon IDs: %v", extMonIDs)
-	for _, extID := range extMonIDs {
-		monStatus, inQuorum := getMonByID(extID, quorumStatus)
-		if inQuorum {
-			logger.Debugf("external mon %q in quorum", extID)
-		} else {
-			logger.Debugf("external mon %q not in quorum %+v, %+v", extID, quorumStatus.Quorum, quorumStatus.MonMap.Mons)
-		}
-		_, inInfo := c.ClusterInfo.ExternalMons[extID]
-		if inQuorum && !inInfo {
-			// add newly discovered external mon to cluster info:
-			monInfo := monStatusToInfo(monStatus)
-			c.ClusterInfo.ExternalMons[extID] = monInfo
-			extMonsChanged = true
-			logger.Infof("new external mon %q found: %s, adding it", extID, monInfo.Endpoint)
-		} else if !inQuorum && inInfo {
-			// remove external mon from cluster info if it is out of quorum:
-			delete(c.ClusterInfo.ExternalMons, extID)
-			extMonsChanged = true
-			logger.Infof("new external mon %q not in quorum: removing it", extID)
-		}
-	}
-	if extMonsChanged {
-		// update config if external mon was removed or added:
-		if err := c.saveMonConfig(); err != nil {
-			return cephclient.MonStatusResponse{}, errors.Wrap(err, "failed to save mon config after adding/removing external mon")
-		}
-	}
-
-	// now, remove processed external mons from ceph quorum status response
-	// to not affect existing logic processing internal mons that are within the mon.count
-	quorumStatus = removeMonsFromQuorumStatusResponse(quorumStatus, extMonIDs)
-	return quorumStatus, nil
-}
-
-func removeMonsFromQuorumStatusResponse(quorumStatus cephclient.MonStatusResponse, idsToRemove []string) cephclient.MonStatusResponse {
-	var removeFromQuorum []int
-	var keepMons []cephclient.MonMapEntry
-
-	for _, mon := range quorumStatus.MonMap.Mons {
-		if !slices.Contains(idsToRemove, mon.Name) {
-			keepMons = append(keepMons, mon)
-			continue
-		}
-		removeFromQuorum = append(removeFromQuorum, mon.Rank)
-	}
-
-	var keepQuorum []int
-	for _, rank := range quorumStatus.Quorum {
-		if slices.Contains(removeFromQuorum, rank) {
-			continue
-		}
-		keepQuorum = append(keepQuorum, rank)
-	}
-	quorumStatus.MonMap.Mons = keepMons
-	quorumStatus.Quorum = keepQuorum
-	return quorumStatus
-}
-
 func (c *Cluster) trackMonInOrOutOfQuorum(monName string, inQuorum bool) (bool, error) {
 	updateNeeded := false
 	var monsOutOfQuorum []string
 	if monName == "" {
 		// All mons are in quorum, so make sure no mons are marked out of quorum
-		for monName, mon := range c.ClusterInfo.InternalMonitors {
+		for monName, mon := range c.ClusterInfo.Monitors {
 			if mon.OutOfQuorum {
 				logger.Infof("resetting mon %q to be back in quorum", monName)
 				mon.OutOfQuorum = false
@@ -486,7 +390,7 @@ func (c *Cluster) trackMonInOrOutOfQuorum(monName string, inQuorum bool) (bool,
 			}
 		}
 	} else {
-		mon, ok := c.ClusterInfo.InternalMonitors[monName]
+		mon, ok := c.ClusterInfo.Monitors[monName]
 		if !ok {
 			logger.Infof("mon %q not found to keep track of being out of quorum", monName)
 			return false, nil
@@ -771,7 +675,7 @@ func (c *Cluster) failoverMon(name string) error {
 			m.PublicIP = monService.Spec.ClusterIP
 		}
 	}
-	c.ClusterInfo.InternalMonitors[m.DaemonName] = cephclient.NewMonInfo(m.DaemonName, m.PublicIP, m.Port)
+	c.ClusterInfo.Monitors[m.DaemonName] = cephclient.NewMonInfo(m.DaemonName, m.PublicIP, m.Port)
 
 	// Start the deployment
 	newMonMightBeInQuorum = true
@@ -813,7 +717,7 @@ func (c *Cluster) removeMonWithOptionalQuorum(daemonName string, shouldRemoveFro
 			logger.Errorf("failed to remove mon %q from quorum. %v", daemonName, err)
 		}
 	}
-	delete(c.ClusterInfo.InternalMonitors, daemonName)
+	delete(c.ClusterInfo.Monitors, daemonName)
 	delete(c.mapping.Schedule, daemonName)
 
 	if err := c.saveMonConfig(); err != nil {
@@ -909,9 +813,9 @@ func (c *Cluster) addOrRemoveExternalMonitor(status cephclient.MonStatusResponse
 	// clearing the content of clusterinfo monitors
 	// and populate oldClusterInfoMonitors with monitors from clusterinfo
 	// later c.ClusterInfo.Monitors get populated again
-	for monName, mon := range c.ClusterInfo.InternalMonitors {
+	for monName, mon := range c.ClusterInfo.Monitors {
 		oldClusterInfoMonitors[mon.Name] = mon
-		delete(c.ClusterInfo.InternalMonitors, monName)
+		delete(c.ClusterInfo.Monitors, monName)
 	}
 	logger.Debugf("ClusterInfo is now Empty, refilling it from status.MonMap.Mons")
 
@@ -927,9 +831,17 @@ func (c *Cluster) addOrRemoveExternalMonitor(status cephclient.MonStatusResponse
 		if _, ok := oldClusterInfoMonitors[mon.Name]; !ok {
 			// If the mon is part of the quorum
 			if inQuorum {
-				info := monStatusToInfo(mon)
-				logger.Infof("new external mon %q found: %s, adding it", mon.Name, info.Endpoint)
-				c.ClusterInfo.InternalMonitors[mon.Name] = info
+				// let's add it to ClusterInfo
+				// FYI mon.PublicAddr is "10.97.171.131:6789/0"
+				// so we need to remove '/0'
+				endpointSlash := strings.Split(mon.PublicAddr, "/")
+				endpoint := endpointSlash[0]
+
+				// find IP and Port of that Mon
+				monIP := cephutil.GetIPFromEndpoint(endpoint)
+				monPort := cephutil.GetPortFromEndpoint(endpoint)
+				logger.Infof("new external mon %q found: %s, adding it", mon.Name, endpoint)
+				c.ClusterInfo.Monitors[mon.Name] = cephclient.NewMonInfo(mon.Name, monIP, monPort)
 			} else {
 				logger.Debugf("mon %q is not in quorum and not in ClusterInfo", mon.Name)
 			}
@@ -945,7 +857,7 @@ func (c *Cluster) addOrRemoveExternalMonitor(status cephclient.MonStatusResponse
 			} else {
 				// this mon was in clusterInfo and is still in the quorum
 				// add it again
-				c.ClusterInfo.InternalMonitors[mon.Name] = oldClusterInfoMonitors[mon.Name]
+				c.ClusterInfo.Monitors[mon.Name] = oldClusterInfoMonitors[mon.Name]
 				logger.Debugf("everything is fine mon %q in the clusterInfo and its quorum status is %v", mon.Name, inQuorum)
 			}
 		}
@@ -953,33 +865,20 @@ func (c *Cluster) addOrRemoveExternalMonitor(status cephclient.MonStatusResponse
 	// compare old clusterInfo with new ClusterInfo
 	// if length differ -> the are different
 	// then check if all elements are the same
-	if len(oldClusterInfoMonitors) != len(c.ClusterInfo.InternalMonitors) {
+	if len(oldClusterInfoMonitors) != len(c.ClusterInfo.Monitors) {
 		changed = true
 	} else {
-		for _, mon := range c.ClusterInfo.InternalMonitors {
+		for _, mon := range c.ClusterInfo.Monitors {
 			if old, ok := oldClusterInfoMonitors[mon.Name]; !ok || *old != *mon {
 				changed = true
 			}
 		}
 	}
 
-	logger.Debugf("ClusterInfo.Monitors is %+v", c.ClusterInfo.InternalMonitors)
+	logger.Debugf("ClusterInfo.Monitors is %+v", c.ClusterInfo.Monitors)
 	return changed, nil
 }
 
-func monStatusToInfo(mon cephclient.MonMapEntry) *cephclient.MonInfo {
-	// let's add it to ClusterInfo
-	// FYI mon.PublicAddr is "10.97.171.131:6789/0"
-	// so we need to remove '/0'
-	endpointSlash := strings.Split(mon.PublicAddr, "/")
-	endpoint := endpointSlash[0]
-
-	// find IP and Port of that Mon
-	monIP := cephutil.GetIPFromEndpoint(endpoint)
-	monPort := cephutil.GetPortFromEndpoint(endpoint)
-	return cephclient.NewMonInfo(mon.Name, monIP, monPort)
-}
-
 func (c *Cluster) evictMonIfMultipleOnSameNode() error {
 	if c.spec.Mon.AllowMultiplePerNode {
 		logger.Debug("skipping check for multiple mons on same node since multiple mons are allowed")
diff --git a/pkg/operator/ceph/cluster/mon/mon.go b/pkg/operator/ceph/cluster/mon/mon.go
index 4d059b39c..b0d2d94e7 100644
--- a/pkg/operator/ceph/cluster/mon/mon.go
+++ b/pkg/operator/ceph/cluster/mon/mon.go
@@ -58,8 +58,6 @@ const (
 	EndpointConfigMapName = "rook-ceph-mon-endpoints"
 	// EndpointDataKey is the name of the key inside the mon configmap to get the endpoints
 	EndpointDataKey = "data"
-	// EndpointExternalMonsKey key in EndpointConfigMapName configmap containing IDs of external mons
-	EndpointExternalMonsKey = "externalMons"
 	// AppName is the name of the secret storing cluster mon.admin key, fsid and name
 	AppName = "rook-ceph-mon"
 	//nolint:gosec // OperatorCreds is the name of the secret
@@ -305,7 +303,7 @@ func (c *Cluster) startMons(targetCount int) error {
 		}
 	}
 
-	logger.Debugf("mon endpoints used are: %s", flattenMonEndpoints(c.ClusterInfo.AllMonitors()))
+	logger.Debugf("mon endpoints used are: %s", flattenMonEndpoints(c.ClusterInfo.Monitors))
 
 	// reconcile mon PDB
 	if err := c.reconcileMonPDB(); err != nil {
@@ -388,18 +386,6 @@ func (c *Cluster) ConfigureArbiter() error {
 		return errors.Wrapf(err, "failed to find two failure domains %q in the CRUSH map", failureDomain)
 	}
 
-	// Before entering stretch mode, we must create at least one pool based on the default stretch rule
-	// Wait for the .mgr pool to be created, which we expect is defined as a CephBlockPool
-	// We may be able to remove this code waiting for the pool once this is in the ceph release:
-	//  https://github.com/ceph/ceph/pull/61371
-	logger.Info("enabling stretch mode... waiting for the builtin .mgr pool to be created")
-	err = wait.PollUntilContextTimeout(c.ClusterInfo.Context, pollInterval, totalWaitTime, true, func(ctx context.Context) (bool, error) {
-		return c.builtinMgrPoolExists(), nil
-	})
-	if err != nil {
-		return errors.Wrapf(err, "failed to wait for .mgr pool to be created before setting the stretch tiebreaker")
-	}
-
 	// Set the mon tiebreaker
 	if err := cephclient.SetMonStretchTiebreaker(c.context, c.ClusterInfo, c.arbiterMon, failureDomain); err != nil {
 		return errors.Wrap(err, "failed to set mon tiebreaker")
@@ -408,14 +394,6 @@ func (c *Cluster) ConfigureArbiter() error {
 	return nil
 }
 
-func (c *Cluster) builtinMgrPoolExists() bool {
-	_, err := cephclient.GetPoolDetails(c.context, c.ClusterInfo, ".mgr")
-
-	// If the call fails, either the pool does not exist or else there is some Ceph error.
-	// Either way, we want to wait for confirmation of the pool existing.
-	return err == nil
-}
-
 func (c *Cluster) readyToConfigureArbiter(checkOSDPods bool) (bool, error) {
 	failureDomain := c.getFailureDomainName()
 
@@ -486,7 +464,7 @@ func (c *Cluster) ensureMonsRunning(mons []*monConfig, i, targetCount int, requi
 	// Calculate how many mons we expected to exist after this method is completed.
 	// If we are adding a new mon, we expect one more than currently exist.
 	// If we haven't created all the desired mons already, we will be adding a new one with this iteration
-	expectedMonCount := len(c.ClusterInfo.InternalMonitors)
+	expectedMonCount := len(c.ClusterInfo.Monitors)
 	if expectedMonCount < targetCount {
 		expectedMonCount++
 	}
@@ -550,12 +528,13 @@ func (c *Cluster) initClusterInfo(cephVersion cephver.CephVersion, clusterName s
 }
 
 func (c *Cluster) initMonConfig(size int) (int, []*monConfig, error) {
+
 	// initialize the mon pod info for mons that have been previously created
 	mons := c.clusterInfoToMonConfig()
 
 	// initialize mon info if we don't have enough mons (at first startup)
-	existingCount := len(c.ClusterInfo.InternalMonitors)
-	for i := len(c.ClusterInfo.InternalMonitors); i < size; i++ {
+	existingCount := len(c.ClusterInfo.Monitors)
+	for i := len(c.ClusterInfo.Monitors); i < size; i++ {
 		c.maxMonID++
 		zone, err := c.findAvailableZone(mons)
 		if err != nil {
@@ -573,7 +552,7 @@ func (c *Cluster) clusterInfoToMonConfig() []*monConfig {
 
 func (c *Cluster) clusterInfoToMonConfigWithExclude(excludedMon string) []*monConfig {
 	mons := []*monConfig{}
-	for _, monitor := range c.ClusterInfo.InternalMonitors {
+	for _, monitor := range c.ClusterInfo.Monitors {
 		if monitor.Name == excludedMon {
 			// Skip a mon if it is being failed over
 			continue
@@ -852,7 +831,7 @@ func (c *Cluster) initMonIPs(mons []*monConfig) error {
 				}
 			}
 		}
-		c.ClusterInfo.InternalMonitors[m.DaemonName] = cephclient.NewMonInfo(m.DaemonName, m.PublicIP, m.Port)
+		c.ClusterInfo.Monitors[m.DaemonName] = cephclient.NewMonInfo(m.DaemonName, m.PublicIP, m.Port)
 	}
 
 	return nil
@@ -1161,7 +1140,7 @@ func (c *Cluster) saveMonConfig() error {
 		return errors.Wrap(err, "failed to write connection config for new mons")
 	}
 
-	monEndpoints := csi.MonEndpoints(c.ClusterInfo.AllMonitors(), c.spec.RequireMsgr2())
+	monEndpoints := csi.MonEndpoints(c.ClusterInfo.Monitors, c.spec.RequireMsgr2())
 	csiConfigEntry := &csi.CSIClusterConfigEntry{
 		Namespace: c.ClusterInfo.Namespace,
 		ClusterInfo: cephcsi.ClusterInfo{
@@ -1174,7 +1153,7 @@ func (c *Cluster) saveMonConfig() error {
 		return errors.Wrap(err, "failed to update csi cluster config")
 	}
 
-	if csi.EnableCSIOperator() && len(c.ClusterInfo.AllMonitors()) > 0 {
+	if csi.EnableCSIOperator() && len(c.ClusterInfo.Monitors) > 0 {
 		err := csi.CreateUpdateCephConnection(c.context.Client, c.ClusterInfo, c.spec)
 		if err != nil {
 			return errors.Wrap(err, "failed to create/update cephConnection")
@@ -1208,7 +1187,7 @@ func (c *Cluster) persistExpectedMonDaemons() error {
 	}
 
 	csiConfigValue, err := csi.FormatCsiClusterConfig(
-		c.Namespace, c.ClusterInfo.AllMonitors())
+		c.Namespace, c.ClusterInfo.Monitors)
 	if err != nil {
 		return errors.Wrap(err, "failed to format csi config")
 	}
@@ -1220,21 +1199,14 @@ func (c *Cluster) persistExpectedMonDaemons() error {
 
 	// preserve the mons detected out of quorum
 	var monsOutOfQuorum []string
-	for monName, mon := range c.ClusterInfo.InternalMonitors {
+	for monName, mon := range c.ClusterInfo.Monitors {
 		if mon.OutOfQuorum {
 			monsOutOfQuorum = append(monsOutOfQuorum, monName)
 		}
 	}
-	extMonIDs := make([]string, 0, len(c.ClusterInfo.ExternalMons))
-	if c.ClusterInfo.ExternalMons != nil {
-		for monID := range c.ClusterInfo.ExternalMons {
-			extMonIDs = append(extMonIDs, monID)
-		}
-	}
 
 	configMap.Data = map[string]string{
-		EndpointDataKey:         flattenMonEndpoints(c.ClusterInfo.AllMonitors()),
-		EndpointExternalMonsKey: strings.Join(extMonIDs, ","),
+		EndpointDataKey: flattenMonEndpoints(c.ClusterInfo.Monitors),
 		// persist the maxMonID that was previously stored in the configmap. We are likely saving info
 		// about scheduling of the mons, but we only want to update the maxMonID once a new mon has
 		// actually been started. If the operator is restarted or the reconcile is otherwise restarted,
diff --git a/pkg/operator/ceph/cluster/mon/util.go b/pkg/operator/ceph/cluster/mon/util.go
index 39b20e582..f688a3794 100644
--- a/pkg/operator/ceph/cluster/mon/util.go
+++ b/pkg/operator/ceph/cluster/mon/util.go
@@ -17,7 +17,6 @@ limitations under the License.
 package mon
 
 import (
-	"slices"
 	"strings"
 
 	"github.com/rook/rook/pkg/daemon/ceph/client"
@@ -35,17 +34,6 @@ func monInQuorum(monitor client.MonMapEntry, quorum []int) bool {
 	return false
 }
 
-func getMonByID(monID string, monMap client.MonStatusResponse) (info client.MonMapEntry, inQuorum bool) {
-	for _, mon := range monMap.MonMap.Mons {
-		if mon.Name != monID {
-			continue
-		}
-		monRank := mon.Rank
-		return mon, slices.Contains(monMap.Quorum, monRank)
-	}
-	return client.MonMapEntry{}, false
-}
-
 // convert the mon name to the numeric mon ID
 func fullNameToIndex(name string) (int, error) {
 	// remove the "rook-ceph-mon" prefix
diff --git a/pkg/operator/ceph/cluster/nodedaemon/exporter.go b/pkg/operator/ceph/cluster/nodedaemon/exporter.go
index cbd0495e5..8fb73b621 100644
--- a/pkg/operator/ceph/cluster/nodedaemon/exporter.go
+++ b/pkg/operator/ceph/cluster/nodedaemon/exporter.go
@@ -198,6 +198,11 @@ func getCephExporterDaemonContainer(cephCluster cephv1.CephCluster, cephVersion
 		args = append(args, "--addrs", "::")
 	}
 
+	containerPort := corev1.ContainerPort{
+		ContainerPort: int32(DefaultMetricsPort),
+		Protocol:      corev1.ProtocolTCP,
+	}
+
 	container := corev1.Container{
 		Name:            "ceph-exporter",
 		Command:         []string{"ceph-exporter"},
@@ -205,6 +210,7 @@ func getCephExporterDaemonContainer(cephCluster cephv1.CephCluster, cephVersion
 		Image:           cephImage,
 		ImagePullPolicy: controller.GetContainerImagePullPolicy(cephCluster.Spec.CephVersion.ImagePullPolicy),
 		Env:             envVars,
+		Ports:           []corev1.ContainerPort{containerPort},
 		VolumeMounts:    volumeMounts,
 		Resources:       cephv1.GetCephExporterResources(cephCluster.Spec.Resources),
 		SecurityContext: controller.PodSecurityContext(),
diff --git a/pkg/operator/ceph/cluster/osd/osd.go b/pkg/operator/ceph/cluster/osd/osd.go
index 188659da9..04d91ca8e 100644
--- a/pkg/operator/ceph/cluster/osd/osd.go
+++ b/pkg/operator/ceph/cluster/osd/osd.go
@@ -88,7 +88,6 @@ type Cluster struct {
 	deviceSets     []deviceSet
 	migrateOSD     *OSDInfo
 	deprecatedOSDs map[string][]int
-	nodeConfigmaps map[string]struct{}
 }
 
 // New creates an instance of the OSD manager
@@ -207,9 +206,6 @@ func (c *Cluster) Start() error {
 	if err := c.validateOSDSettings(); err != nil {
 		return err
 	}
-	if err := c.initializeNodeConfigmaps(); err != nil {
-		return err
-	}
 	logger.Infof("start running osds in namespace %q", namespace)
 
 	if !c.spec.Storage.UseAllNodes && len(c.spec.Storage.Nodes) == 0 && len(c.spec.Storage.StorageClassDeviceSets) == 0 {
@@ -1057,24 +1053,3 @@ func getOSDLocationFromArgs(args []string) (string, bool) {
 
 	return "", false
 }
-
-func (c *Cluster) initializeNodeConfigmaps() error {
-	// Find existing configmaps matching the label
-	label := "node.config.rook.io/osd"
-	options := metav1.ListOptions{LabelSelector: label}
-	configmaps, err := c.context.Clientset.CoreV1().ConfigMaps(c.clusterInfo.Namespace).List(c.clusterInfo.Context, options)
-	if err != nil {
-		return errors.Wrapf(err, "failed to list configmaps in namespace %q with label %q", c.clusterInfo.Namespace, label)
-	}
-
-	c.nodeConfigmaps = map[string]struct{}{}
-	prefix := k8sutil.ConfigOverrideName + "-"
-	for _, configmap := range configmaps.Items {
-		if strings.HasPrefix(configmap.Name, prefix) {
-			nodeName := strings.TrimPrefix(configmap.Name, prefix)
-			logger.Infof("found node %q configmap, will mount it for alternate ceph conf overrides for osds on this node", nodeName)
-			c.nodeConfigmaps[nodeName] = struct{}{}
-		}
-	}
-	return nil
-}
diff --git a/pkg/operator/ceph/cluster/osd/provision_spec.go b/pkg/operator/ceph/cluster/osd/provision_spec.go
index 81166ca95..01b70b62e 100644
--- a/pkg/operator/ceph/cluster/osd/provision_spec.go
+++ b/pkg/operator/ceph/cluster/osd/provision_spec.go
@@ -104,7 +104,6 @@ func (c *Cluster) provisionPodTemplateSpec(osdProps osdProperties, restart v1.Re
 	// ceph-volume is currently set up to use /etc/ceph/ceph.conf; this means no user config
 	// overrides will apply to ceph-volume, but this is unnecessary anyway
 	volumes := append(controller.PodVolumes(provisionConfig.DataPathMap, c.spec.DataDirHostPath, c.spec.DataDirHostPath, true), copyBinariesVolume)
-	volumes = c.updateCephConfigVolume(volumes, osdProps.crushHostname)
 
 	// create a volume on /dev so the pod can access devices on the host
 	devVolume := v1.Volume{Name: "devices", VolumeSource: v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/dev"}}}
diff --git a/pkg/operator/ceph/cluster/osd/spec.go b/pkg/operator/ceph/cluster/osd/spec.go
index 37c56fb9b..ac4aeaeb9 100644
--- a/pkg/operator/ceph/cluster/osd/spec.go
+++ b/pkg/operator/ceph/cluster/osd/spec.go
@@ -328,24 +328,6 @@ func deploymentName(osdID int) string {
 	return fmt.Sprintf(osdAppNameFmt, osdID)
 }
 
-func (c *Cluster) updateCephConfigVolume(volumes []v1.Volume, nodeName string) []v1.Volume {
-	if _, ok := c.nodeConfigmaps[nodeName]; !ok {
-		logger.Debugf("no configmap to override for node %q", nodeName)
-		return volumes
-	}
-
-	updatedVolumes := []v1.Volume{}
-	for _, volume := range volumes {
-		if volume.Name == k8sutil.ConfigOverrideName {
-			logger.Debugf("found configmap volume to override for node %q", nodeName)
-			name := fmt.Sprintf("%s-%s", k8sutil.ConfigOverrideName, nodeName)
-			volume.VolumeSource.Projected.Sources[0].ConfigMap.LocalObjectReference.Name = name
-		}
-		updatedVolumes = append(updatedVolumes, volume)
-	}
-	return updatedVolumes
-}
-
 func (c *Cluster) makeDeployment(osdProps osdProperties, osd *OSDInfo, provisionConfig *provisionConfig) (*apps.Deployment, error) {
 	deploymentName := deploymentName(osd.ID)
 	replicaCount := int32(1)
@@ -357,8 +339,6 @@ func (c *Cluster) makeDeployment(osdProps osdProperties, osd *OSDInfo, provision
 		dataDirHostPath = ""
 	}
 	volumes := controller.PodVolumes(provisionConfig.DataPathMap, dataDirHostPath, c.spec.DataDirHostPath, false)
-	volumes = c.updateCephConfigVolume(volumes, osdProps.crushHostname)
-
 	failureDomainValue := osdProps.crushHostname
 	doConfigInit := true     // initialize ceph.conf in init container?
 	doBinaryCopyInit := true // copy rook binary in an init container?
diff --git a/pkg/operator/ceph/cluster/watcher.go b/pkg/operator/ceph/cluster/watcher.go
index c5ee33184..57363bfa2 100644
--- a/pkg/operator/ceph/cluster/watcher.go
+++ b/pkg/operator/ceph/cluster/watcher.go
@@ -159,21 +159,27 @@ func (c *clientCluster) onK8sNode(ctx context.Context, object runtime.Object, op
 }
 
 func (c *clientCluster) handleNodeFailure(ctx context.Context, cluster *cephv1.CephCluster, node *corev1.Node, opNamespace string) error {
-	watchForNodeLoss := k8sutil.GetOperatorSetting("ROOK_WATCH_FOR_NODE_FAILURE", "true")
+	watchForNodeLoss, err := k8sutil.GetOperatorSetting(ctx, c.context.Clientset, opcontroller.OperatorSettingConfigMapName, "ROOK_WATCH_FOR_NODE_FAILURE", "true")
+	if err != nil {
+		return pkgerror.Wrapf(err, "failed to get configmap value `ROOK_WATCH_FOR_NODE_FAILURE`.")
+	}
 
 	if strings.ToLower(watchForNodeLoss) != "true" {
 		logger.Debugf("not watching for node failures since `ROOK_WATCH_FOR_NODE_FAILURE` is set to %q", watchForNodeLoss)
 		return nil
 	}
 
-	disabledCSI := k8sutil.GetOperatorSetting("ROOK_CSI_DISABLE_DRIVER", "false")
+	disabledCSI, err := k8sutil.GetOperatorSetting(ctx, c.context.Clientset, opcontroller.OperatorSettingConfigMapName, "ROOK_CSI_DISABLE_DRIVER", "false")
+	if err != nil {
+		return pkgerror.Wrapf(err, "failed to get configmap value `ROOK_CSI_DISABLE_DRIVER`.")
+	}
 
 	if strings.ToLower(disabledCSI) != "false" {
 		logger.Debugf("not watching for node failures since `ROOK_CSI_DISABLE_DRIVER` is set to %q, skip creating networkFence", disabledCSI)
 		return nil
 	}
 
-	_, err := c.context.ApiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, "networkfences.csiaddons.openshift.io", metav1.GetOptions{})
+	_, err = c.context.ApiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, "networkfences.csiaddons.openshift.io", metav1.GetOptions{})
 	if err != nil {
 		if errors.IsNotFound(err) {
 			logger.Debug("networkfences.csiaddons.openshift.io CRD not found, skip creating networkFence")
diff --git a/pkg/operator/ceph/controller.go b/pkg/operator/ceph/controller.go
index 5aa86f336..879a51b2e 100644
--- a/pkg/operator/ceph/controller.go
+++ b/pkg/operator/ceph/controller.go
@@ -33,6 +33,7 @@ import (
 	"github.com/rook/rook/pkg/operator/k8sutil"
 	"github.com/rook/rook/pkg/util"
 	v1 "k8s.io/api/core/v1"
+	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -102,43 +103,52 @@ func (r *ReconcileConfig) Reconcile(context context.Context, request reconcile.R
 
 func (r *ReconcileConfig) reconcile(request reconcile.Request) (reconcile.Result, error) {
 	// Fetch the operator's configmap
+	opConfig := &v1.ConfigMap{}
 	logger.Debugf("reconciling %s", request.NamespacedName)
-	if request.Name == opcontroller.OperatorSettingConfigMapName {
-		if err := k8sutil.ApplyOperatorSettingsConfigmap(r.opManagerContext, r.context.Clientset); err != nil {
-			return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to get operator setting configmap")
+	err := r.client.Get(r.opManagerContext, request.NamespacedName, opConfig)
+	if err != nil {
+		if kerrors.IsNotFound(err) {
+			logger.Debug("operator's configmap resource not found. will use default value or env var.")
+		} else {
+			// Error reading the object - requeue the request.
+			return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to get operator's configmap")
 		}
+	} else {
+		// Populate the operator's config
+		r.config.Parameters = opConfig.Data
 	}
+
 	// Reconcile Ceph CLI timeout, since the clusterd context is passed to by pointer to all CRD
 	// controllers they will receive the update
-	opcontroller.SetCephCommandsTimeout()
+	opcontroller.SetCephCommandsTimeout(r.config.Parameters)
 
 	// Reconcile Operator's logging level
-	reconcileOperatorLogLevel()
+	reconcileOperatorLogLevel(opConfig.Data)
 
 	// Reconcile discovery daemon
-	err := r.reconcileDiscoveryDaemon()
+	err = r.reconcileDiscoveryDaemon(opConfig.Data)
 	if err != nil {
 		return opcontroller.ImmediateRetryResult, err
 	}
 
-	opcontroller.SetAllowLoopDevices()
-	opcontroller.SetEnforceHostNetwork()
-	opcontroller.SetRevisionHistoryLimit()
-	opcontroller.SetObcAllowAdditionalConfigFields()
+	opcontroller.SetAllowLoopDevices(r.config.Parameters)
+	opcontroller.SetEnforceHostNetwork(r.config.Parameters)
+	opcontroller.SetRevisionHistoryLimit(r.config.Parameters)
+	opcontroller.SetObcAllowAdditionalConfigFields(r.config.Parameters)
 
 	logger.Infof("%s done reconciling", controllerName)
 	return reconcile.Result{}, nil
 }
 
-func reconcileOperatorLogLevel() {
-	rookLogLevel := k8sutil.GetOperatorSetting("ROOK_LOG_LEVEL", util.DefaultLogLevel.String())
+func reconcileOperatorLogLevel(data map[string]string) {
+	rookLogLevel := k8sutil.GetValue(data, "ROOK_LOG_LEVEL", util.DefaultLogLevel.String())
 	util.SetGlobalLogLevel(rookLogLevel, logger)
 }
 
-func (r *ReconcileConfig) reconcileDiscoveryDaemon() error {
+func (r *ReconcileConfig) reconcileDiscoveryDaemon(data map[string]string) error {
 	rookDiscover := discover.New(r.context.Clientset)
-	if opcontroller.DiscoveryDaemonEnabled() {
-		if err := rookDiscover.Start(r.opManagerContext, r.config.OperatorNamespace, r.config.Image, r.config.ServiceAccount, true); err != nil {
+	if opcontroller.DiscoveryDaemonEnabled(r.config.Parameters) {
+		if err := rookDiscover.Start(r.opManagerContext, r.config.OperatorNamespace, r.config.Image, r.config.ServiceAccount, data, true); err != nil {
 			return errors.Wrap(err, "failed to start device discovery daemonset")
 		}
 	} else {
diff --git a/pkg/operator/ceph/controller/cluster_info.go b/pkg/operator/ceph/controller/cluster_info.go
index 7a4d3851d..2da3191ca 100644
--- a/pkg/operator/ceph/controller/cluster_info.go
+++ b/pkg/operator/ceph/controller/cluster_info.go
@@ -23,7 +23,6 @@ import (
 	"os"
 	"path"
 	"path/filepath"
-	"slices"
 	"strconv"
 	"strings"
 	"time"
@@ -57,8 +56,6 @@ const (
 	EndpointConfigMapName = "rook-ceph-mon-endpoints"
 	// EndpointDataKey is the name of the key inside the mon configmap to get the endpoints
 	EndpointDataKey = "data"
-	// EndpointExternalMonsKey key in EndpointConfigMapName configmap containing IDs of external mons
-	EndpointExternalMonsKey = "externalMons"
 	// OutOfQuorumKey is the name of the key for tracking mons detected out of quorum
 	OutOfQuorumKey = "outOfQuorum"
 	// MaxMonIDKey is the name of the max mon id used
@@ -151,7 +148,7 @@ func CreateOrLoadClusterInfo(clusterdContext *clusterd.Context, context context.
 	}
 
 	// get the existing monitor config
-	clusterInfo.ExternalMons, clusterInfo.InternalMonitors, maxMonID, monMapping, err = loadMonConfig(clusterdContext.Clientset, namespace)
+	clusterInfo.Monitors, maxMonID, monMapping, err = loadMonConfig(clusterdContext.Clientset, namespace)
 	if err != nil {
 		return nil, maxMonID, monMapping, errors.Wrap(err, "failed to get mon config")
 	}
@@ -279,34 +276,33 @@ func UpdateMonsOutOfQuorum(clientset kubernetes.Interface, namespace string, mon
 }
 
 // loadMonConfig returns the monitor endpoints and maxMonID
-func loadMonConfig(clientset kubernetes.Interface, namespace string) (extMons map[string]*cephclient.MonInfo, internalMons map[string]*cephclient.MonInfo, maxMonID int, monMapping *Mapping, err error) {
+func loadMonConfig(clientset kubernetes.Interface, namespace string) (map[string]*cephclient.MonInfo, int, *Mapping, error) {
 	ctx := context.TODO()
-	extMons = map[string]*cephclient.MonInfo{}
-	internalMons = map[string]*cephclient.MonInfo{}
-	maxMonID = -1
-	monMapping = &Mapping{
+	monEndpointMap := map[string]*cephclient.MonInfo{}
+	maxMonID := -1
+	monMapping := &Mapping{
 		Schedule: map[string]*MonScheduleInfo{},
 	}
 
 	cm, err := clientset.CoreV1().ConfigMaps(namespace).Get(ctx, EndpointConfigMapName, metav1.GetOptions{})
 	if err != nil {
 		if !kerrors.IsNotFound(err) {
-			return nil, nil, maxMonID, monMapping, err
+			return nil, maxMonID, monMapping, err
 		}
 		// If the config map was not found, initialize the empty set of monitors
-		return extMons, internalMons, maxMonID, monMapping, nil
+		return monEndpointMap, maxMonID, monMapping, nil
 	}
 
 	// Parse the monitor List
 	if info, ok := cm.Data[EndpointDataKey]; ok {
-		internalMons = ParseMonEndpoints(info)
+		monEndpointMap = ParseMonEndpoints(info)
 	}
 
 	// Parse the mons that were detected out of quorum
 	if outOfQuorum, ok := cm.Data[OutOfQuorumKey]; ok && len(outOfQuorum) > 0 {
 		monNames := strings.Split(outOfQuorum, ",")
 		for _, monName := range monNames {
-			if monInfo, ok := internalMons[monName]; ok {
+			if monInfo, ok := monEndpointMap[monName]; ok {
 				monInfo.OutOfQuorum = true
 			} else {
 				logger.Warningf("did not find mon %q to set it out of quorum in the cluster info", monName)
@@ -326,7 +322,7 @@ func loadMonConfig(clientset kubernetes.Interface, namespace string) (extMons ma
 	}
 
 	// Make sure the max id is consistent with the current monitors
-	for _, m := range internalMons {
+	for _, m := range monEndpointMap {
 		id, _ := fullNameToIndex(m.Name)
 		if maxMonID < id {
 			maxMonID = id
@@ -340,19 +336,9 @@ func loadMonConfig(clientset kubernetes.Interface, namespace string) (extMons ma
 	if err != nil {
 		logger.Errorf("invalid JSON in mon mapping. %v", err)
 	}
-	// filter external mons:
-	if extMonIDsStr, ok := cm.Data[EndpointExternalMonsKey]; ok && extMonIDsStr != "" {
-		extMonIDs := strings.Split(extMonIDsStr, ",")
-		for monID, mon := range internalMons {
-			if slices.Contains(extMonIDs, monID) {
-				extMons[monID] = mon
-				delete(internalMons, monID)
-			}
-		}
-	}
 
-	logger.Debugf("loaded: maxMonID=%d, extMons=%+v, mons=%+v, assignment=%+v", maxMonID, extMons, internalMons, monMapping)
-	return extMons, internalMons, maxMonID, monMapping, nil
+	logger.Debugf("loaded: maxMonID=%d, mons=%+v, assignment=%+v", maxMonID, monEndpointMap, monMapping)
+	return monEndpointMap, maxMonID, monMapping, nil
 }
 
 // convert the mon name to the numeric mon ID
@@ -427,7 +413,7 @@ func PopulateExternalClusterInfo(cephClusterSpec *cephv1.ClusterSpec, context *c
 			time.Sleep(externalConnectionRetry)
 			continue
 		}
-		logger.Infof("found the cluster info to connect to the external cluster. will use %q to check health and monitor status. mons=%+v", clusterInfo.CephCred.Username, clusterInfo.AllMonitors())
+		logger.Infof("found the cluster info to connect to the external cluster. will use %q to check health and monitor status. mons=%+v", clusterInfo.CephCred.Username, clusterInfo.Monitors)
 		clusterInfo.OwnerInfo = ownerInfo
 
 		return clusterInfo, nil
diff --git a/pkg/operator/ceph/controller/controller_utils.go b/pkg/operator/ceph/controller/controller_utils.go
index 01ca10e56..8cb661d74 100644
--- a/pkg/operator/ceph/controller/controller_utils.go
+++ b/pkg/operator/ceph/controller/controller_utils.go
@@ -40,6 +40,7 @@ type OperatorConfig struct {
 	Image             string
 	ServiceAccount    string
 	NamespaceToWatch  string
+	Parameters        map[string]string
 }
 
 // ClusterHealth is passed to the various monitoring go routines to stop them when the context is cancelled
@@ -98,13 +99,13 @@ var (
 	obcAllowAdditionalConfigFields = strings.Split(obcAllowAdditionalConfigFieldsDefaultValue, ",")
 )
 
-func DiscoveryDaemonEnabled() bool {
-	return k8sutil.GetOperatorSetting("ROOK_ENABLE_DISCOVERY_DAEMON", "false") == "true"
+func DiscoveryDaemonEnabled(data map[string]string) bool {
+	return k8sutil.GetValue(data, "ROOK_ENABLE_DISCOVERY_DAEMON", "false") == "true"
 }
 
 // SetCephCommandsTimeout sets the timeout value of Ceph commands which are executed from Rook
-func SetCephCommandsTimeout() {
-	strTimeoutSeconds := k8sutil.GetOperatorSetting("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS", "15")
+func SetCephCommandsTimeout(data map[string]string) {
+	strTimeoutSeconds := k8sutil.GetValue(data, "ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS", "15")
 	timeoutSeconds, err := strconv.Atoi(strTimeoutSeconds)
 	if err != nil || timeoutSeconds < 1 {
 		logger.Warningf("ROOK_CEPH_COMMANDS_TIMEOUT is %q but it should be >= 1, set the default value 15", strTimeoutSeconds)
@@ -113,8 +114,8 @@ func SetCephCommandsTimeout() {
 	exec.CephCommandsTimeout = time.Duration(timeoutSeconds) * time.Second
 }
 
-func SetAllowLoopDevices() {
-	strLoopDevicesAllowed := k8sutil.GetOperatorSetting("ROOK_CEPH_ALLOW_LOOP_DEVICES", "false")
+func SetAllowLoopDevices(data map[string]string) {
+	strLoopDevicesAllowed := k8sutil.GetValue(data, "ROOK_CEPH_ALLOW_LOOP_DEVICES", "false")
 	var err error
 	loopDevicesAllowed, err = strconv.ParseBool(strLoopDevicesAllowed)
 	if err != nil {
@@ -127,8 +128,8 @@ func LoopDevicesAllowed() bool {
 	return loopDevicesAllowed
 }
 
-func SetEnforceHostNetwork() {
-	strval := k8sutil.GetOperatorSetting(enforceHostNetworkSettingName, enforceHostNetworkDefaultValue)
+func SetEnforceHostNetwork(data map[string]string) {
+	strval := k8sutil.GetValue(data, enforceHostNetworkSettingName, enforceHostNetworkDefaultValue)
 	val, err := strconv.ParseBool(strval)
 	if err != nil {
 		logger.Warningf("failed to parse value %q for %q. assuming false value", strval, enforceHostNetworkSettingName)
@@ -142,8 +143,8 @@ func EnforceHostNetwork() bool {
 	return cephv1.EnforceHostNetwork()
 }
 
-func SetRevisionHistoryLimit() {
-	strval := k8sutil.GetOperatorSetting(revisionHistoryLimitSettingName, "")
+func SetRevisionHistoryLimit(data map[string]string) {
+	strval := k8sutil.GetValue(data, revisionHistoryLimitSettingName, "")
 	var limit int32
 	if strval == "" {
 		logger.Debugf("not parsing empty string to int for %q. assuming default value.", revisionHistoryLimitSettingName)
@@ -165,8 +166,8 @@ func RevisionHistoryLimit() *int32 {
 	return revisionHistoryLimit
 }
 
-func SetObcAllowAdditionalConfigFields() {
-	strval := k8sutil.GetOperatorSetting(obcAllowAdditionalConfigFieldsSettingName, obcAllowAdditionalConfigFieldsDefaultValue)
+func SetObcAllowAdditionalConfigFields(data map[string]string) {
+	strval := k8sutil.GetValue(data, obcAllowAdditionalConfigFieldsSettingName, obcAllowAdditionalConfigFieldsDefaultValue)
 	obcAllowAdditionalConfigFields = strings.Split(strval, ",")
 }
 
@@ -184,30 +185,13 @@ func canIgnoreHealthErrStatusInReconcile(cephCluster cephv1.CephCluster, control
 		}
 	}
 
-	// If there are no errors, the caller actually expects false to be returned so the absence
-	// of an error doesn't cause the health status to be ignored. In production, if there are no
-	// errors, we would anyway expect the health status to be ok or warning. False in this case
-	// will cover if the health status is blank.
-	if len(healthErrKeys) == 0 {
-		return false
+	// If there is only one cause for HEALTH_ERR and it's on the allowed list of errors, ignore it.
+	var allowedErrStatus = []string{"MDS_ALL_DOWN"}
+	var ignoreHealthErr = len(healthErrKeys) == 1 && contains(allowedErrStatus, healthErrKeys[0])
+	if ignoreHealthErr {
+		logger.Debugf("%q: ignoring ceph status %q because only cause is %q (full status is %+v)", controllerName, cephCluster.Status.CephStatus.Health, healthErrKeys[0], cephCluster.Status.CephStatus)
 	}
-
-	allowedErrStatus := map[string]struct{}{
-		"MDS_ALL_DOWN":     {},
-		"MGR_MODULE_ERROR": {},
-	}
-	allCanBeIgnored := true
-	for _, healthErrKey := range healthErrKeys {
-		if _, ok := allowedErrStatus[healthErrKey]; !ok {
-			allCanBeIgnored = false
-			break
-		}
-	}
-	if allCanBeIgnored {
-		logger.Debugf("%q: ignoring ceph error status (full status is %+v)", controllerName, cephCluster.Status.CephStatus)
-		return true
-	}
-	return false
+	return ignoreHealthErr
 }
 
 // IsReadyToReconcile determines if a controller is ready to reconcile or not
diff --git a/pkg/operator/ceph/cr_manager.go b/pkg/operator/ceph/cr_manager.go
index 21d9e4860..b3398d681 100644
--- a/pkg/operator/ceph/cr_manager.go
+++ b/pkg/operator/ceph/cr_manager.go
@@ -143,7 +143,11 @@ func (o *Operator) startCRDManager(context context.Context, mgrErrorCh chan erro
 		}
 	}
 
-	metricsBindAddress := k8sutil.GetOperatorSetting("ROOK_OPERATOR_METRICS_BIND_ADDRESS", "0")
+	metricsBindAddress, err := k8sutil.GetOperatorSetting(context, o.context.Clientset, opcontroller.OperatorSettingConfigMapName, "ROOK_OPERATOR_METRICS_BIND_ADDRESS", "0")
+	if err != nil {
+		mgrErrorCh <- errors.Wrap(err, "failed to get configmap value `ROOK_OPERATOR_METRICS_BIND_ADDRESS`.")
+		return
+	}
 	skipNameValidation := true
 	// Set up a manager
 	mgrOpts := manager.Options{
diff --git a/pkg/operator/ceph/csi/ceph_connection.go b/pkg/operator/ceph/csi/ceph_connection.go
index 61d89c57b..518b09d2f 100644
--- a/pkg/operator/ceph/csi/ceph_connection.go
+++ b/pkg/operator/ceph/csi/ceph_connection.go
@@ -91,7 +91,7 @@ func generateCephConnSpec(c client.Client, clusterInfo *cephclient.ClusterInfo,
 		csiClusterConnSpec.RbdMirrorDaemonCount = cephRBDMirrorList.Items[0].Spec.Count
 	}
 
-	for _, mon := range clusterInfo.AllMonitors() {
+	for _, mon := range clusterInfo.Monitors {
 		csiClusterConnSpec.Monitors = append(csiClusterConnSpec.Monitors, mon.Endpoint)
 	}
 
diff --git a/pkg/operator/ceph/csi/controller.go b/pkg/operator/ceph/csi/controller.go
index 809b2da4d..b9c6c5034 100644
--- a/pkg/operator/ceph/csi/controller.go
+++ b/pkg/operator/ceph/csi/controller.go
@@ -42,6 +42,7 @@ import (
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
 )
 
 const (
@@ -157,17 +158,32 @@ func (r *ReconcileCSI) reconcile(request reconcile.Request) (reconcile.Result, e
 		return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed creating csi config map")
 	}
 
-	if err := k8sutil.ApplyOperatorSettingsConfigmap(r.opManagerContext, r.context.Clientset); err != nil {
-		return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to apply operator settings configmap")
+	// Fetch the operator's configmap. We force the NamespaceName to the operator since the request
+	// could be a CephCluster. If so the NamespaceName will be the one from the cluster and thus the
+	// CM won't be found
+	opNamespaceName := types.NamespacedName{Name: opcontroller.OperatorSettingConfigMapName, Namespace: r.opConfig.OperatorNamespace}
+	opConfig := &v1.ConfigMap{}
+	err = r.client.Get(r.opManagerContext, opNamespaceName, opConfig)
+	if err != nil {
+		if kerrors.IsNotFound(err) {
+			logger.Debug("operator's configmap resource not found. will use default value or env var.")
+			r.opConfig.Parameters = make(map[string]string)
+		} else {
+			// Error reading the object - requeue the request.
+			return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to get operator's configmap")
+		}
+	} else {
+		// Populate the operator's config
+		r.opConfig.Parameters = opConfig.Data
 	}
 
-	enableCSIOperator, err = strconv.ParseBool(k8sutil.GetOperatorSetting("ROOK_USE_CSI_OPERATOR", "false"))
+	enableCSIOperator, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_USE_CSI_OPERATOR", "false"))
 	if err != nil {
 		return reconcileResult, errors.Wrap(err, "unable to parse value for 'ROOK_USE_CSI_OPERATOR'")
 	}
 
 	// do not recocnile if csi driver is disabled
-	disableCSI, err := strconv.ParseBool(k8sutil.GetOperatorSetting("ROOK_CSI_DISABLE_DRIVER", "false"))
+	disableCSI, err := strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_DISABLE_DRIVER", "false"))
 	if err != nil {
 		return reconcile.Result{}, errors.Wrap(err, "unable to parse value for 'ROOK_CSI_DISABLE_DRIVER")
 	} else if disableCSI {
diff --git a/pkg/operator/ceph/csi/csi.go b/pkg/operator/ceph/csi/csi.go
index 6919eaf54..9769a519f 100644
--- a/pkg/operator/ceph/csi/csi.go
+++ b/pkg/operator/ceph/csi/csi.go
@@ -56,31 +56,31 @@ func (r *ReconcileCSI) validateAndConfigureDrivers(ownerInfo *k8sutil.OwnerInfo)
 func (r *ReconcileCSI) setParams() error {
 	var err error
 
-	if EnableRBD, err = strconv.ParseBool(k8sutil.GetOperatorSetting("ROOK_CSI_ENABLE_RBD", "true")); err != nil {
+	if EnableRBD, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_ENABLE_RBD", "true")); err != nil {
 		return errors.Wrap(err, "unable to parse value for 'ROOK_CSI_ENABLE_RBD'")
 	}
 
-	if EnableCephFS, err = strconv.ParseBool(k8sutil.GetOperatorSetting("ROOK_CSI_ENABLE_CEPHFS", "true")); err != nil {
+	if EnableCephFS, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_ENABLE_CEPHFS", "true")); err != nil {
 		return errors.Wrap(err, "unable to parse value for 'ROOK_CSI_ENABLE_CEPHFS'")
 	}
 
-	if EnableNFS, err = strconv.ParseBool(k8sutil.GetOperatorSetting("ROOK_CSI_ENABLE_NFS", "false")); err != nil {
+	if EnableNFS, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_ENABLE_NFS", "false")); err != nil {
 		return errors.Wrap(err, "unable to parse value for 'ROOK_CSI_ENABLE_NFS'")
 	}
 
-	if CSIParam.EnableCSIHostNetwork, err = strconv.ParseBool(k8sutil.GetOperatorSetting("CSI_ENABLE_HOST_NETWORK", "true")); err != nil {
+	if CSIParam.EnableCSIHostNetwork, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_HOST_NETWORK", "true")); err != nil {
 		return errors.Wrap(err, "failed to parse value for 'CSI_ENABLE_HOST_NETWORK'")
 	}
 
 	// If not set or set to anything but "false", the kernel client will be enabled
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_FORCE_CEPHFS_KERNEL_CLIENT", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_FORCE_CEPHFS_KERNEL_CLIENT", "true"), "false") {
 		CSIParam.ForceCephFSKernelClient = "false"
 	} else {
 		CSIParam.ForceCephFSKernelClient = "true"
 	}
 
 	// parse RPC timeout
-	timeout := k8sutil.GetOperatorSetting(grpcTimeout, "150")
+	timeout := k8sutil.GetValue(r.opConfig.Parameters, grpcTimeout, "150")
 	timeoutSeconds, err := strconv.Atoi(timeout)
 	if err != nil {
 		logger.Errorf("failed to parse %q. Defaulting to %d. %v", grpcTimeout, defaultGRPCTimeout, err)
@@ -93,32 +93,20 @@ func (r *ReconcileCSI) setParams() error {
 	CSIParam.GRPCTimeout = time.Duration(timeoutSeconds) * time.Second
 
 	// parse Liveness port
-	CSIParam.CephFSLivenessMetricsPort, err = getPortFromConfig("CSI_CEPHFS_LIVENESS_METRICS_PORT", DefaultCephFSLivenessMerticsPort)
+	CSIParam.CephFSLivenessMetricsPort, err = getPortFromConfig(r.opConfig.Parameters, "CSI_CEPHFS_LIVENESS_METRICS_PORT", DefaultCephFSLivenessMerticsPort)
 	if err != nil {
 		return errors.Wrap(err, "error getting CSI CephFS liveness metrics port.")
 	}
-
-	CSIParam.CSIAddonsPort, err = getPortFromConfig("CSIADDONS_PORT", DefaultCSIAddonsPort)
+	CSIParam.CSIAddonsPort, err = getPortFromConfig(r.opConfig.Parameters, "CSIADDONS_PORT", DefaultCSIAddonsPort)
 	if err != nil {
 		return errors.Wrap(err, "failed to get CSI Addons port")
 	}
-
-	CSIParam.CSIAddonsRBDProvisionerPort, err = getPortFromConfig("CSIADDONS_RBD_PROVISIONER_PORT", DefaultCSIAddonsRBDProvisionerPort)
-	if err != nil {
-		return errors.Wrap(err, "failed to get CSI Addons port for RBD provisioner")
-	}
-
-	CSIParam.CSIAddonsCephFSProvisionerPort, err = getPortFromConfig("CSIADDONS_CEPHFS_PROVISIONER_PORT", DefaultCSIAddonsCephFSProvisionerPort)
-	if err != nil {
-		return errors.Wrap(err, "failed to get CSI Addons port for Ceph FS provisioner")
-	}
-
-	CSIParam.RBDLivenessMetricsPort, err = getPortFromConfig("CSI_RBD_LIVENESS_METRICS_PORT", DefaultRBDLivenessMerticsPort)
+	CSIParam.RBDLivenessMetricsPort, err = getPortFromConfig(r.opConfig.Parameters, "CSI_RBD_LIVENESS_METRICS_PORT", DefaultRBDLivenessMerticsPort)
 	if err != nil {
 		return errors.Wrap(err, "error getting CSI RBD liveness metrics port.")
 	}
 
-	CSIParam.EnableLiveness, err = strconv.ParseBool(k8sutil.GetOperatorSetting("CSI_ENABLE_LIVENESS", "false"))
+	CSIParam.EnableLiveness, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_LIVENESS", "false"))
 	if err != nil {
 		return errors.Wrap(err, "failed to parse value for 'CSI_ENABLE_LIVENESS'")
 	}
@@ -126,31 +114,31 @@ func (r *ReconcileCSI) setParams() error {
 	CSIParam.Privileged = controller.HostPathRequiresPrivileged()
 
 	// default value `system-node-critical` is the highest available priority
-	CSIParam.PluginPriorityClassName = k8sutil.GetOperatorSetting("CSI_PLUGIN_PRIORITY_CLASSNAME", "")
+	CSIParam.PluginPriorityClassName = k8sutil.GetValue(r.opConfig.Parameters, "CSI_PLUGIN_PRIORITY_CLASSNAME", "")
 
 	// default value `system-cluster-critical` is applied for some
 	// critical pods in cluster but less priority than plugin pods
-	CSIParam.ProvisionerPriorityClassName = k8sutil.GetOperatorSetting("CSI_PROVISIONER_PRIORITY_CLASSNAME", "")
+	CSIParam.ProvisionerPriorityClassName = k8sutil.GetValue(r.opConfig.Parameters, "CSI_PROVISIONER_PRIORITY_CLASSNAME", "")
 
 	CSIParam.EnableOMAPGenerator = false
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_OMAP_GENERATOR", "false"), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_OMAP_GENERATOR", "false"), "true") {
 		CSIParam.EnableOMAPGenerator = true
 	}
 
 	CSIParam.EnableCSIDriverSeLinuxMount = true
 
 	CSIParam.EnableRBDSnapshotter = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_RBD_SNAPSHOTTER", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_RBD_SNAPSHOTTER", "true"), "false") {
 		CSIParam.EnableRBDSnapshotter = false
 	}
 
 	CSIParam.EnableCephFSSnapshotter = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_CEPHFS_SNAPSHOTTER", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_CEPHFS_SNAPSHOTTER", "true"), "false") {
 		CSIParam.EnableCephFSSnapshotter = false
 	}
 
 	CSIParam.EnableNFSSnapshotter = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_NFS_SNAPSHOTTER", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_NFS_SNAPSHOTTER", "true"), "false") {
 		CSIParam.EnableNFSSnapshotter = false
 	}
 
@@ -159,36 +147,36 @@ func (r *ReconcileCSI) setParams() error {
 	if err == nil {
 		CSIParam.EnableCSIAddonsSideCar = true
 	}
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_CSIADDONS", ""), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_CSIADDONS", ""), "false") {
 		CSIParam.EnableCSIAddonsSideCar = false
 	}
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_CSIADDONS", ""), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_CSIADDONS", ""), "true") {
 		CSIParam.EnableCSIAddonsSideCar = true
 	}
 
 	CSIParam.EnableCSITopology = false
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_TOPOLOGY", "false"), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_TOPOLOGY", "false"), "true") {
 		CSIParam.EnableCSITopology = true
 	}
 
 	CSIParam.EnableCSIEncryption = false
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_ENCRYPTION", "false"), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_ENCRYPTION", "false"), "true") {
 		CSIParam.EnableCSIEncryption = true
 	}
 
 	CSIParam.CSIEnableMetadata = false
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_METADATA", "false"), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_METADATA", "false"), "true") {
 		CSIParam.CSIEnableMetadata = true
 	}
 
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_CEPHFS_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_CEPHFS_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
 		CSIParam.CephFSPluginUpdateStrategy = onDelete
 	} else {
 		CSIParam.CephFSPluginUpdateStrategy = rollingUpdate
-		CSIParam.CephFSPluginUpdateStrategyMaxUnavailable = k8sutil.GetOperatorSetting("CSI_CEPHFS_PLUGIN_UPDATE_STRATEGY_MAX_UNAVAILABLE", "1")
+		CSIParam.CephFSPluginUpdateStrategyMaxUnavailable = k8sutil.GetValue(r.opConfig.Parameters, "CSI_CEPHFS_PLUGIN_UPDATE_STRATEGY_MAX_UNAVAILABLE", "1")
 	}
 
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_NFS_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_NFS_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
 		CSIParam.NFSPluginUpdateStrategy = onDelete
 	} else {
 		CSIParam.NFSPluginUpdateStrategy = rollingUpdate
@@ -196,19 +184,19 @@ func (r *ReconcileCSI) setParams() error {
 
 	// Default values are based on Kubernetes official documentation.
 	// https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_RBD_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_RBD_PLUGIN_UPDATE_STRATEGY", rollingUpdate), onDelete) {
 		CSIParam.RBDPluginUpdateStrategy = onDelete
 	} else {
 		CSIParam.RBDPluginUpdateStrategy = rollingUpdate
-		CSIParam.RBDPluginUpdateStrategyMaxUnavailable = k8sutil.GetOperatorSetting("CSI_RBD_PLUGIN_UPDATE_STRATEGY_MAX_UNAVAILABLE", "1")
+		CSIParam.RBDPluginUpdateStrategyMaxUnavailable = k8sutil.GetValue(r.opConfig.Parameters, "CSI_RBD_PLUGIN_UPDATE_STRATEGY_MAX_UNAVAILABLE", "1")
 	}
 
 	CSIParam.EnablePluginSelinuxHostMount = false
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_PLUGIN_ENABLE_SELINUX_HOST_MOUNT", "false"), "true") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_PLUGIN_ENABLE_SELINUX_HOST_MOUNT", "false"), "true") {
 		CSIParam.EnablePluginSelinuxHostMount = true
 	}
 
-	logLevel := k8sutil.GetOperatorSetting("CSI_LOG_LEVEL", "")
+	logLevel := k8sutil.GetValue(r.opConfig.Parameters, "CSI_LOG_LEVEL", "")
 	CSIParam.LogLevel = defaultLogLevel
 	if logLevel != "" {
 		l, err := strconv.ParseUint(logLevel, 10, 8)
@@ -219,7 +207,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	sidecarLogLevel := k8sutil.GetOperatorSetting("CSI_SIDECAR_LOG_LEVEL", "")
+	sidecarLogLevel := k8sutil.GetValue(r.opConfig.Parameters, "CSI_SIDECAR_LOG_LEVEL", "")
 	CSIParam.SidecarLogLevel = defaultSidecarLogLevel
 	if sidecarLogLevel != "" {
 		l, err := strconv.ParseUint(sidecarLogLevel, 10, 8)
@@ -230,7 +218,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	leaderElectionLeaseDuration := k8sutil.GetOperatorSetting("CSI_LEADER_ELECTION_LEASE_DURATION", "")
+	leaderElectionLeaseDuration := k8sutil.GetValue(r.opConfig.Parameters, "CSI_LEADER_ELECTION_LEASE_DURATION", "")
 	CSIParam.LeaderElectionLeaseDuration = defaultLeaderElectionLeaseDuration
 	if leaderElectionLeaseDuration != "" {
 		d, err := time.ParseDuration(leaderElectionLeaseDuration)
@@ -241,7 +229,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	leaderElectionRenewDeadline := k8sutil.GetOperatorSetting("CSI_LEADER_ELECTION_RENEW_DEADLINE", "")
+	leaderElectionRenewDeadline := k8sutil.GetValue(r.opConfig.Parameters, "CSI_LEADER_ELECTION_RENEW_DEADLINE", "")
 	CSIParam.LeaderElectionRenewDeadline = defaultLeaderElectionRenewDeadline
 	if leaderElectionRenewDeadline != "" {
 		d, err := time.ParseDuration(leaderElectionRenewDeadline)
@@ -252,7 +240,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	leaderElectionRetryPeriod := k8sutil.GetOperatorSetting("CSI_LEADER_ELECTION_RETRY_PERIOD", "")
+	leaderElectionRetryPeriod := k8sutil.GetValue(r.opConfig.Parameters, "CSI_LEADER_ELECTION_RETRY_PERIOD", "")
 	CSIParam.LeaderElectionRetryPeriod = defaultLeaderElectionRetryPeriod
 	if leaderElectionRetryPeriod != "" {
 		d, err := time.ParseDuration(leaderElectionRetryPeriod)
@@ -269,7 +257,7 @@ func (r *ReconcileCSI) setParams() error {
 		if len(nodes.Items) == 1 {
 			CSIParam.ProvisionerReplicas = 1
 		} else {
-			replicaStr := k8sutil.GetOperatorSetting("CSI_PROVISIONER_REPLICAS", "2")
+			replicaStr := k8sutil.GetValue(r.opConfig.Parameters, "CSI_PROVISIONER_REPLICAS", "2")
 			replicas, err := strconv.ParseInt(replicaStr, 10, 32)
 			if err != nil {
 				logger.Errorf("failed to parse CSI_PROVISIONER_REPLICAS. Defaulting to %d. %v", defaultProvisionerReplicas, err)
@@ -281,39 +269,39 @@ func (r *ReconcileCSI) setParams() error {
 		logger.Errorf("failed to get nodes. Defaulting the number of replicas of provisioner pods to %d. %v", CSIParam.ProvisionerReplicas, err)
 	}
 
-	CSIParam.CSIPluginImage = getImage("ROOK_CSI_CEPH_IMAGE", DefaultCSIPluginImage)
-	CSIParam.RegistrarImage = getImage("ROOK_CSI_REGISTRAR_IMAGE", DefaultRegistrarImage)
-	CSIParam.ProvisionerImage = getImage("ROOK_CSI_PROVISIONER_IMAGE", DefaultProvisionerImage)
-	CSIParam.AttacherImage = getImage("ROOK_CSI_ATTACHER_IMAGE", DefaultAttacherImage)
-	CSIParam.SnapshotterImage = getImage("ROOK_CSI_SNAPSHOTTER_IMAGE", DefaultSnapshotterImage)
-	CSIParam.ResizerImage = getImage("ROOK_CSI_RESIZER_IMAGE", DefaultResizerImage)
-	CSIParam.KubeletDirPath = k8sutil.GetOperatorSetting("ROOK_CSI_KUBELET_DIR_PATH", DefaultKubeletDirPath)
-	CSIParam.CSIAddonsImage = getImage("ROOK_CSIADDONS_IMAGE", DefaultCSIAddonsImage)
-	CSIParam.CSIDomainLabels = k8sutil.GetOperatorSetting("CSI_TOPOLOGY_DOMAIN_LABELS", "")
-	csiCephFSPodLabels := k8sutil.GetOperatorSetting("ROOK_CSI_CEPHFS_POD_LABELS", "")
+	CSIParam.CSIPluginImage = getImage(r.opConfig.Parameters, "ROOK_CSI_CEPH_IMAGE", DefaultCSIPluginImage)
+	CSIParam.RegistrarImage = getImage(r.opConfig.Parameters, "ROOK_CSI_REGISTRAR_IMAGE", DefaultRegistrarImage)
+	CSIParam.ProvisionerImage = getImage(r.opConfig.Parameters, "ROOK_CSI_PROVISIONER_IMAGE", DefaultProvisionerImage)
+	CSIParam.AttacherImage = getImage(r.opConfig.Parameters, "ROOK_CSI_ATTACHER_IMAGE", DefaultAttacherImage)
+	CSIParam.SnapshotterImage = getImage(r.opConfig.Parameters, "ROOK_CSI_SNAPSHOTTER_IMAGE", DefaultSnapshotterImage)
+	CSIParam.ResizerImage = getImage(r.opConfig.Parameters, "ROOK_CSI_RESIZER_IMAGE", DefaultResizerImage)
+	CSIParam.KubeletDirPath = k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_KUBELET_DIR_PATH", DefaultKubeletDirPath)
+	CSIParam.CSIAddonsImage = getImage(r.opConfig.Parameters, "ROOK_CSIADDONS_IMAGE", DefaultCSIAddonsImage)
+	CSIParam.CSIDomainLabels = k8sutil.GetValue(r.opConfig.Parameters, "CSI_TOPOLOGY_DOMAIN_LABELS", "")
+	csiCephFSPodLabels := k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_CEPHFS_POD_LABELS", "")
 	CSIParam.CSICephFSPodLabels = k8sutil.ParseStringToLabels(csiCephFSPodLabels)
-	csiNFSPodLabels := k8sutil.GetOperatorSetting("ROOK_CSI_NFS_POD_LABELS", "")
+	csiNFSPodLabels := k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_NFS_POD_LABELS", "")
 	CSIParam.CSINFSPodLabels = k8sutil.ParseStringToLabels(csiNFSPodLabels)
-	csiRBDPodLabels := k8sutil.GetOperatorSetting("ROOK_CSI_RBD_POD_LABELS", "")
+	csiRBDPodLabels := k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_RBD_POD_LABELS", "")
 	CSIParam.CSIRBDPodLabels = k8sutil.ParseStringToLabels(csiRBDPodLabels)
-	CSIParam.CSIClusterName = k8sutil.GetOperatorSetting("CSI_CLUSTER_NAME", "")
-	CSIParam.ImagePullPolicy = k8sutil.GetOperatorSetting("ROOK_CSI_IMAGE_PULL_POLICY", DefaultCSIImagePullPolicy)
-	CSIParam.CephFSKernelMountOptions = k8sutil.GetOperatorSetting("CSI_CEPHFS_KERNEL_MOUNT_OPTIONS", "")
+	CSIParam.CSIClusterName = k8sutil.GetValue(r.opConfig.Parameters, "CSI_CLUSTER_NAME", "")
+	CSIParam.ImagePullPolicy = k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_IMAGE_PULL_POLICY", DefaultCSIImagePullPolicy)
+	CSIParam.CephFSKernelMountOptions = k8sutil.GetValue(r.opConfig.Parameters, "CSI_CEPHFS_KERNEL_MOUNT_OPTIONS", "")
 
 	CSIParam.CephFSAttachRequired = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_CEPHFS_ATTACH_REQUIRED", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_CEPHFS_ATTACH_REQUIRED", "true"), "false") {
 		CSIParam.CephFSAttachRequired = false
 	}
 	CSIParam.RBDAttachRequired = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_RBD_ATTACH_REQUIRED", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_RBD_ATTACH_REQUIRED", "true"), "false") {
 		CSIParam.RBDAttachRequired = false
 	}
 	CSIParam.NFSAttachRequired = true
-	if strings.EqualFold(k8sutil.GetOperatorSetting("CSI_NFS_ATTACH_REQUIRED", "true"), "false") {
+	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_NFS_ATTACH_REQUIRED", "true"), "false") {
 		CSIParam.NFSAttachRequired = false
 	}
 
-	CSIParam.DriverNamePrefix = k8sutil.GetOperatorSetting("CSI_DRIVER_NAME_PREFIX", r.opConfig.OperatorNamespace)
+	CSIParam.DriverNamePrefix = k8sutil.GetValue(r.opConfig.Parameters, "CSI_DRIVER_NAME_PREFIX", r.opConfig.OperatorNamespace)
 
 	crd, err := r.context.ApiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), "volumegroupsnapshotclasses.groupsnapshot.storage.k8s.io", metav1.GetOptions{})
 	if err != nil && !kerrors.IsNotFound(err) {
@@ -324,7 +312,7 @@ func (r *ReconcileCSI) setParams() error {
 	if err == nil && len(crd.Spec.Versions) > 0 {
 		ver := crd.Spec.Versions[0]
 		// Determine if VolumeGroupSnapshot feature should be disabled
-		disableVGS := strings.EqualFold(k8sutil.GetOperatorSetting("CSI_ENABLE_VOLUME_GROUP_SNAPSHOT", "true"), "false")
+		disableVGS := strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_VOLUME_GROUP_SNAPSHOT", "true"), "false")
 		const (
 			enableVolumeGroupSnapshotFlag = "--enable-volume-group-snapshots="
 			featureGateFlag               = "--feature-gates=CSIVolumeGroupSnapshot="
@@ -346,7 +334,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	kubeApiBurst := k8sutil.GetOperatorSetting("CSI_KUBE_API_BURST", "")
+	kubeApiBurst := k8sutil.GetValue(r.opConfig.Parameters, "CSI_KUBE_API_BURST", "")
 	CSIParam.KubeApiBurst = 0
 	if kubeApiBurst != "" {
 		k, err := strconv.ParseUint(kubeApiBurst, 10, 16)
@@ -357,7 +345,7 @@ func (r *ReconcileCSI) setParams() error {
 		}
 	}
 
-	kubeApiQPS := k8sutil.GetOperatorSetting("CSI_KUBE_API_QPS", "")
+	kubeApiQPS := k8sutil.GetValue(r.opConfig.Parameters, "CSI_KUBE_API_QPS", "")
 	CSIParam.KubeApiQPS = 0
 	if kubeApiQPS != "" {
 		k, err := strconv.ParseFloat(kubeApiQPS, 32)
diff --git a/pkg/operator/ceph/csi/operator_config.go b/pkg/operator/ceph/csi/operator_config.go
index 7e013245c..638e512b2 100644
--- a/pkg/operator/ceph/csi/operator_config.go
+++ b/pkg/operator/ceph/csi/operator_config.go
@@ -94,9 +94,9 @@ func (r *ReconcileCSI) generateCSIOpConfigSpec(cluster cephv1.CephCluster, opCon
 				PodCommonSpec: csiopv1a1.PodCommonSpec{
 					PrioritylClassName: &CSIParam.ProvisionerPriorityClassName,
 					Affinity: &v1.Affinity{
-						NodeAffinity: getNodeAffinity(pluginNodeAffinityEnv, &v1.NodeAffinity{}),
+						NodeAffinity: getNodeAffinity(r.opConfig.Parameters, pluginNodeAffinityEnv, &v1.NodeAffinity{}),
 					},
-					Tolerations: getToleration(pluginTolerationsEnv, []v1.Toleration{}),
+					Tolerations: getToleration(r.opConfig.Parameters, pluginTolerationsEnv, []v1.Toleration{}),
 				},
 				Resources:              csiopv1a1.NodePluginResourcesSpec{},
 				KubeletDirPath:         CSIParam.KubeletDirPath,
@@ -106,9 +106,9 @@ func (r *ReconcileCSI) generateCSIOpConfigSpec(cluster cephv1.CephCluster, opCon
 				PodCommonSpec: csiopv1a1.PodCommonSpec{
 					PrioritylClassName: &CSIParam.PluginPriorityClassName,
 					Affinity: &v1.Affinity{
-						NodeAffinity: getNodeAffinity(provisionerNodeAffinityEnv, &v1.NodeAffinity{}),
+						NodeAffinity: getNodeAffinity(r.opConfig.Parameters, provisionerNodeAffinityEnv, &v1.NodeAffinity{}),
 					},
-					Tolerations: getToleration(provisionerTolerationsEnv, []v1.Toleration{}),
+					Tolerations: getToleration(r.opConfig.Parameters, provisionerTolerationsEnv, []v1.Toleration{}),
 				},
 				Replicas:  &CSIParam.ProvisionerReplicas,
 				Resources: csiopv1a1.ControllerPluginResourcesSpec{},
diff --git a/pkg/operator/ceph/csi/operator_driver.go b/pkg/operator/ceph/csi/operator_driver.go
index 99c845a2b..f5457be45 100644
--- a/pkg/operator/ceph/csi/operator_driver.go
+++ b/pkg/operator/ceph/csi/operator_driver.go
@@ -92,11 +92,11 @@ func (r *ReconcileCSI) createOrUpdateRBDDriverResource(cluster cephv1.CephCluste
 		Spec: spec,
 	}
 
-	rbdDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(rbdPluginResource)
+	rbdDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(r.opConfig.Parameters, rbdPluginResource)
 	rbdDriver.Spec.Liveness = &csiopv1a1.LivenessSpec{
 		MetricsPort: int(CSIParam.RBDLivenessMetricsPort),
 	}
-	rbdDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(rbdProvisionerResource)
+	rbdDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(r.opConfig.Parameters, rbdProvisionerResource)
 	rbdDriver.Spec.NodePlugin.UpdateStrategy = &v1.DaemonSetUpdateStrategy{
 		Type: v1.RollingUpdateDaemonSetStrategyType,
 	}
@@ -143,12 +143,12 @@ func (r *ReconcileCSI) createOrUpdateCephFSDriverResource(cluster cephv1.CephClu
 		cephFsDriver.Spec.SnapshotPolicy = csiopv1a1.VolumeGroupSnapshotPolicy
 	}
 
-	cephFsDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(cephFSPluginResource)
+	cephFsDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(r.opConfig.Parameters, cephFSPluginResource)
 	cephFsDriver.Spec.Liveness = &csiopv1a1.LivenessSpec{
 		MetricsPort: int(CSIParam.CephFSLivenessMetricsPort),
 	}
 
-	cephFsDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(cephFSProvisionerResource)
+	cephFsDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(r.opConfig.Parameters, cephFSProvisionerResource)
 	cephFsDriver.Spec.NodePlugin.UpdateStrategy = &v1.DaemonSetUpdateStrategy{
 		Type: v1.RollingUpdateDaemonSetStrategyType,
 	}
@@ -190,9 +190,9 @@ func (r *ReconcileCSI) createOrUpdateNFSDriverResource(cluster cephv1.CephCluste
 		Spec: spec,
 	}
 
-	NFSDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(nfsPluginResource)
+	NFSDriver.Spec.ControllerPlugin.Resources = createDriverControllerPluginResources(r.opConfig.Parameters, nfsPluginResource)
 
-	NFSDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(nfsProvisionerResource)
+	NFSDriver.Spec.NodePlugin.Resources = createDriverNodePluginResouces(r.opConfig.Parameters, nfsProvisionerResource)
 	NFSDriver.Spec.NodePlugin.UpdateStrategy = &v1.DaemonSetUpdateStrategy{
 		Type: v1.RollingUpdateDaemonSetStrategyType,
 	}
@@ -270,9 +270,9 @@ func (r *ReconcileCSI) generateDriverSpec(clusterName string) (csiopv1a1.DriverS
 			PodCommonSpec: csiopv1a1.PodCommonSpec{
 				PrioritylClassName: &CSIParam.ProvisionerPriorityClassName,
 				Affinity: &corev1.Affinity{
-					NodeAffinity: getNodeAffinity(pluginNodeAffinityEnv, &corev1.NodeAffinity{}),
+					NodeAffinity: getNodeAffinity(r.opConfig.Parameters, pluginNodeAffinityEnv, &corev1.NodeAffinity{}),
 				},
-				Tolerations: getToleration(pluginTolerationsEnv, []corev1.Toleration{}),
+				Tolerations: getToleration(r.opConfig.Parameters, pluginTolerationsEnv, []corev1.Toleration{}),
 			},
 			Resources:              csiopv1a1.NodePluginResourcesSpec{},
 			KubeletDirPath:         CSIParam.KubeletDirPath,
@@ -282,9 +282,9 @@ func (r *ReconcileCSI) generateDriverSpec(clusterName string) (csiopv1a1.DriverS
 			PodCommonSpec: csiopv1a1.PodCommonSpec{
 				PrioritylClassName: &CSIParam.PluginPriorityClassName,
 				Affinity: &corev1.Affinity{
-					NodeAffinity: getNodeAffinity(provisionerNodeAffinityEnv, &corev1.NodeAffinity{}),
+					NodeAffinity: getNodeAffinity(r.opConfig.Parameters, provisionerNodeAffinityEnv, &corev1.NodeAffinity{}),
 				},
-				Tolerations: getToleration(provisionerTolerationsEnv, []corev1.Toleration{}),
+				Tolerations: getToleration(r.opConfig.Parameters, provisionerTolerationsEnv, []corev1.Toleration{}),
 			},
 			Replicas:  &CSIParam.ProvisionerReplicas,
 			Resources: csiopv1a1.ControllerPluginResourcesSpec{},
@@ -294,9 +294,9 @@ func (r *ReconcileCSI) generateDriverSpec(clusterName string) (csiopv1a1.DriverS
 	}, nil
 }
 
-func createDriverControllerPluginResources(key string) csiopv1a1.ControllerPluginResourcesSpec {
+func createDriverControllerPluginResources(opConfig map[string]string, key string) csiopv1a1.ControllerPluginResourcesSpec {
 	controllerPluginResources := csiopv1a1.ControllerPluginResourcesSpec{}
-	resource := getComputeResource(key)
+	resource := getComputeResource(opConfig, key)
 
 	for _, r := range resource {
 		if !reflect.DeepEqual(r.Resource, corev1.ResourceRequirements{}) {
@@ -347,9 +347,9 @@ func createDriverControllerPluginResources(key string) csiopv1a1.ControllerPlugi
 	return controllerPluginResources
 }
 
-func createDriverNodePluginResouces(key string) csiopv1a1.NodePluginResourcesSpec {
+func createDriverNodePluginResouces(opConfig map[string]string, key string) csiopv1a1.NodePluginResourcesSpec {
 	nodePluginResources := csiopv1a1.NodePluginResourcesSpec{}
-	resource := getComputeResource(key)
+	resource := getComputeResource(opConfig, key)
 
 	for _, r := range resource {
 		if !reflect.DeepEqual(r.Resource, corev1.ResourceRequirements{}) {
diff --git a/pkg/operator/ceph/csi/spec.go b/pkg/operator/ceph/csi/spec.go
index 5b968453f..9381619cc 100644
--- a/pkg/operator/ceph/csi/spec.go
+++ b/pkg/operator/ceph/csi/spec.go
@@ -83,8 +83,6 @@ type Param struct {
 	SidecarLogLevel                          uint8
 	CephFSLivenessMetricsPort                uint16
 	CSIAddonsPort                            uint16
-	CSIAddonsRBDProvisionerPort              uint16
-	CSIAddonsCephFSProvisionerPort           uint16
 	RBDLivenessMetricsPort                   uint16
 	KubeApiBurst                             uint16
 	KubeApiQPS                               float32
@@ -221,14 +219,12 @@ const (
 	// kubelet directory path
 	DefaultKubeletDirPath = "/var/lib/kubelet"
 
-	// gRPC metrics and liveness port for CephFS and RBD
-	DefaultCephFSGRPCMerticsPort          uint16 = 9091
-	DefaultCephFSLivenessMerticsPort      uint16 = 9081
-	DefaultRBDGRPCMerticsPort             uint16 = 9090
-	DefaultRBDLivenessMerticsPort         uint16 = 9080
-	DefaultCSIAddonsPort                  uint16 = 9070
-	DefaultCSIAddonsRBDProvisionerPort    uint16 = 9070
-	DefaultCSIAddonsCephFSProvisionerPort uint16 = 9070
+	// grpc metrics and liveness port for cephfs  and rbd
+	DefaultCephFSGRPCMerticsPort     uint16 = 9091
+	DefaultCephFSLivenessMerticsPort uint16 = 9081
+	DefaultRBDGRPCMerticsPort        uint16 = 9090
+	DefaultRBDLivenessMerticsPort    uint16 = 9080
+	DefaultCSIAddonsPort             uint16 = 9070
 
 	// default log level for csi containers
 	defaultLogLevel        uint8 = 0
@@ -426,25 +422,25 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 	}
 
 	// get common provisioner tolerations and node affinity
-	provisionerTolerations := getToleration(provisionerTolerationsEnv, []corev1.Toleration{})
-	provisionerNodeAffinity := getNodeAffinity(provisionerNodeAffinityEnv, &corev1.NodeAffinity{})
+	provisionerTolerations := getToleration(r.opConfig.Parameters, provisionerTolerationsEnv, []corev1.Toleration{})
+	provisionerNodeAffinity := getNodeAffinity(r.opConfig.Parameters, provisionerNodeAffinityEnv, &corev1.NodeAffinity{})
 
 	// get common plugin tolerations and node affinity
-	pluginTolerations := getToleration(pluginTolerationsEnv, []corev1.Toleration{})
-	pluginNodeAffinity := getNodeAffinity(pluginNodeAffinityEnv, &corev1.NodeAffinity{})
+	pluginTolerations := getToleration(r.opConfig.Parameters, pluginTolerationsEnv, []corev1.Toleration{})
+	pluginNodeAffinity := getNodeAffinity(r.opConfig.Parameters, pluginNodeAffinityEnv, &corev1.NodeAffinity{})
 
 	if rbdPlugin != nil {
 		// get RBD plugin tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		rbdPluginTolerations := getToleration(rbdPluginTolerationsEnv, pluginTolerations)
-		rbdPluginNodeAffinity := getNodeAffinity(rbdPluginNodeAffinityEnv, pluginNodeAffinity)
+		rbdPluginTolerations := getToleration(r.opConfig.Parameters, rbdPluginTolerationsEnv, pluginTolerations)
+		rbdPluginNodeAffinity := getNodeAffinity(r.opConfig.Parameters, rbdPluginNodeAffinityEnv, pluginNodeAffinity)
 		// apply RBD plugin tolerations and node affinity
 		applyToPodSpec(&rbdPlugin.Spec.Template.Spec, rbdPluginNodeAffinity, rbdPluginTolerations)
 		// apply resource request and limit to rbdplugin containers
-		applyResourcesToContainers(rbdPluginResource, &rbdPlugin.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, rbdPluginResource, &rbdPlugin.Spec.Template.Spec)
 		// apply custom mounts to volumes
-		applyVolumeToPodSpec(rbdPluginVolume, &rbdPlugin.Spec.Template.Spec)
+		applyVolumeToPodSpec(r.opConfig.Parameters, rbdPluginVolume, &rbdPlugin.Spec.Template.Spec)
 		// apply custom mounts to volume mounts
-		applyVolumeMountToContainer(rbdPluginVolumeMount, "csi-rbdplugin", &rbdPlugin.Spec.Template.Spec)
+		applyVolumeMountToContainer(r.opConfig.Parameters, rbdPluginVolumeMount, "csi-rbdplugin", &rbdPlugin.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(rbdPlugin)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to rbd plugin daemonset %q", rbdPlugin.Name)
@@ -462,12 +458,12 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 
 	if rbdProvisionerDeployment != nil {
 		// get RBD provisioner tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		rbdProvisionerTolerations := getToleration(rbdProvisionerTolerationsEnv, provisionerTolerations)
-		rbdProvisionerNodeAffinity := getNodeAffinity(rbdProvisionerNodeAffinityEnv, provisionerNodeAffinity)
+		rbdProvisionerTolerations := getToleration(r.opConfig.Parameters, rbdProvisionerTolerationsEnv, provisionerTolerations)
+		rbdProvisionerNodeAffinity := getNodeAffinity(r.opConfig.Parameters, rbdProvisionerNodeAffinityEnv, provisionerNodeAffinity)
 		// apply RBD provisioner tolerations and node affinity
 		applyToPodSpec(&rbdProvisionerDeployment.Spec.Template.Spec, rbdProvisionerNodeAffinity, rbdProvisionerTolerations)
 		// apply resource request and limit to rbd provisioner containers
-		applyResourcesToContainers(rbdProvisionerResource, &rbdProvisionerDeployment.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, rbdProvisionerResource, &rbdProvisionerDeployment.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(rbdProvisionerDeployment)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to rbd provisioner deployment %q", rbdProvisionerDeployment.Name)
@@ -504,16 +500,16 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 
 	if cephfsPlugin != nil {
 		// get CephFS plugin tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		cephFSPluginTolerations := getToleration(cephFSPluginTolerationsEnv, pluginTolerations)
-		cephFSPluginNodeAffinity := getNodeAffinity(cephFSPluginNodeAffinityEnv, pluginNodeAffinity)
+		cephFSPluginTolerations := getToleration(r.opConfig.Parameters, cephFSPluginTolerationsEnv, pluginTolerations)
+		cephFSPluginNodeAffinity := getNodeAffinity(r.opConfig.Parameters, cephFSPluginNodeAffinityEnv, pluginNodeAffinity)
 		// apply CephFS plugin tolerations and node affinity
 		applyToPodSpec(&cephfsPlugin.Spec.Template.Spec, cephFSPluginNodeAffinity, cephFSPluginTolerations)
 		// apply resource request and limit to cephfs plugin containers
-		applyResourcesToContainers(cephFSPluginResource, &cephfsPlugin.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, cephFSPluginResource, &cephfsPlugin.Spec.Template.Spec)
 		// apply custom mounts to volumes
-		applyVolumeToPodSpec(cephFSPluginVolume, &cephfsPlugin.Spec.Template.Spec)
+		applyVolumeToPodSpec(r.opConfig.Parameters, cephFSPluginVolume, &cephfsPlugin.Spec.Template.Spec)
 		// apply custom mounts to volume mounts
-		applyVolumeMountToContainer(cephFSPluginVolumeMount, "csi-cephfsplugin", &cephfsPlugin.Spec.Template.Spec)
+		applyVolumeMountToContainer(r.opConfig.Parameters, cephFSPluginVolumeMount, "csi-cephfsplugin", &cephfsPlugin.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(cephfsPlugin)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to cephfs plugin daemonset %q", cephfsPlugin.Name)
@@ -532,13 +528,13 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 
 	if cephfsProvisionerDeployment != nil {
 		// get CephFS provisioner tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		cephFSProvisionerTolerations := getToleration(cephFSProvisionerTolerationsEnv, provisionerTolerations)
-		cephFSProvisionerNodeAffinity := getNodeAffinity(cephFSProvisionerNodeAffinityEnv, provisionerNodeAffinity)
+		cephFSProvisionerTolerations := getToleration(r.opConfig.Parameters, cephFSProvisionerTolerationsEnv, provisionerTolerations)
+		cephFSProvisionerNodeAffinity := getNodeAffinity(r.opConfig.Parameters, cephFSProvisionerNodeAffinityEnv, provisionerNodeAffinity)
 		// apply CephFS provisioner tolerations and node affinity
 		applyToPodSpec(&cephfsProvisionerDeployment.Spec.Template.Spec, cephFSProvisionerNodeAffinity, cephFSProvisionerTolerations)
 		// get resource details for cephfs provisioner
 		// apply resource request and limit to cephfs provisioner containers
-		applyResourcesToContainers(cephFSProvisionerResource, &cephfsProvisionerDeployment.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, cephFSProvisionerResource, &cephfsProvisionerDeployment.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(cephfsProvisionerDeployment)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to cephfs provisioner deployment %q", cephfsProvisionerDeployment.Name)
@@ -574,16 +570,16 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 
 	if nfsPlugin != nil {
 		// get NFS plugin tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		nfsPluginTolerations := getToleration(nfsPluginTolerationsEnv, pluginTolerations)
-		nfsPluginNodeAffinity := getNodeAffinity(nfsPluginNodeAffinityEnv, pluginNodeAffinity)
+		nfsPluginTolerations := getToleration(r.opConfig.Parameters, nfsPluginTolerationsEnv, pluginTolerations)
+		nfsPluginNodeAffinity := getNodeAffinity(r.opConfig.Parameters, nfsPluginNodeAffinityEnv, pluginNodeAffinity)
 		// apply NFS plugin tolerations and node affinity
 		applyToPodSpec(&nfsPlugin.Spec.Template.Spec, nfsPluginNodeAffinity, nfsPluginTolerations)
 		// apply resource request and limit to nfs plugin containers
-		applyResourcesToContainers(nfsPluginResource, &nfsPlugin.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, nfsPluginResource, &nfsPlugin.Spec.Template.Spec)
 		// apply custom mounts to volumes
-		applyVolumeToPodSpec(nfsPluginVolume, &nfsPlugin.Spec.Template.Spec)
+		applyVolumeToPodSpec(r.opConfig.Parameters, nfsPluginVolume, &nfsPlugin.Spec.Template.Spec)
 		// apply custom mounts to volume mounts
-		applyVolumeMountToContainer(nfsPluginVolumeMount, "csi-nfsplugin", &nfsPlugin.Spec.Template.Spec)
+		applyVolumeMountToContainer(r.opConfig.Parameters, nfsPluginVolumeMount, "csi-nfsplugin", &nfsPlugin.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(nfsPlugin)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to nfs plugin daemonset %q", nfsPlugin.Name)
@@ -602,13 +598,13 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 
 	if nfsProvisionerDeployment != nil {
 		// get NFS provisioner tolerations and node affinity, defaults to common tolerations and node affinity if not specified
-		nfsProvisionerTolerations := getToleration(nfsProvisionerTolerationsEnv, provisionerTolerations)
-		nfsProvisionerNodeAffinity := getNodeAffinity(nfsProvisionerNodeAffinityEnv, provisionerNodeAffinity)
+		nfsProvisionerTolerations := getToleration(r.opConfig.Parameters, nfsProvisionerTolerationsEnv, provisionerTolerations)
+		nfsProvisionerNodeAffinity := getNodeAffinity(r.opConfig.Parameters, nfsProvisionerNodeAffinityEnv, provisionerNodeAffinity)
 		// apply NFS provisioner tolerations and node affinity
 		applyToPodSpec(&nfsProvisionerDeployment.Spec.Template.Spec, nfsProvisionerNodeAffinity, nfsProvisionerTolerations)
 		// get resource details for nfs provisioner
 		// apply resource request and limit to nfs provisioner containers
-		applyResourcesToContainers(nfsProvisionerResource, &nfsProvisionerDeployment.Spec.Template.Spec)
+		applyResourcesToContainers(r.opConfig.Parameters, nfsProvisionerResource, &nfsProvisionerDeployment.Spec.Template.Spec)
 		err = ownerInfo.SetControllerReference(nfsProvisionerDeployment)
 		if err != nil {
 			return errors.Wrapf(err, "failed to set owner reference to nfs provisioner deployment %q", nfsProvisionerDeployment.Name)
@@ -634,7 +630,7 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 	if EnableRBD {
 		err = csiDriverobj.createCSIDriverInfo(
 			r.opManagerContext, r.context.Clientset,
-			RBDDriverName, k8sutil.GetOperatorSetting("CSI_RBD_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
+			RBDDriverName, k8sutil.GetValue(r.opConfig.Parameters, "CSI_RBD_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
 			tp.Param.RBDAttachRequired, CSIParam.EnableCSIDriverSeLinuxMount)
 		if err != nil {
 			return errors.Wrapf(err, "failed to create CSI driver object for %q", RBDDriverName)
@@ -643,7 +639,7 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 	if EnableCephFS {
 		err = csiDriverobj.createCSIDriverInfo(
 			r.opManagerContext, r.context.Clientset,
-			CephFSDriverName, k8sutil.GetOperatorSetting("CSI_CEPHFS_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
+			CephFSDriverName, k8sutil.GetValue(r.opConfig.Parameters, "CSI_CEPHFS_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
 			tp.Param.CephFSAttachRequired, CSIParam.EnableCSIDriverSeLinuxMount)
 		if err != nil {
 			return errors.Wrapf(err, "failed to create CSI driver object for %q", CephFSDriverName)
@@ -651,7 +647,7 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 	}
 	if EnableNFS {
 		err = csiDriverobj.createCSIDriverInfo(r.opManagerContext, r.context.Clientset,
-			NFSDriverName, k8sutil.GetOperatorSetting("CSI_NFS_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
+			NFSDriverName, k8sutil.GetValue(r.opConfig.Parameters, "CSI_NFS_FSGROUPPOLICY", string(k8scsi.FileFSGroupPolicy)),
 			tp.Param.NFSAttachRequired, CSIParam.EnableCSIDriverSeLinuxMount)
 		if err != nil {
 			return errors.Wrapf(err, "failed to create CSI driver object for %q", NFSDriverName)
diff --git a/pkg/operator/ceph/csi/template/cephfs/csi-cephfsplugin-provisioner-dep.yaml b/pkg/operator/ceph/csi/template/cephfs/csi-cephfsplugin-provisioner-dep.yaml
index ff9554ad7..5ed29f704 100644
--- a/pkg/operator/ceph/csi/template/cephfs/csi-cephfsplugin-provisioner-dep.yaml
+++ b/pkg/operator/ceph/csi/template/cephfs/csi-cephfsplugin-provisioner-dep.yaml
@@ -216,7 +216,7 @@ spec:
             - "--node-id=$(NODE_ID)"
             - "--v={{ .LogLevel }}"
             - "--csi-addons-address=$(CSIADDONS_ENDPOINT)"
-            - "--controller-port={{ .CSIAddonsCephFSProvisionerPort }}"
+            - "--controller-port={{ .CSIAddonsPort }}"
             - "--pod=$(POD_NAME)"
             - "--namespace=$(POD_NAMESPACE)"
             - "--pod-uid=$(POD_UID)"
@@ -231,7 +231,7 @@ spec:
             - "--log_file={{ .CsiLogRootPath }}/log/{{ .CsiComponentName }}/csi-addons.log"
             {{ end }}
           ports:
-            - containerPort: {{ .CSIAddonsCephFSProvisionerPort }}
+            - containerPort: {{ .CSIAddonsPort }}
           env:
             - name: NODE_ID
               valueFrom:
diff --git a/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin-provisioner-dep.yaml b/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin-provisioner-dep.yaml
index bacd76682..135338322 100644
--- a/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin-provisioner-dep.yaml
+++ b/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin-provisioner-dep.yaml
@@ -169,7 +169,7 @@ spec:
             - "--node-id=$(NODE_ID)"
             - "--v={{ .LogLevel }}"
             - "--csi-addons-address=$(CSIADDONS_ENDPOINT)"
-            - "--controller-port={{ .CSIAddonsRBDProvisionerPort }}"
+            - "--controller-port={{ .CSIAddonsPort }}"
             - "--pod=$(POD_NAME)"
             - "--namespace=$(POD_NAMESPACE)"
             - "--pod-uid=$(POD_UID)"
@@ -184,7 +184,7 @@ spec:
             - "--log_file={{ .CsiLogRootPath }}/log/{{ .CsiComponentName }}/csi-addons.log"
             {{ end }}
           ports:
-            - containerPort: {{ .CSIAddonsRBDProvisionerPort }}
+            - containerPort: {{ .CSIAddonsPort }}
           env:
             - name: NODE_ID
               valueFrom:
diff --git a/pkg/operator/ceph/csi/util.go b/pkg/operator/ceph/csi/util.go
index 60f549bb1..4c3e70827 100644
--- a/pkg/operator/ceph/csi/util.go
+++ b/pkg/operator/ceph/csi/util.go
@@ -98,8 +98,8 @@ func applyLogrotateSidecar(specTemplate *corev1.PodTemplateSpec, name, templateD
 	specTemplate.Spec.Containers = append(specTemplate.Spec.Containers, logrotateSidecarContainer)
 }
 
-func applyResourcesToContainers(key string, podspec *corev1.PodSpec) {
-	resource := getComputeResource(key)
+func applyResourcesToContainers(opConfig map[string]string, key string, podspec *corev1.PodSpec) {
+	resource := getComputeResource(opConfig, key)
 
 	for _, r := range resource {
 		for i, c := range podspec.Containers {
@@ -110,12 +110,12 @@ func applyResourcesToContainers(key string, podspec *corev1.PodSpec) {
 	}
 }
 
-func getComputeResource(key string) []k8sutil.ContainerResource {
+func getComputeResource(opConfig map[string]string, key string) []k8sutil.ContainerResource {
 	// Add Resource list if any
 	resource := []k8sutil.ContainerResource{}
 	var err error
 
-	if resourceRaw := k8sutil.GetOperatorSetting(key, ""); resourceRaw != "" {
+	if resourceRaw := k8sutil.GetValue(opConfig, key, ""); resourceRaw != "" {
 		resource, err = k8sutil.YamlToContainerResourceArray(resourceRaw)
 		if err != nil {
 			logger.Warningf("failed to parse %q. %v", resourceRaw, err)
@@ -124,9 +124,9 @@ func getComputeResource(key string) []k8sutil.ContainerResource {
 	return resource
 }
 
-func getToleration(tolerationsName string, defaultTolerations []corev1.Toleration) []corev1.Toleration {
+func getToleration(opConfig map[string]string, tolerationsName string, defaultTolerations []corev1.Toleration) []corev1.Toleration {
 	// Add toleration if any, otherwise return defaultTolerations
-	tolerationsRaw := k8sutil.GetOperatorSetting(tolerationsName, "")
+	tolerationsRaw := k8sutil.GetValue(opConfig, tolerationsName, "")
 	if tolerationsRaw == "" {
 		return defaultTolerations
 	}
@@ -147,9 +147,9 @@ func getToleration(tolerationsName string, defaultTolerations []corev1.Toleratio
 	return tolerations
 }
 
-func getNodeAffinity(nodeAffinityName string, defaultNodeAffinity *corev1.NodeAffinity) *corev1.NodeAffinity {
+func getNodeAffinity(opConfig map[string]string, nodeAffinityName string, defaultNodeAffinity *corev1.NodeAffinity) *corev1.NodeAffinity {
 	// Add NodeAffinity if any, otherwise return defaultNodeAffinity
-	nodeAffinity := k8sutil.GetOperatorSetting(nodeAffinityName, "")
+	nodeAffinity := k8sutil.GetValue(opConfig, nodeAffinityName, "")
 	if nodeAffinity == "" {
 		return defaultNodeAffinity
 	}
@@ -168,9 +168,9 @@ func applyToPodSpec(pod *corev1.PodSpec, n *corev1.NodeAffinity, t []corev1.Tole
 	}
 }
 
-func getPortFromConfig(env string, defaultPort uint16) (uint16, error) {
-	port := k8sutil.GetOperatorSetting(env, strconv.Itoa(int(defaultPort)))
-	if strings.TrimSpace(k8sutil.GetOperatorSetting(env, strconv.Itoa(int(defaultPort)))) == "" {
+func getPortFromConfig(data map[string]string, env string, defaultPort uint16) (uint16, error) {
+	port := k8sutil.GetValue(data, env, strconv.Itoa(int(defaultPort)))
+	if strings.TrimSpace(k8sutil.GetValue(data, env, strconv.Itoa(int(defaultPort)))) == "" {
 		return defaultPort, nil
 	}
 	p, err := strconv.ParseUint(port, 10, 64)
@@ -203,8 +203,8 @@ func GetPodAntiAffinity(key, value string) corev1.PodAntiAffinity {
 	}
 }
 
-func applyVolumeToPodSpec(configName string, podspec *corev1.PodSpec) {
-	volumesRaw := k8sutil.GetOperatorSetting(configName, "")
+func applyVolumeToPodSpec(opConfig map[string]string, configName string, podspec *corev1.PodSpec) {
+	volumesRaw := k8sutil.GetValue(opConfig, configName, "")
 	if volumesRaw == "" {
 		return
 	}
@@ -230,8 +230,8 @@ func applyVolumeToPodSpec(configName string, podspec *corev1.PodSpec) {
 	}
 }
 
-func applyVolumeMountToContainer(configName, containerName string, podspec *corev1.PodSpec) {
-	volumeMountsRaw := k8sutil.GetOperatorSetting(configName, "")
+func applyVolumeMountToContainer(opConfig map[string]string, configName, containerName string, podspec *corev1.PodSpec) {
+	volumeMountsRaw := k8sutil.GetValue(opConfig, configName, "")
 	if volumeMountsRaw == "" {
 		return
 	}
@@ -267,8 +267,8 @@ func applyVolumeMountToContainer(configName, containerName string, podspec *core
 
 // getImage returns the image for the given setting name. If the image does not contain version,
 // the default version is appended from the default image.
-func getImage(settingName, defaultImage string) string {
-	image := k8sutil.GetOperatorSetting(settingName, defaultImage)
+func getImage(data map[string]string, settingName, defaultImage string) string {
+	image := k8sutil.GetValue(data, settingName, defaultImage)
 	if !strings.Contains(image, ":") {
 		version := strings.SplitN(defaultImage, ":", 2)[1]
 		image = fmt.Sprintf("%s:%s", image, version)
diff --git a/pkg/operator/ceph/file/mds/livenessprobe.sh b/pkg/operator/ceph/file/mds/livenessprobe.sh
index 667bb8e4a..5cddfac15 100644
--- a/pkg/operator/ceph/file/mds/livenessprobe.sh
+++ b/pkg/operator/ceph/file/mds/livenessprobe.sh
@@ -7,8 +7,7 @@ FILESYSTEM_NAME="{{ .FilesystemName }}"
 KEYRING="{{ .Keyring }}"
 CMD_TIMEOUT="{{ .CmdTimeout }}"
 
-export CEPH_ARGS="--keyring $KEYRING" # workaround for https://tracker.ceph.com/issues/70167
-outp="$(ceph fs dump --mon-host="$ROOK_CEPH_MON_HOST" --mon-initial-members="$ROOK_CEPH_MON_INITIAL_MEMBERS" --name="mds.$MDS_ID" --connect-timeout="$CMD_TIMEOUT" --format json)"
+outp="$(ceph fs dump --mon-host="$ROOK_CEPH_MON_HOST" --mon-initial-members="$ROOK_CEPH_MON_INITIAL_MEMBERS" --keyring "$KEYRING" --connect-timeout="$CMD_TIMEOUT" --format json)"
 rc=$?
 if [ $rc -ne 0 ]; then
     echo "ceph MDS dump check failed with the following output:"
diff --git a/pkg/operator/ceph/file/subvolumegroup/controller.go b/pkg/operator/ceph/file/subvolumegroup/controller.go
index 2969f00b7..e5605785a 100644
--- a/pkg/operator/ceph/file/subvolumegroup/controller.go
+++ b/pkg/operator/ceph/file/subvolumegroup/controller.go
@@ -298,7 +298,7 @@ func (r *ReconcileCephFilesystemSubVolumeGroup) updateClusterConfig(cephFilesyst
 	csiClusterConfigEntry := csi.CSIClusterConfigEntry{
 		Namespace: r.clusterInfo.Namespace,
 		ClusterInfo: cephcsi.ClusterInfo{
-			Monitors: csi.MonEndpoints(r.clusterInfo.AllMonitors(), cephCluster.Spec.RequireMsgr2()),
+			Monitors: csi.MonEndpoints(r.clusterInfo.Monitors, cephCluster.Spec.RequireMsgr2()),
 			CephFS: cephcsi.CephFS{
 				SubvolumeGroup:     getSubvolumeGroupName(cephFilesystemSubVolumeGroup),
 				KernelMountOptions: r.clusterInfo.CSIDriverSpec.CephFS.KernelMountOptions,
diff --git a/pkg/operator/ceph/object/bucket/controller.go b/pkg/operator/ceph/object/bucket/controller.go
index b9a4007f7..eed00adc9 100644
--- a/pkg/operator/ceph/object/bucket/controller.go
+++ b/pkg/operator/ceph/object/bucket/controller.go
@@ -36,6 +36,7 @@ import (
 	v1 "k8s.io/api/core/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
 )
 
 const (
@@ -139,6 +140,25 @@ func (r *ReconcileBucket) reconcile(request reconcile.Request) (reconcile.Result
 		return reconcile.Result{}, nil
 	}
 
+	// Fetch the operator's configmap. We force the NamespaceName to the operator since the request
+	// could be a CephCluster. If so the NamespaceName will be the one from the cluster and thus the
+	// CM won't be found
+	opNamespaceName := types.NamespacedName{Name: opcontroller.OperatorSettingConfigMapName, Namespace: r.opConfig.OperatorNamespace}
+	opConfig := &v1.ConfigMap{}
+	err = r.client.Get(r.opManagerContext, opNamespaceName, opConfig)
+	if err != nil {
+		if kerrors.IsNotFound(err) {
+			logger.Debug("operator's configmap resource not found. will use default value or env var.")
+			r.opConfig.Parameters = make(map[string]string)
+		} else {
+			// Error reading the object - requeue the request.
+			return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to get operator's configmap")
+		}
+	} else {
+		// Populate the operator's config
+		r.opConfig.Parameters = opConfig.Data
+	}
+
 	// Populate clusterInfo during each reconcile
 	clusterInfo, _, _, err := opcontroller.LoadClusterInfo(r.context, r.opManagerContext, cephCluster.Namespace, &cephCluster.Spec)
 	if err != nil {
@@ -157,7 +177,7 @@ func (r *ReconcileBucket) reconcile(request reconcile.Request) (reconcile.Result
 
 	// note: the error return below is ignored and is expected to be removed from the
 	//   bucket library's `NewProvisioner` function
-	bucketController, _ := NewBucketController(r.context.KubeConfig, bucketProvisioner)
+	bucketController, _ := NewBucketController(r.context.KubeConfig, bucketProvisioner, r.opConfig.Parameters)
 
 	// We must run this in a go routine since RunWithContext() blocks and waits for the context to
 	// be Done. However, since it has a context, the go routine will exit on reload with SIGHUP
diff --git a/pkg/operator/ceph/object/bucket/util.go b/pkg/operator/ceph/object/bucket/util.go
index 7e8df47ea..19e294cb9 100644
--- a/pkg/operator/ceph/object/bucket/util.go
+++ b/pkg/operator/ceph/object/bucket/util.go
@@ -43,9 +43,9 @@ const (
 	objectStoreEndpoint  = "endpoint"
 )
 
-func NewBucketController(cfg *rest.Config, p *Provisioner) (*provisioner.Provisioner, error) {
+func NewBucketController(cfg *rest.Config, p *Provisioner, data map[string]string) (*provisioner.Provisioner, error) {
 	const allNamespaces = ""
-	provName, err := cephObject.GetObjectBucketProvisioner(p.clusterInfo.Namespace)
+	provName, err := cephObject.GetObjectBucketProvisioner(data, p.clusterInfo.Namespace)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to get provisioner name")
 	}
diff --git a/pkg/operator/ceph/object/config.go b/pkg/operator/ceph/object/config.go
index dad9879c6..8c3608f2b 100644
--- a/pkg/operator/ceph/object/config.go
+++ b/pkg/operator/ceph/object/config.go
@@ -27,7 +27,6 @@ import (
 	cephconfig "github.com/rook/rook/pkg/operator/ceph/config"
 	"github.com/rook/rook/pkg/operator/ceph/config/keyring"
 	v1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 const (
@@ -216,38 +215,12 @@ func (c *clusterConfig) generateMonConfigOptions(rgwConfig *rgwConfig) (map[stri
 	for flag, val := range c.store.Spec.Gateway.RgwConfig {
 		if currVal, ok := configOptions[flag]; ok {
 			// RGW might break with some user-specified config overrides; log clearly to help triage
-			logger.Infof("overriding object store %q RGW config option %q with user-specified rgwConfig",
-				fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag)
-			logger.Tracef("overriding object store %q RGW config option %q (current value %q) with user-specified rgwConfig %q",
+			logger.Infof("overriding object store %q RGW config option %q (current value %q) with user-specified rgwConfig %q",
 				fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag, currVal, val)
 		}
 		configOptions[flag] = val
 	}
 
-	for flag, val := range c.store.Spec.Gateway.RgwConfigFromSecret {
-		if val.Key == "" || val.Name == "" {
-			return nil, fmt.Errorf("invalid object store %q rgwConfigFromSecret value %q=%+v", fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag, val)
-		}
-		secret, err := c.context.Clientset.CoreV1().Secrets(c.clusterInfo.Namespace).Get(c.clusterInfo.Context, val.Name, metav1.GetOptions{})
-		if err != nil {
-			return nil, fmt.Errorf("unable to lookup secret for object store %q rgwConfigFromSecret %q=%+v", fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag, val)
-		}
-		secretVal, ok := secret.Data[val.Key]
-		if !ok {
-			return nil, fmt.Errorf("unable to lookup secret for object store %q rgwConfigFromSecret %q: key %q not found in secret %q",
-				fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag, val.Key, val.Name)
-		}
-
-		if currVal, ok := configOptions[flag]; ok {
-			// RGW might break with some user-specified config overrides; log clearly to help triage
-			logger.Infof("overriding object store %q RGW config option %q with user-specified rgwConfigFromSecret",
-				fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag)
-			logger.Tracef("overriding object store %q RGW config option %q (current value %q) with user-specified rgwConfigFromSecret %q",
-				fmt.Sprintf("%s/%s", c.store.Namespace, c.store.Name), flag, currVal, secretVal)
-		}
-		configOptions[flag] = string(secretVal)
-	}
-
 	return configOptions, nil
 }
 
diff --git a/pkg/operator/ceph/object/objectstore.go b/pkg/operator/ceph/object/objectstore.go
index 67a41626a..080367731 100644
--- a/pkg/operator/ceph/object/objectstore.go
+++ b/pkg/operator/ceph/object/objectstore.go
@@ -1137,10 +1137,10 @@ func poolName(poolPrefix, poolName string) string {
 }
 
 // GetObjectBucketProvisioner returns the bucket provisioner name appended with operator namespace if OBC is watching on it
-func GetObjectBucketProvisioner(namespace string) (string, error) {
+func GetObjectBucketProvisioner(data map[string]string, namespace string) (string, error) {
 	provName := bucketProvisionerName
-	obcWatchOnNamespace := k8sutil.GetOperatorSetting("ROOK_OBC_WATCH_OPERATOR_NAMESPACE", "false")
-	obcProvisionerNamePrefix := k8sutil.GetOperatorSetting("ROOK_OBC_PROVISIONER_NAME_PREFIX", "")
+	obcWatchOnNamespace := k8sutil.GetValue(data, "ROOK_OBC_WATCH_OPERATOR_NAMESPACE", "false")
+	obcProvisionerNamePrefix := k8sutil.GetValue(data, "ROOK_OBC_PROVISIONER_NAME_PREFIX", "")
 	if obcProvisionerNamePrefix != "" {
 		errList := validation.IsDNS1123Label(obcProvisionerNamePrefix)
 		if len(errList) > 0 {
diff --git a/pkg/operator/ceph/operator.go b/pkg/operator/ceph/operator.go
index 2674c41ab..e43ba8c78 100644
--- a/pkg/operator/ceph/operator.go
+++ b/pkg/operator/ceph/operator.go
@@ -129,11 +129,6 @@ func (o *Operator) runCRDManager() {
 	// Create the context and the cancellation function
 	opManagerContext, opManagerStop = context.WithCancel(context.Background())
 
-	// Initialize the operator settings from the configmap
-	if err := k8sutil.ApplyOperatorSettingsConfigmap(opManagerContext, o.context.Clientset); err != nil {
-		panic("failed to load operator settings configmap. " + err.Error())
-	}
-
 	// The operator config manager is also watching for changes here so if the operator config map
 	// content changes for ROOK_CURRENT_NAMESPACE_ONLY we must reload the operator CRD manager
 	o.namespaceToWatch()
@@ -154,7 +149,7 @@ func (o *Operator) runCRDManager() {
 }
 
 func (o *Operator) namespaceToWatch() {
-	currentNamespaceOnly := k8sutil.GetOperatorSetting("ROOK_CURRENT_NAMESPACE_ONLY", "true")
+	currentNamespaceOnly, _ := k8sutil.GetOperatorSetting(opManagerContext, o.context.Clientset, opcontroller.OperatorSettingConfigMapName, "ROOK_CURRENT_NAMESPACE_ONLY", "true")
 	if currentNamespaceOnly == "true" {
 		o.config.NamespaceToWatch = o.config.OperatorNamespace
 		logger.Infof("watching the current namespace %q for Ceph CRs", o.config.OperatorNamespace)
diff --git a/pkg/operator/ceph/pool/radosnamespace/controller.go b/pkg/operator/ceph/pool/radosnamespace/controller.go
index 14974bdf6..bbac0c9cc 100644
--- a/pkg/operator/ceph/pool/radosnamespace/controller.go
+++ b/pkg/operator/ceph/pool/radosnamespace/controller.go
@@ -323,7 +323,7 @@ func (r *ReconcileCephBlockPoolRadosNamespace) updateClusterConfig(cephBlockPool
 	csiClusterConfigEntry := csi.CSIClusterConfigEntry{
 		Namespace: r.clusterInfo.Namespace,
 		ClusterInfo: cephcsi.ClusterInfo{
-			Monitors: csi.MonEndpoints(r.clusterInfo.AllMonitors(), cephCluster.Spec.RequireMsgr2()),
+			Monitors: csi.MonEndpoints(r.clusterInfo.Monitors, cephCluster.Spec.RequireMsgr2()),
 			RBD: cephcsi.RBD{
 				RadosNamespace: getRadosNamespaceName(cephBlockPoolRadosNamespace),
 			},
diff --git a/pkg/operator/discover/discover.go b/pkg/operator/discover/discover.go
index dca033d57..5b73b8cc0 100644
--- a/pkg/operator/discover/discover.go
+++ b/pkg/operator/discover/discover.go
@@ -73,16 +73,16 @@ func New(clientset kubernetes.Interface) *Discover {
 }
 
 // Start the discover
-func (d *Discover) Start(ctx context.Context, namespace, discoverImage, securityAccount string, useCephVolume bool) error {
-	err := d.createDiscoverDaemonSet(ctx, namespace, discoverImage, securityAccount, useCephVolume)
+func (d *Discover) Start(ctx context.Context, namespace, discoverImage, securityAccount string, data map[string]string, useCephVolume bool) error {
+	err := d.createDiscoverDaemonSet(ctx, namespace, discoverImage, securityAccount, data, useCephVolume)
 	if err != nil {
 		return fmt.Errorf("failed to start discover daemonset. %v", err)
 	}
 	return nil
 }
 
-func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, discoverImage, securityAccount string, useCephVolume bool) error {
-	discoveryInterval := k8sutil.GetOperatorSetting(discoverIntervalEnv, defaultDiscoverInterval)
+func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, discoverImage, securityAccount string, data map[string]string, useCephVolume bool) error {
+	discoveryInterval := k8sutil.GetValue(data, discoverIntervalEnv, defaultDiscoverInterval)
 
 	discoveryParameters := []string{"discover",
 		"--discover-interval", discoveryInterval}
@@ -90,7 +90,7 @@ func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, disco
 		discoveryParameters = append(discoveryParameters, "--use-ceph-volume")
 	}
 
-	discoverDaemonResourcesRaw := k8sutil.GetOperatorSetting(discoverDaemonResourcesEnv, "")
+	discoverDaemonResourcesRaw := k8sutil.GetValue(data, discoverDaemonResourcesEnv, "")
 	discoverDaemonResources, err := k8sutil.YamlToContainerResource(discoverDaemonResourcesRaw)
 	if err != nil {
 		logger.Warningf("failed to parse.%s %v", discoverDaemonResourcesRaw, err)
@@ -176,7 +176,7 @@ func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, disco
 						},
 					},
 					HostNetwork:       opcontroller.EnforceHostNetwork(),
-					PriorityClassName: k8sutil.GetOperatorSetting(discoverDaemonsetPriorityClassNameEnv, ""),
+					PriorityClassName: k8sutil.GetValue(data, discoverDaemonsetPriorityClassNameEnv, ""),
 					SecurityContext:   &v1.PodSecurityContext{},
 				},
 			},
@@ -191,18 +191,18 @@ func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, disco
 	}
 
 	// Add toleration if any
-	tolerationValue := k8sutil.GetOperatorSetting(discoverDaemonsetTolerationEnv, "")
+	tolerationValue := k8sutil.GetValue(data, discoverDaemonsetTolerationEnv, "")
 	if tolerationValue != "" {
 		ds.Spec.Template.Spec.Tolerations = []v1.Toleration{
 			{
 				Effect:   v1.TaintEffect(tolerationValue),
 				Operator: v1.TolerationOpExists,
-				Key:      k8sutil.GetOperatorSetting(discoverDaemonsetTolerationKeyEnv, ""),
+				Key:      k8sutil.GetValue(data, discoverDaemonsetTolerationKeyEnv, ""),
 			},
 		}
 	}
 
-	tolerationsRaw := k8sutil.GetOperatorSetting(discoverDaemonsetTolerationsEnv, "")
+	tolerationsRaw := k8sutil.GetValue(data, discoverDaemonsetTolerationsEnv, "")
 	tolerations, err := k8sutil.YamlToTolerations(tolerationsRaw)
 	if err != nil {
 		logger.Warningf("failed to parse %s. %+v", tolerationsRaw, err)
@@ -211,7 +211,7 @@ func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, disco
 	ds.Spec.Template.Spec.Tolerations = append(ds.Spec.Template.Spec.Tolerations, tolerations...)
 
 	// Add NodeAffinity if any
-	nodeAffinity := k8sutil.GetOperatorSetting(discoverDaemonSetNodeAffinityEnv, "")
+	nodeAffinity := k8sutil.GetValue(data, discoverDaemonSetNodeAffinityEnv, "")
 	if nodeAffinity != "" {
 		v1NodeAffinity, err := k8sutil.GenerateNodeAffinity(nodeAffinity)
 		if err != nil {
@@ -224,7 +224,7 @@ func (d *Discover) createDiscoverDaemonSet(ctx context.Context, namespace, disco
 		logger.Infof("nodeAffinity: %s", v1NodeAffinity)
 	}
 
-	podLabels := k8sutil.GetOperatorSetting(discoverDaemonSetPodLabelsEnv, "")
+	podLabels := k8sutil.GetValue(data, discoverDaemonSetPodLabelsEnv, "")
 	if podLabels != "" {
 		podLabels := k8sutil.ParseStringToLabels(podLabels)
 		// Override / Set the app label even if set by the user as
diff --git a/pkg/operator/k8sutil/configmap.go b/pkg/operator/k8sutil/configmap.go
index 7e57bb7d7..08b932430 100644
--- a/pkg/operator/k8sutil/configmap.go
+++ b/pkg/operator/k8sutil/configmap.go
@@ -20,17 +20,17 @@ import (
 	"context"
 	"fmt"
 	"os"
+	"sync"
 	"time"
 
 	"github.com/pkg/errors"
 	v1 "k8s.io/api/core/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/client-go/kubernetes"
 )
 
-var loadedOperatorSettings bool
+var loggedOperatorSettings = sync.Map{}
 
 // DeleteConfigMap deletes a ConfigMap.
 func DeleteConfigMap(ctx context.Context, clientset kubernetes.Interface, cmName, namespace string, opts *DeleteOptions) error {
@@ -70,43 +70,50 @@ func CreateOrUpdateConfigMap(ctx context.Context, clientset kubernetes.Interface
 	return existingCm, nil
 }
 
-// GetOperatorSetting gets the operator setting from Env Var merged with ConfigMap
+// GetOperatorSetting gets the operator setting from ConfigMap or Env Var
 // returns defaultValue if setting is not found
-func GetOperatorSetting(settingName, defaultValue string) string {
-	if !loadedOperatorSettings {
-		logger.Warningf("WARNING: attempting to load operator setting %q before configmap is loaded", settingName)
-	}
-	if settingValue, ok := os.LookupEnv(settingName); ok {
-		return settingValue
+func GetOperatorSetting(context context.Context, clientset kubernetes.Interface, configMapName, settingName, defaultValue string) (string, error) {
+	// config must be in operator pod namespace
+	namespace := os.Getenv(PodNamespaceEnvVar)
+	cm, err := clientset.CoreV1().ConfigMaps(namespace).Get(context, configMapName, metav1.GetOptions{})
+	if err != nil {
+		if kerrors.IsNotFound(err) {
+			if settingValue, ok := os.LookupEnv(settingName); ok {
+				logger.Infof("%s=%q (env var)", settingName, settingValue)
+				return settingValue, nil
+			}
+			logger.Infof("%s=%q (default)", settingName, defaultValue)
+			return defaultValue, nil
+		}
+		return defaultValue, fmt.Errorf("error reading ConfigMap %q. %v", configMapName, err)
 	}
-	return defaultValue
+
+	return GetValue(cm.Data, settingName, defaultValue), nil
 }
 
-func ApplyOperatorSettingsConfigmap(ctx context.Context, clientset kubernetes.Interface) error {
-	namespacedName := types.NamespacedName{Namespace: os.Getenv(PodNamespaceEnvVar), Name: "rook-ceph-operator-config"}
-	logger.Debugf("loading operator settings configmap from %v", namespacedName)
-	opConfig, err := clientset.CoreV1().ConfigMaps(namespacedName.Namespace).Get(ctx, namespacedName.Name, metav1.GetOptions{})
+func GetValue(data map[string]string, settingName, defaultValue string) string {
+	settingValue := defaultValue
+	settingSource := "default"
 
-	if err != nil {
-		if kerrors.IsNotFound(err) {
-			loadedOperatorSettings = true
-			logger.Debug("operator's configmap resource not found. will use default value or env var.")
-			return nil
-		}
-		return err
+	if val, ok := data[settingName]; ok {
+		settingValue = val
+		settingSource = "configmap"
+	} else if val, ok := os.LookupEnv(settingName); ok {
+		settingValue = val
+		settingSource = "env var"
 	}
 
-	for key, value := range opConfig.Data {
-		currentValue := os.Getenv(key)
-		if currentValue == value {
-			continue
-		}
-		logger.Infof("operator setting %q = %q", key, value)
-		if err := os.Setenv(key, value); err != nil {
-			logger.Errorf("failed to set env var %q = %q. %v", key, value, err)
-		}
+	if logChangedSettings(settingName, settingValue) {
+		logger.Infof("%s=%q (%s)", settingName, settingValue, settingSource)
+	}
+
+	return settingValue
+}
+
+func logChangedSettings(settingName, settingValue string) bool {
+	if val, ok := loggedOperatorSettings.Load(settingName); !ok || val != settingValue {
+		loggedOperatorSettings.Store(settingName, settingValue)
+		return true
 	}
-	loadedOperatorSettings = true
-	logger.Debug("done loading operator settings configmap")
-	return nil
+	return false
 }


Test patch:
diff --git a/.github/workflows/canary-integration-test.yml b/.github/workflows/canary-integration-test.yml
index 5e0ab7f7f..dd31d10fc 100644
--- a/.github/workflows/canary-integration-test.yml
+++ b/.github/workflows/canary-integration-test.yml
@@ -1644,7 +1644,6 @@ jobs:
         uses: ./.github/workflows/collect-logs
         with:
           name: ${{ github.job }}-${{ matrix.ceph-image }}
-          additional-namespace: rook-ceph-secondary
 
   encryption-pvc-kms-ibm-kp:
     runs-on: ubuntu-22.04
diff --git a/.github/workflows/integration-test-keystone-auth-suite.yaml b/.github/workflows/integration-test-keystone-auth-suite.yaml
index 34c2ed6f6..73873f084 100644
--- a/.github/workflows/integration-test-keystone-auth-suite.yaml
+++ b/.github/workflows/integration-test-keystone-auth-suite.yaml
@@ -24,7 +24,7 @@ concurrency:
 jobs:
   TestCephKeystoneAuthSuite:
     if: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && !contains(github.event.pull_request.labels.*.name, 'skip-ci') }}
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-20.04
     strategy:
       fail-fast: false
       matrix:
diff --git a/deploy/examples/create-external-cluster-resources-tests.py b/deploy/examples/create-external-cluster-resources-tests.py
index 8c1feecf9..b3a5196c7 100644
--- a/deploy/examples/create-external-cluster-resources-tests.py
+++ b/deploy/examples/create-external-cluster-resources-tests.py
@@ -3,7 +3,7 @@ Copyright 2022 The Rook Authors. All rights reserved.
 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
+	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.
diff --git a/pkg/daemon/ceph/client/config_test.go b/pkg/daemon/ceph/client/config_test.go
index cf7810312..cfb2afa20 100644
--- a/pkg/daemon/ceph/client/config_test.go
+++ b/pkg/daemon/ceph/client/config_test.go
@@ -37,7 +37,7 @@ func TestCreateDefaultCephConfig(t *testing.T) {
 		FSID:          "id",
 		MonitorSecret: "monsecret",
 		Namespace:     "foo-cluster",
-		InternalMonitors: map[string]*MonInfo{
+		Monitors: map[string]*MonInfo{
 			"node0": {Name: "mon0", Endpoint: "10.0.0.1:6789"},
 			"node1": {Name: "mon1", Endpoint: "10.0.0.2:6789"},
 		},
@@ -89,7 +89,7 @@ func TestGenerateConfigFile(t *testing.T) {
 		FSID:          "myfsid",
 		MonitorSecret: "monsecret",
 		Namespace:     ns,
-		InternalMonitors: map[string]*MonInfo{
+		Monitors: map[string]*MonInfo{
 			"node0": {Name: "mon0", Endpoint: "10.0.0.1:6789"},
 		},
 		CephCred: CephCred{Username: "admin", Secret: "mysecret"},
@@ -112,9 +112,9 @@ func TestGenerateConfigFile(t *testing.T) {
 }
 
 func verifyConfig(t *testing.T, cephConfig *CephConfig, cluster *ClusterInfo, loggingLevel int) {
-	monMembers := make([]string, len(cluster.InternalMonitors))
+	monMembers := make([]string, len(cluster.Monitors))
 	i := 0
-	for _, expectedMon := range cluster.InternalMonitors {
+	for _, expectedMon := range cluster.Monitors {
 		contained := false
 		monMembers[i] = expectedMon.Name
 		for _, actualMon := range strings.Split(cephConfig.MonMembers, " ") {
diff --git a/pkg/daemon/ceph/client/test/info.go b/pkg/daemon/ceph/client/test/info.go
index c5081e243..4aaa57ac3 100644
--- a/pkg/daemon/ceph/client/test/info.go
+++ b/pkg/daemon/ceph/client/test/info.go
@@ -51,15 +51,14 @@ func CreateTestClusterInfo(monCount int) *client.ClusterInfo {
 			Username: client.AdminUsername,
 			Secret:   "adminkey",
 		},
-		InternalMonitors: map[string]*client.MonInfo{},
-		ExternalMons:     map[string]*client.MonInfo{},
-		OwnerInfo:        ownerInfo,
-		Context:          context.TODO(),
+		Monitors:  map[string]*client.MonInfo{},
+		OwnerInfo: ownerInfo,
+		Context:   context.TODO(),
 	}
 	mons := []string{"a", "b", "c", "d", "e"}
 	for i := 0; i < monCount; i++ {
 		id := mons[i]
-		c.InternalMonitors[id] = &client.MonInfo{
+		c.Monitors[id] = &client.MonInfo{
 			Name:     id,
 			Endpoint: fmt.Sprintf("1.2.3.%d:3300", (i + 1)),
 		}
diff --git a/pkg/operator/ceph/cluster/mon/health_test.go b/pkg/operator/ceph/cluster/mon/health_test.go
index 5a9452a75..90a778dbc 100644
--- a/pkg/operator/ceph/cluster/mon/health_test.go
+++ b/pkg/operator/ceph/cluster/mon/health_test.go
@@ -29,7 +29,6 @@ import (
 	cephclient "github.com/rook/rook/pkg/daemon/ceph/client"
 	clienttest "github.com/rook/rook/pkg/daemon/ceph/client/test"
 	"github.com/rook/rook/pkg/operator/ceph/config"
-	"github.com/rook/rook/pkg/operator/ceph/controller"
 	opcontroller "github.com/rook/rook/pkg/operator/ceph/controller"
 	testopk8s "github.com/rook/rook/pkg/operator/k8sutil/test"
 	"github.com/rook/rook/pkg/operator/test"
@@ -75,7 +74,7 @@ func TestCheckHealth(t *testing.T) {
 	assert.NotNil(t, err)
 
 	c.spec.Mon.Count = 3
-	logger.Infof("initial mons: %v", c.ClusterInfo.InternalMonitors)
+	logger.Infof("initial mons: %v", c.ClusterInfo.Monitors)
 	c.waitForStart = false
 
 	c.mapping.Schedule["f"] = &opcontroller.MonScheduleInfo{
@@ -93,7 +92,7 @@ func TestCheckHealth(t *testing.T) {
 	c.ClusterInfo.Context = ctx
 	err = c.checkHealth(ctx)
 	assert.Nil(t, err)
-	logger.Infof("mons after checkHealth: %v", c.ClusterInfo.InternalMonitors)
+	logger.Infof("mons after checkHealth: %v", c.ClusterInfo.Monitors)
 	assert.ElementsMatch(t, []string{"rook-ceph-mon-a", "rook-ceph-mon-f"}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
 	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
 
@@ -106,8 +105,8 @@ func TestCheckHealth(t *testing.T) {
 		"g",
 	}
 	for _, monName := range newMons {
-		_, ok := c.ClusterInfo.InternalMonitors[monName]
-		assert.True(t, ok, fmt.Sprintf("mon %s not found in monitor list. %v", monName, c.ClusterInfo.InternalMonitors))
+		_, ok := c.ClusterInfo.Monitors[monName]
+		assert.True(t, ok, fmt.Sprintf("mon %s not found in monitor list. %v", monName, c.ClusterInfo.Monitors))
 	}
 
 	deployments, err := clientset.AppsV1().Deployments(c.Namespace).List(ctx, metav1.ListOptions{})
@@ -154,7 +153,7 @@ func TestCheckHealth(t *testing.T) {
 func TestRemoveExtraMon(t *testing.T) {
 	endpoint := "1.2.3.4:6789"
 	c := &Cluster{mapping: &opcontroller.Mapping{}}
-	c.ClusterInfo = &cephclient.ClusterInfo{InternalMonitors: map[string]*cephclient.MonInfo{
+	c.ClusterInfo = &cephclient.ClusterInfo{Monitors: map[string]*cephclient.MonInfo{
 		"a": {Name: "a", Endpoint: endpoint},
 		"b": {Name: "b", Endpoint: endpoint},
 		"c": {Name: "c", Endpoint: endpoint},
@@ -183,7 +182,7 @@ func TestRemoveExtraMon(t *testing.T) {
 		{Name: "y"},
 		{Name: "z"},
 	}}
-	c.ClusterInfo.InternalMonitors["e"] = &cephclient.MonInfo{Name: "e", Endpoint: endpoint}
+	c.ClusterInfo.Monitors["e"] = &cephclient.MonInfo{Name: "e", Endpoint: endpoint}
 	c.mapping.Schedule["a"].Zone = "x"
 	c.mapping.Schedule["b"].Zone = "y"
 	c.mapping.Schedule["c"].Zone = "y"
@@ -219,7 +218,7 @@ func TestTrackMonsOutOfQuorum(t *testing.T) {
 		context:   &clusterd.Context{Clientset: clientset, ConfigDir: tempDir},
 		ownerInfo: ownerInfo,
 		Namespace: "ns"}
-	c.ClusterInfo = &cephclient.ClusterInfo{InternalMonitors: map[string]*cephclient.MonInfo{
+	c.ClusterInfo = &cephclient.ClusterInfo{Monitors: map[string]*cephclient.MonInfo{
 		"a": {Name: "a", Endpoint: endpoint},
 		"b": {Name: "b", Endpoint: endpoint},
 		"c": {Name: "c", Endpoint: endpoint},
@@ -324,7 +323,7 @@ func TestScaleMonDeployment(t *testing.T) {
 
 	name := "a"
 	c.spec.Mon.Count = 3
-	logger.Infof("initial mons: %v", c.ClusterInfo.InternalMonitors[name])
+	logger.Infof("initial mons: %v", c.ClusterInfo.Monitors[name])
 	monConfig := &monConfig{ResourceName: resourceName(name), DaemonName: name, DataPathMap: &config.DataPathMap{}}
 	d, err := c.makeDeployment(monConfig, false)
 	require.NoError(t, err)
@@ -438,7 +437,7 @@ func TestAddRemoveMons(t *testing.T) {
 	// checking the health will increase the mons as desired all in one go
 	err := c.checkHealth(ctx)
 	assert.Nil(t, err)
-	assert.Equal(t, 5, len(c.ClusterInfo.InternalMonitors), fmt.Sprintf("mons: %v", c.ClusterInfo.InternalMonitors))
+	assert.Equal(t, 5, len(c.ClusterInfo.Monitors), fmt.Sprintf("mons: %v", c.ClusterInfo.Monitors))
 	assert.ElementsMatch(t, []string{
 		// b is created first, no updates
 		"rook-ceph-mon-b",                    // b updated when c created
@@ -449,39 +448,39 @@ func TestAddRemoveMons(t *testing.T) {
 	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
 
 	// reducing the mon count to 3 will reduce the mon count once each time we call checkHealth
-	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(c.ClusterInfo.InternalMonitors)
+	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(c.ClusterInfo.Monitors)
 	c.spec.Mon.Count = 3
 	err = c.checkHealth(ctx)
 	assert.Nil(t, err)
-	assert.Equal(t, 4, len(c.ClusterInfo.InternalMonitors))
+	assert.Equal(t, 4, len(c.ClusterInfo.Monitors))
 	// No updates in unit tests w/ workaround
 	assert.ElementsMatch(t, []string{}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
 	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
 
 	// after the second call we will be down to the expected count of 3
-	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(c.ClusterInfo.InternalMonitors)
+	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(c.ClusterInfo.Monitors)
 	err = c.checkHealth(ctx)
 	assert.Nil(t, err)
-	assert.Equal(t, 3, len(c.ClusterInfo.InternalMonitors))
+	assert.Equal(t, 3, len(c.ClusterInfo.Monitors))
 	// No updates in unit tests w/ workaround
 	assert.ElementsMatch(t, []string{}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
 	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
 
 	// now attempt to reduce the mons down to quorum size 1
-	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(c.ClusterInfo.InternalMonitors)
+	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(c.ClusterInfo.Monitors)
 	c.spec.Mon.Count = 1
 	err = c.checkHealth(ctx)
 	assert.Nil(t, err)
-	assert.Equal(t, 2, len(c.ClusterInfo.InternalMonitors))
+	assert.Equal(t, 2, len(c.ClusterInfo.Monitors))
 	// No updates in unit tests w/ workaround
 	assert.ElementsMatch(t, []string{}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
 	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
 
 	// cannot reduce from quorum size of 2 to 1
-	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(c.ClusterInfo.InternalMonitors)
+	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(c.ClusterInfo.Monitors)
 	err = c.checkHealth(ctx)
 	assert.Nil(t, err)
-	assert.Equal(t, 2, len(c.ClusterInfo.InternalMonitors))
+	assert.Equal(t, 2, len(c.ClusterInfo.Monitors))
 	// No updates in unit tests w/ workaround
 	assert.ElementsMatch(t, []string{}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
 	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
@@ -512,7 +511,7 @@ func TestAddOrRemoveExternalMonitor(t *testing.T) {
 	changed, err = c.addOrRemoveExternalMonitor(fakeResp)
 	assert.NoError(t, err)
 	assert.False(t, changed)
-	assert.Equal(t, 1, len(c.ClusterInfo.InternalMonitors))
+	assert.Equal(t, 1, len(c.ClusterInfo.Monitors))
 
 	//
 	// TEST 2
@@ -525,7 +524,7 @@ func TestAddOrRemoveExternalMonitor(t *testing.T) {
 	assert.NoError(t, err)
 	assert.True(t, changed)
 	// ClusterInfo should shrink to 1
-	assert.Equal(t, 1, len(c.ClusterInfo.InternalMonitors))
+	assert.Equal(t, 1, len(c.ClusterInfo.Monitors))
 
 	//
 	// TEST 3
@@ -546,7 +545,7 @@ func TestAddOrRemoveExternalMonitor(t *testing.T) {
 	assert.NoError(t, err)
 	assert.True(t, changed)
 	// ClusterInfo should now have 2 monitors
-	assert.Equal(t, 2, len(c.ClusterInfo.InternalMonitors))
+	assert.Equal(t, 2, len(c.ClusterInfo.Monitors))
 }
 
 func TestNewHealthChecker(t *testing.T) {
@@ -611,522 +610,3 @@ func TestUpdateMonInterval(t *testing.T) {
 		assert.Equal(t, time.Minute, h.interval)
 	})
 }
-
-func Test_removeMonsFromQuorumStatusResponse(t *testing.T) {
-	type args struct {
-		quorumStatus cephclient.MonStatusResponse
-		idsToRemove  []string
-	}
-	tests := []struct {
-		name string
-		args args
-		want cephclient.MonStatusResponse
-	}{
-		{
-			name: "remove one mon",
-			args: args{
-				quorumStatus: cephclient.MonStatusResponse{
-					Quorum: []int{0, 1, 2},
-					MonMap: struct {
-						Mons []cephclient.MonMapEntry `json:"mons"`
-					}{
-						Mons: []cephclient.MonMapEntry{
-							{
-								Name: "a",
-								Rank: 0,
-							},
-							{
-								Name: "b",
-								Rank: 1,
-							},
-							{
-								Name: "c",
-								Rank: 2,
-							},
-						},
-					},
-				},
-				idsToRemove: []string{"b"},
-			},
-			want: cephclient.MonStatusResponse{
-				Quorum: []int{0, 2},
-				MonMap: struct {
-					Mons []cephclient.MonMapEntry `json:"mons"`
-				}{
-					Mons: []cephclient.MonMapEntry{
-						{
-							Name: "a",
-							Rank: 0,
-						},
-						{
-							Name: "c",
-							Rank: 2,
-						},
-					},
-				},
-			},
-		},
-		{
-			name: "not remove if not present",
-			args: args{
-				quorumStatus: cephclient.MonStatusResponse{
-					Quorum: []int{0, 1, 2},
-					MonMap: struct {
-						Mons []cephclient.MonMapEntry `json:"mons"`
-					}{
-						Mons: []cephclient.MonMapEntry{
-							{
-								Name: "a",
-								Rank: 0,
-							},
-							{
-								Name: "b",
-								Rank: 1,
-							},
-							{
-								Name: "c",
-								Rank: 2,
-							},
-						},
-					},
-				},
-				idsToRemove: []string{"e"},
-			},
-			want: cephclient.MonStatusResponse{
-				Quorum: []int{0, 1, 2},
-				MonMap: struct {
-					Mons []cephclient.MonMapEntry `json:"mons"`
-				}{
-					Mons: []cephclient.MonMapEntry{
-						{
-							Name: "a",
-							Rank: 0,
-						},
-						{
-							Name: "b",
-							Rank: 1,
-						},
-						{
-							Name: "c",
-							Rank: 2,
-						},
-					},
-				},
-			},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := removeMonsFromQuorumStatusResponse(tt.args.quorumStatus, tt.args.idsToRemove); !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("removeMonsFromQuorumStatusResponse() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
-func TestExternalMons_notInSpec_InQuorum(t *testing.T) {
-	// 1. setup test
-	ctx := context.TODO()
-	var deploymentsUpdated *[]*apps.Deployment
-	updateDeploymentAndWait, deploymentsUpdated = testopk8s.UpdateDeploymentAndWaitStub()
-
-	monQuorumResponse := clienttest.MonInQuorumResponse()
-	executor := &exectest.MockExecutor{
-		MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
-			logger.Infof("executing command: %s %+v", command, args)
-			if args[0] == "auth" && args[1] == "get-or-create-key" {
-				return "{\"key\":\"mysecurekey\"}", nil
-			}
-			return monQuorumResponse, nil
-		},
-	}
-	clientset := test.New(t, 1)
-	configDir := t.TempDir()
-	context := &clusterd.Context{
-		Clientset: clientset,
-		ConfigDir: configDir,
-		Executor:  executor,
-	}
-	ownerInfo := cephclient.NewMinimumOwnerInfoWithOwnerRef()
-	c := New(ctx, context, "ns", cephv1.ClusterSpec{}, ownerInfo)
-	setCommonMonProperties(c, 0, cephv1.MonSpec{Count: 5, AllowMultiplePerNode: true}, "myversion")
-	c.maxMonID = 0 // "a" is max mon id
-	c.waitForStart = false
-
-	// checking the health will increase the mons as desired all in one go
-	err := c.checkHealth(ctx)
-	assert.Nil(t, err)
-	assert.Equal(t, 5, len(c.ClusterInfo.InternalMonitors), fmt.Sprintf("mons: %v", c.ClusterInfo.InternalMonitors))
-	assert.ElementsMatch(t, []string{
-		// b is created first, no updates
-		"rook-ceph-mon-b",                    // b updated when c created
-		"rook-ceph-mon-b", "rook-ceph-mon-c", // b and c updated when d created
-		"rook-ceph-mon-b", "rook-ceph-mon-c", "rook-ceph-mon-d", // etc.
-		"rook-ceph-mon-b", "rook-ceph-mon-c", "rook-ceph-mon-d", "rook-ceph-mon-e"},
-		testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	inital5Mons := make(map[string]*cephclient.MonInfo)
-	for k, v := range c.ClusterInfo.InternalMonitors {
-		inital5Mons[k] = v
-	}
-
-	// 2. add external mon to quorum but not in spec:
-
-	mons := make(map[string]*cephclient.MonInfo)
-	for k, v := range inital5Mons {
-		mons[k] = v
-	}
-	// add unknown mon to quorum:
-	mons["ext-mon-id"] = &cephclient.MonInfo{Name: "ext-mon-id", Endpoint: "0.0.0.0:6789"}
-	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(mons)
-
-	// internal mons and deployments has not changed
-	// and unknown mon was removed from quorum
-	err = c.checkHealth(ctx)
-	assert.Nil(t, err)
-	assert.Equal(t, 5, len(c.ClusterInfo.InternalMonitors))
-	assert.Empty(t, c.ClusterInfo.ExternalMons)
-
-	// No updates in unit tests w/ workaround
-	assert.ElementsMatch(t, []string{}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	// check that unknown mon is not in endpoint configmap
-	cm, err := c.context.Clientset.CoreV1().ConfigMaps(c.Namespace).Get(ctx, EndpointConfigMapName, metav1.GetOptions{})
-	assert.Nil(t, err)
-	assert.Empty(t, cm.Data[EndpointExternalMonsKey])
-	monsFromCM := controller.ParseMonEndpoints(cm.Data[EndpointDataKey])
-	assert.Len(t, monsFromCM, 5)
-	for id, mon := range monsFromCM {
-		assert.Equal(t, inital5Mons[id].Name, mon.Name)
-		assert.Equal(t, inital5Mons[id].Endpoint, mon.Endpoint)
-		assert.Equal(t, inital5Mons[id].OutOfQuorum, mon.OutOfQuorum)
-	}
-
-	// 3. downscale mons to 4:
-	c.spec.Mon.Count = 4
-	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(c.ClusterInfo.InternalMonitors)
-
-	err = c.checkHealth(ctx)
-	assert.Nil(t, err)
-	// todo fix
-	assert.Equal(t, 4, len(c.ClusterInfo.InternalMonitors))
-	assert.Empty(t, c.ClusterInfo.ExternalMons)
-	// No updates in unit tests w/ workaround
-	assert.ElementsMatch(t, []string{}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	// check that unknown mon is not in endpoint configmap
-	cm, err = c.context.Clientset.CoreV1().ConfigMaps(c.Namespace).Get(ctx, EndpointConfigMapName, metav1.GetOptions{})
-	assert.Nil(t, err)
-	assert.Empty(t, cm.Data[EndpointExternalMonsKey])
-	monsFromCM = controller.ParseMonEndpoints(cm.Data[EndpointDataKey])
-	assert.Len(t, monsFromCM, 4)
-	for id, mon := range monsFromCM {
-		assert.Equal(t, inital5Mons[id].Name, mon.Name)
-		assert.Equal(t, inital5Mons[id].Endpoint, mon.Endpoint)
-		assert.Equal(t, inital5Mons[id].OutOfQuorum, mon.OutOfQuorum)
-	}
-
-	// 4. upscale back to 5:
-	c.spec.Mon.Count = 5
-	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(c.ClusterInfo.InternalMonitors)
-
-	err = c.checkHealth(ctx)
-	assert.Nil(t, err)
-	assert.Equal(t, 5, len(c.ClusterInfo.InternalMonitors))
-	assert.Empty(t, c.ClusterInfo.ExternalMons)
-	// No updates in unit tests w/ workaround
-	assert.Len(t, testopk8s.DeploymentNamesUpdated(deploymentsUpdated), 4)
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	// check that unknown mon is not in endpoint configmap
-	cm, err = c.context.Clientset.CoreV1().ConfigMaps(c.Namespace).Get(ctx, EndpointConfigMapName, metav1.GetOptions{})
-	assert.Nil(t, err)
-	assert.Empty(t, cm.Data[EndpointExternalMonsKey])
-	monsFromCM = controller.ParseMonEndpoints(cm.Data[EndpointDataKey])
-	assert.Len(t, monsFromCM, 5)
-	for id, mon := range monsFromCM {
-		assert.Equal(t, c.ClusterInfo.InternalMonitors[id].Name, mon.Name)
-		assert.Equal(t, c.ClusterInfo.InternalMonitors[id].Endpoint, mon.Endpoint)
-		assert.Equal(t, c.ClusterInfo.InternalMonitors[id].OutOfQuorum, mon.OutOfQuorum)
-	}
-
-}
-
-func TestExternalMons_inSpec_notInQuorum(t *testing.T) {
-	// 1. setup test
-	ctx := context.TODO()
-	var deploymentsUpdated *[]*apps.Deployment
-	updateDeploymentAndWait, deploymentsUpdated = testopk8s.UpdateDeploymentAndWaitStub()
-
-	monQuorumResponse := clienttest.MonInQuorumResponse()
-	executor := &exectest.MockExecutor{
-		MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
-			logger.Infof("executing command: %s %+v", command, args)
-			if args[0] == "auth" && args[1] == "get-or-create-key" {
-				return "{\"key\":\"mysecurekey\"}", nil
-			}
-			return monQuorumResponse, nil
-		},
-	}
-	clientset := test.New(t, 1)
-	configDir := t.TempDir()
-	context := &clusterd.Context{
-		Clientset: clientset,
-		ConfigDir: configDir,
-		Executor:  executor,
-	}
-	ownerInfo := cephclient.NewMinimumOwnerInfoWithOwnerRef()
-	c := New(ctx, context, "ns", cephv1.ClusterSpec{}, ownerInfo)
-	setCommonMonProperties(c, 0, cephv1.MonSpec{Count: 5, AllowMultiplePerNode: true}, "myversion")
-	c.maxMonID = 0 // "a" is max mon id
-	c.waitForStart = false
-
-	// checking the health will increase the mons as desired all in one go
-	err := c.checkHealth(ctx)
-	assert.Nil(t, err)
-	assert.Equal(t, 5, len(c.ClusterInfo.InternalMonitors), fmt.Sprintf("mons: %v", c.ClusterInfo.InternalMonitors))
-	assert.ElementsMatch(t, []string{
-		// b is created first, no updates
-		"rook-ceph-mon-b",                    // b updated when c created
-		"rook-ceph-mon-b", "rook-ceph-mon-c", // b and c updated when d created
-		"rook-ceph-mon-b", "rook-ceph-mon-c", "rook-ceph-mon-d", // etc.
-		"rook-ceph-mon-b", "rook-ceph-mon-c", "rook-ceph-mon-d", "rook-ceph-mon-e"},
-		testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	inital5Mons := make(map[string]*cephclient.MonInfo)
-	for k, v := range c.ClusterInfo.InternalMonitors {
-		inital5Mons[k] = v
-	}
-
-	// 2. add external mon id to spec but not to quorum
-	c.spec.Mon.ExternalMonIDs = []string{"ext-mon-id"}
-
-	// don't add ext mon to quorum:
-	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(inital5Mons)
-
-	err = c.checkHealth(ctx)
-	assert.Nil(t, err)
-	assert.Equal(t, 5, len(c.ClusterInfo.InternalMonitors))
-	assert.Empty(t, c.ClusterInfo.ExternalMons)
-
-	// No updates in unit tests w/ workaround
-	assert.ElementsMatch(t, []string{}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	// check that unknown mon is not in endpoint configmap
-	cm, err := c.context.Clientset.CoreV1().ConfigMaps(c.Namespace).Get(ctx, EndpointConfigMapName, metav1.GetOptions{})
-	assert.Nil(t, err)
-	assert.Empty(t, cm.Data[EndpointExternalMonsKey])
-	monsFromCM := controller.ParseMonEndpoints(cm.Data[EndpointDataKey])
-	assert.Len(t, monsFromCM, 5)
-	for id, mon := range monsFromCM {
-		assert.Equal(t, inital5Mons[id].Name, mon.Name)
-		assert.Equal(t, inital5Mons[id].Endpoint, mon.Endpoint)
-		assert.Equal(t, inital5Mons[id].OutOfQuorum, mon.OutOfQuorum)
-	}
-
-	// 3. downscale mons to 4:
-	c.spec.Mon.Count = 4
-
-	err = c.checkHealth(ctx)
-	assert.Nil(t, err)
-	assert.Equal(t, 4, len(c.ClusterInfo.InternalMonitors))
-	assert.Empty(t, c.ClusterInfo.ExternalMons)
-	// No updates in unit tests w/ workaround
-	assert.ElementsMatch(t, []string{}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	// check that unknown mon is not in endpoint configmap
-	cm, err = c.context.Clientset.CoreV1().ConfigMaps(c.Namespace).Get(ctx, EndpointConfigMapName, metav1.GetOptions{})
-	assert.Nil(t, err)
-	assert.Empty(t, cm.Data[EndpointExternalMonsKey])
-	monsFromCM = controller.ParseMonEndpoints(cm.Data[EndpointDataKey])
-	assert.Len(t, monsFromCM, 4)
-	for id, mon := range monsFromCM {
-		assert.Equal(t, inital5Mons[id].Name, mon.Name)
-		assert.Equal(t, inital5Mons[id].Endpoint, mon.Endpoint)
-		assert.Equal(t, inital5Mons[id].OutOfQuorum, mon.OutOfQuorum)
-	}
-
-	// 4. upscale back to 5:
-	c.spec.Mon.Count = 5
-	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(c.ClusterInfo.InternalMonitors)
-
-	err = c.checkHealth(ctx)
-	assert.Nil(t, err)
-	assert.Equal(t, 5, len(c.ClusterInfo.InternalMonitors))
-	assert.Empty(t, c.ClusterInfo.ExternalMons)
-	// No updates in unit tests w/ workaround
-	assert.Len(t, testopk8s.DeploymentNamesUpdated(deploymentsUpdated), 4)
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	// check that unknown mon is not in endpoint configmap
-	cm, err = c.context.Clientset.CoreV1().ConfigMaps(c.Namespace).Get(ctx, EndpointConfigMapName, metav1.GetOptions{})
-	assert.Nil(t, err)
-	assert.Empty(t, cm.Data[EndpointExternalMonsKey])
-	monsFromCM = controller.ParseMonEndpoints(cm.Data[EndpointDataKey])
-	assert.Len(t, monsFromCM, 5)
-	for id, mon := range monsFromCM {
-		assert.Equal(t, c.ClusterInfo.InternalMonitors[id].Name, mon.Name)
-		assert.Equal(t, c.ClusterInfo.InternalMonitors[id].Endpoint, mon.Endpoint)
-		assert.Equal(t, c.ClusterInfo.InternalMonitors[id].OutOfQuorum, mon.OutOfQuorum)
-	}
-
-}
-
-func TestExternalMons_inSpec_inQuorum(t *testing.T) {
-	// 1. setup test
-	ctx := context.TODO()
-	var deploymentsUpdated *[]*apps.Deployment
-	updateDeploymentAndWait, deploymentsUpdated = testopk8s.UpdateDeploymentAndWaitStub()
-
-	monQuorumResponse := clienttest.MonInQuorumResponse()
-	executor := &exectest.MockExecutor{
-		MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
-			logger.Infof("executing command: %s %+v", command, args)
-			if args[0] == "auth" && args[1] == "get-or-create-key" {
-				return "{\"key\":\"mysecurekey\"}", nil
-			}
-			return monQuorumResponse, nil
-		},
-	}
-	clientset := test.New(t, 1)
-	configDir := t.TempDir()
-	context := &clusterd.Context{
-		Clientset: clientset,
-		ConfigDir: configDir,
-		Executor:  executor,
-	}
-	ownerInfo := cephclient.NewMinimumOwnerInfoWithOwnerRef()
-	c := New(ctx, context, "ns", cephv1.ClusterSpec{}, ownerInfo)
-	setCommonMonProperties(c, 0, cephv1.MonSpec{Count: 5, AllowMultiplePerNode: true}, "myversion")
-	c.maxMonID = 0 // "a" is max mon id
-	c.waitForStart = false
-
-	// checking the health will increase the mons as desired all in one go
-	err := c.checkHealth(ctx)
-	assert.Nil(t, err)
-	assert.Equal(t, 5, len(c.ClusterInfo.InternalMonitors), fmt.Sprintf("mons: %v", c.ClusterInfo.InternalMonitors))
-	assert.ElementsMatch(t, []string{
-		// b is created first, no updates
-		"rook-ceph-mon-b",                    // b updated when c created
-		"rook-ceph-mon-b", "rook-ceph-mon-c", // b and c updated when d created
-		"rook-ceph-mon-b", "rook-ceph-mon-c", "rook-ceph-mon-d", // etc.
-		"rook-ceph-mon-b", "rook-ceph-mon-c", "rook-ceph-mon-d", "rook-ceph-mon-e"},
-		testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	inital5Mons := make(map[string]*cephclient.MonInfo)
-	for k, v := range c.ClusterInfo.InternalMonitors {
-		inital5Mons[k] = v
-	}
-
-	// 2. add external mon id to spec
-	c.spec.Mon.ExternalMonIDs = []string{"ext-mon-id"}
-
-	// add ext mon to quorum:
-	mons := make(map[string]*cephclient.MonInfo)
-	for k, v := range inital5Mons {
-		mons[k] = v
-	}
-	mons["ext-mon-id"] = &cephclient.MonInfo{Name: "ext-mon-id", Endpoint: "0.0.0.0:6789"}
-	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(mons)
-
-	// internal mons and deployments has not changed
-	err = c.checkHealth(ctx)
-	assert.Nil(t, err)
-	assert.Equal(t, 5, len(c.ClusterInfo.InternalMonitors))
-	// external mon is in quorum
-	assert.Len(t, c.ClusterInfo.ExternalMons, 1)
-	assert.Equal(t, "ext-mon-id", c.ClusterInfo.ExternalMons["ext-mon-id"].Name)
-
-	// No updates in unit tests w/ workaround
-	assert.ElementsMatch(t, []string{}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	// check that ext mon is in endpoint configmap
-	cm, err := c.context.Clientset.CoreV1().ConfigMaps(c.Namespace).Get(ctx, EndpointConfigMapName, metav1.GetOptions{})
-	assert.Nil(t, err)
-	assert.Equal(t, "ext-mon-id", cm.Data[EndpointExternalMonsKey])
-	monsFromCM := controller.ParseMonEndpoints(cm.Data[EndpointDataKey])
-	assert.Len(t, monsFromCM, 6)
-	for id, mon := range monsFromCM {
-		if id == "ext-mon-id" {
-			assert.Equal(t, c.ClusterInfo.ExternalMons[id].Name, mon.Name)
-			assert.Equal(t, c.ClusterInfo.ExternalMons[id].Endpoint, mon.Endpoint)
-			assert.Equal(t, c.ClusterInfo.ExternalMons[id].OutOfQuorum, mon.OutOfQuorum)
-		} else {
-			assert.Equal(t, inital5Mons[id].Name, mon.Name)
-			assert.Equal(t, inital5Mons[id].Endpoint, mon.Endpoint)
-			assert.Equal(t, inital5Mons[id].OutOfQuorum, mon.OutOfQuorum)
-		}
-	}
-
-	// 3. downscale mons to 4:
-	c.spec.Mon.Count = 4
-
-	err = c.checkHealth(ctx)
-	assert.Nil(t, err)
-	assert.Equal(t, 4, len(c.ClusterInfo.InternalMonitors))
-	assert.Len(t, c.ClusterInfo.ExternalMons, 1)
-	// No updates in unit tests w/ workaround
-	assert.ElementsMatch(t, []string{}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	// check that unknown mon is not in endpoint configmap
-	cm, err = c.context.Clientset.CoreV1().ConfigMaps(c.Namespace).Get(ctx, EndpointConfigMapName, metav1.GetOptions{})
-	assert.Nil(t, err)
-	assert.Equal(t, "ext-mon-id", cm.Data[EndpointExternalMonsKey])
-	monsFromCM = controller.ParseMonEndpoints(cm.Data[EndpointDataKey])
-	assert.Len(t, monsFromCM, 5)
-	for id, mon := range monsFromCM {
-		if id == "ext-mon-id" {
-			assert.Equal(t, c.ClusterInfo.ExternalMons[id].Name, mon.Name)
-			assert.Equal(t, c.ClusterInfo.ExternalMons[id].Endpoint, mon.Endpoint)
-			assert.Equal(t, c.ClusterInfo.ExternalMons[id].OutOfQuorum, mon.OutOfQuorum)
-		} else {
-			assert.Equal(t, inital5Mons[id].Name, mon.Name)
-			assert.Equal(t, inital5Mons[id].Endpoint, mon.Endpoint)
-			assert.Equal(t, inital5Mons[id].OutOfQuorum, mon.OutOfQuorum)
-		}
-	}
-
-	// 4. upscale back to 5:
-	c.spec.Mon.Count = 5
-	mons = make(map[string]*cephclient.MonInfo)
-	for k, v := range c.ClusterInfo.InternalMonitors {
-		mons[k] = v
-	}
-	mons["ext-mon-id"] = &cephclient.MonInfo{Name: "ext-mon-id", Endpoint: "0.0.0.0:6789"}
-	monQuorumResponse = clienttest.MonInQuorumResponseFromMons(mons)
-
-	err = c.checkHealth(ctx)
-	assert.Nil(t, err)
-	assert.Equal(t, 5, len(c.ClusterInfo.InternalMonitors))
-	assert.Len(t, c.ClusterInfo.ExternalMons, 1)
-	// No updates in unit tests w/ workaround
-	assert.Len(t, testopk8s.DeploymentNamesUpdated(deploymentsUpdated), 4)
-	testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
-
-	// check that unknown mon is not in endpoint configmap
-	cm, err = c.context.Clientset.CoreV1().ConfigMaps(c.Namespace).Get(ctx, EndpointConfigMapName, metav1.GetOptions{})
-	assert.Nil(t, err)
-	assert.Equal(t, "ext-mon-id", cm.Data[EndpointExternalMonsKey])
-	monsFromCM = controller.ParseMonEndpoints(cm.Data[EndpointDataKey])
-	assert.Len(t, monsFromCM, 6)
-	for id, mon := range monsFromCM {
-		if id == "ext-mon-id" {
-			assert.Equal(t, c.ClusterInfo.ExternalMons[id].Name, mon.Name)
-			assert.Equal(t, c.ClusterInfo.ExternalMons[id].Endpoint, mon.Endpoint)
-			assert.Equal(t, c.ClusterInfo.ExternalMons[id].OutOfQuorum, mon.OutOfQuorum)
-		} else {
-			assert.Equal(t, c.ClusterInfo.InternalMonitors[id].Name, mon.Name)
-			assert.Equal(t, c.ClusterInfo.InternalMonitors[id].Endpoint, mon.Endpoint)
-			assert.Equal(t, c.ClusterInfo.InternalMonitors[id].OutOfQuorum, mon.OutOfQuorum)
-		}
-	}
-}
diff --git a/pkg/operator/ceph/cluster/mon/mon_test.go b/pkg/operator/ceph/cluster/mon/mon_test.go
index a481ec4d7..b4c5e0f5b 100644
--- a/pkg/operator/ceph/cluster/mon/mon_test.go
+++ b/pkg/operator/ceph/cluster/mon/mon_test.go
@@ -288,8 +288,8 @@ func TestPersistMons(t *testing.T) {
 	assert.Equal(t, map[string]string{"key": "value"}, cm.Annotations)
 
 	// Persist mon b, and remove mon a for simply testing the configmap is updated
-	c.ClusterInfo.InternalMonitors["b"] = &cephclient.MonInfo{Name: "b", Endpoint: "4.5.6.7:3300"}
-	delete(c.ClusterInfo.InternalMonitors, "a")
+	c.ClusterInfo.Monitors["b"] = &cephclient.MonInfo{Name: "b", Endpoint: "4.5.6.7:3300"}
+	delete(c.ClusterInfo.Monitors, "a")
 	err = c.persistExpectedMonDaemons()
 	assert.NoError(t, err)
 
@@ -317,7 +317,7 @@ func TestSaveMonEndpoints(t *testing.T) {
 	assert.Equal(t, "-1", cm.Data[opcontroller.MaxMonIDKey])
 
 	// update the config map
-	c.ClusterInfo.InternalMonitors["a"].Endpoint = "2.3.4.5:6789"
+	c.ClusterInfo.Monitors["a"].Endpoint = "2.3.4.5:6789"
 	c.maxMonID = 2
 	c.mapping.Schedule["a"] = &opcontroller.MonScheduleInfo{
 		Name:     "node0",
diff --git a/pkg/operator/ceph/config/store_test.go b/pkg/operator/ceph/config/store_test.go
index e9a2962b7..902a72e4c 100644
--- a/pkg/operator/ceph/config/store_test.go
+++ b/pkg/operator/ceph/config/store_test.go
@@ -45,25 +45,25 @@ func TestStore(t *testing.T) {
 		sec, e := clientset.CoreV1().Secrets(ns).Get(ctxt, StoreName, metav1.GetOptions{})
 		assert.NoError(t, e)
 		mh := strings.Split(sec.StringData["mon_host"], ",") // list of mon ip:port pairs in cluster
-		expectedEndpoints := len(ci.InternalMonitors)
+		expectedEndpoints := len(ci.Monitors)
 		if mon1EndpointsEnabled {
 			expectedEndpoints *= 2
 		}
-		assert.Equal(t, expectedEndpoints, len(mh), ci.InternalMonitors["a"].Endpoint) // we need to pass x2 since we split on "," above and that returns msgr1 and msgr2 addresses
-		mim := strings.Split(sec.StringData["mon_initial_members"], ",")               // list of mon ids in cluster
-		assert.Equal(t, len(ci.InternalMonitors), len(mim))
+		assert.Equal(t, expectedEndpoints, len(mh), ci.Monitors["a"].Endpoint) // we need to pass x2 since we split on "," above and that returns msgr1 and msgr2 addresses
+		mim := strings.Split(sec.StringData["mon_initial_members"], ",")       // list of mon ids in cluster
+		assert.Equal(t, len(ci.Monitors), len(mim))
 		// make sure every mon has its id/ip:port in mon_initial_members/mon_host
 		for _, id := range mim {
 			// cannot use "assert.Contains(t, mh, ci.Monitors[id].Endpoint)"
 			// it looks like the value is not found but if present, it might be confused by the brackets
 			contains := false
 			for _, c := range mh {
-				if strings.Contains(c, ci.InternalMonitors[id].Endpoint) {
+				if strings.Contains(c, ci.Monitors[id].Endpoint) {
 					contains = true
 				}
 			}
 			assert.True(t, contains)
-			assert.Contains(t, mim, ci.InternalMonitors[id].Name)
+			assert.Contains(t, mim, ci.Monitors[id].Name)
 		}
 	}
 
@@ -80,14 +80,14 @@ func TestStore(t *testing.T) {
 
 	// Now run the same test for v1 endpoints
 	mon1EndpointsEnabled = true
-	for _, mon := range i1.InternalMonitors {
+	for _, mon := range i1.Monitors {
 		mon.Endpoint = "1.2.3.4:6789"
 	}
 	err = s.CreateOrUpdate(i1)
 	assert.NoError(t, err)
 	assertConfigStore(i1)
 
-	for _, mon := range i3.InternalMonitors {
+	for _, mon := range i3.Monitors {
 		mon.Endpoint = "1.2.3.4:6789"
 	}
 	err = s.CreateOrUpdate(i3)
diff --git a/pkg/operator/ceph/controller/controller_utils_test.go b/pkg/operator/ceph/controller/controller_utils_test.go
index 6725c6e22..cda45d991 100644
--- a/pkg/operator/ceph/controller/controller_utils_test.go
+++ b/pkg/operator/ceph/controller/controller_utils_test.go
@@ -18,7 +18,6 @@ package controller
 
 import (
 	ctx "context"
-	"os"
 	"testing"
 	"time"
 
@@ -46,11 +45,7 @@ func TestCanIgnoreHealthErrStatusInReconcile(t *testing.T) {
 	var cluster = CreateTestClusterFromStatusDetails(map[string]cephv1.CephHealthMessage{
 		"MDS_ALL_DOWN": {
 			Severity: "HEALTH_ERR",
-			Message:  "mds all down error",
-		},
-		"MGR_MODULE_ERROR": {
-			Severity: "HEALTH_ERR",
-			Message:  "mgr module error",
+			Message:  "MDS_ALL_DOWN",
 		},
 		"TEST_OTHER": {
 			Severity: "HEALTH_WARN",
@@ -63,22 +58,6 @@ func TestCanIgnoreHealthErrStatusInReconcile(t *testing.T) {
 	})
 	assert.True(t, canIgnoreHealthErrStatusInReconcile(cluster, "controller"))
 
-	cluster = CreateTestClusterFromStatusDetails(map[string]cephv1.CephHealthMessage{
-		"MGR_MODULE_ERROR": {
-			Severity: "HEALTH_ERR",
-			Message:  "mgr module error",
-		},
-	})
-	assert.True(t, canIgnoreHealthErrStatusInReconcile(cluster, "controller"))
-
-	cluster = CreateTestClusterFromStatusDetails(map[string]cephv1.CephHealthMessage{
-		"MGR_MODULE_ERROR_FALSE": {
-			Severity: "HEALTH_ERR",
-			Message:  "mgr module error",
-		},
-	})
-	assert.False(t, canIgnoreHealthErrStatusInReconcile(cluster, "controller"))
-
 	cluster = CreateTestClusterFromStatusDetails(map[string]cephv1.CephHealthMessage{
 		"MDS_ALL_DOWN": {
 			Severity: "HEALTH_ERR",
@@ -98,101 +77,86 @@ func TestCanIgnoreHealthErrStatusInReconcile(t *testing.T) {
 		},
 	})
 	assert.False(t, canIgnoreHealthErrStatusInReconcile(cluster, "controller"))
-
-	// The empty status should return false
-	cluster = CreateTestClusterFromStatusDetails(map[string]cephv1.CephHealthMessage{})
-	assert.False(t, canIgnoreHealthErrStatusInReconcile(cluster, "controller"))
 }
 
 func TestSetCephCommandsTimeout(t *testing.T) {
-	defer os.Unsetenv("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS")
-
-	SetCephCommandsTimeout()
+	SetCephCommandsTimeout(map[string]string{})
 	assert.Equal(t, 15*time.Second, exec.CephCommandsTimeout)
 
 	exec.CephCommandsTimeout = 0
-	os.Setenv("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS", "0")
-	SetCephCommandsTimeout()
+	SetCephCommandsTimeout(map[string]string{"ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS": "0"})
 	assert.Equal(t, 15*time.Second, exec.CephCommandsTimeout)
 
 	exec.CephCommandsTimeout = 0
-	os.Setenv("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS", "1")
-	SetCephCommandsTimeout()
+	SetCephCommandsTimeout(map[string]string{"ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS": "1"})
 	assert.Equal(t, 1*time.Second, exec.CephCommandsTimeout)
 }
 
 func TestSetAllowLoopDevices(t *testing.T) {
-	defer os.Unsetenv("ROOK_CEPH_ALLOW_LOOP_DEVICES")
-
-	SetAllowLoopDevices()
+	SetAllowLoopDevices(map[string]string{})
 	assert.False(t, LoopDevicesAllowed())
 
-	os.Setenv("ROOK_CEPH_ALLOW_LOOP_DEVICES", "foo")
-	SetAllowLoopDevices()
+	SetAllowLoopDevices(map[string]string{"ROOK_CEPH_ALLOW_LOOP_DEVICES": "foo"})
 	assert.False(t, LoopDevicesAllowed())
 
-	os.Setenv("ROOK_CEPH_ALLOW_LOOP_DEVICES", "false")
-	SetAllowLoopDevices()
+	SetAllowLoopDevices(map[string]string{"ROOK_CEPH_ALLOW_LOOP_DEVICES": "false"})
 	assert.False(t, LoopDevicesAllowed())
 
-	os.Setenv("ROOK_CEPH_ALLOW_LOOP_DEVICES", "true")
-	SetAllowLoopDevices()
+	SetAllowLoopDevices(map[string]string{"ROOK_CEPH_ALLOW_LOOP_DEVICES": "true"})
 	assert.True(t, LoopDevicesAllowed())
 }
 
 func TestSetEnforceHostNetwork(t *testing.T) {
-	defer os.Unsetenv(enforceHostNetworkSettingName)
-
 	logger.Infof("testing default value for %v", enforceHostNetworkSettingName)
-	SetEnforceHostNetwork()
+	opConfig := map[string]string{}
+	SetEnforceHostNetwork(opConfig)
 	assert.False(t, EnforceHostNetwork())
 
 	// test invalid setting
 	var value string = "foo"
 	logger.Infof("testing invalid value'%v' for %v", value, enforceHostNetworkSettingName)
-	os.Setenv(enforceHostNetworkSettingName, value)
-	SetEnforceHostNetwork()
+	opConfig[enforceHostNetworkSettingName] = value
+	SetEnforceHostNetwork(opConfig)
 	assert.False(t, EnforceHostNetwork())
 
 	// test valid settings
 	value = "true"
 	logger.Infof("testing valid value'%v' for %v", value, enforceHostNetworkSettingName)
-	os.Setenv(enforceHostNetworkSettingName, value)
-	SetEnforceHostNetwork()
+	opConfig[enforceHostNetworkSettingName] = value
+	SetEnforceHostNetwork(opConfig)
 	assert.True(t, EnforceHostNetwork())
 
 	value = "false"
 	logger.Infof("testing valid value'%v' for %v", value, enforceHostNetworkSettingName)
-	os.Setenv(enforceHostNetworkSettingName, value)
-	SetEnforceHostNetwork()
+	opConfig[enforceHostNetworkSettingName] = value
+	SetEnforceHostNetwork(opConfig)
 	assert.False(t, EnforceHostNetwork())
 }
 
 func TestSetRevisionHistoryLimit(t *testing.T) {
-	defer os.Unsetenv(revisionHistoryLimitSettingName)
-
+	opConfig := map[string]string{}
 	t.Run("ROOK_REVISION_HISTORY_LIMIT: test default value", func(t *testing.T) {
-		SetRevisionHistoryLimit()
+		SetRevisionHistoryLimit(opConfig)
 		assert.Nil(t, RevisionHistoryLimit())
 	})
 
 	var value string = "foo"
 	t.Run("ROOK_REVISION_HISTORY_LIMIT: test invalid value 'foo'", func(t *testing.T) {
-		os.Setenv(revisionHistoryLimitSettingName, value)
-		SetRevisionHistoryLimit()
+		opConfig[revisionHistoryLimitSettingName] = value
+		SetRevisionHistoryLimit(opConfig)
 		assert.Nil(t, RevisionHistoryLimit())
 	})
 
 	t.Run("ROOK_REVISION_HISTORY_LIMIT: test empty string value", func(t *testing.T) {
 		value = ""
-		os.Setenv(revisionHistoryLimitSettingName, value)
-		SetRevisionHistoryLimit()
+		opConfig[revisionHistoryLimitSettingName] = value
+		SetRevisionHistoryLimit(opConfig)
 		assert.Nil(t, RevisionHistoryLimit())
 	})
 	t.Run("ROOK_REVISION_HISTORY_LIMIT:  test valig value '10'", func(t *testing.T) {
 		value = "10"
-		os.Setenv(revisionHistoryLimitSettingName, value)
-		SetRevisionHistoryLimit()
+		opConfig[revisionHistoryLimitSettingName] = value
+		SetRevisionHistoryLimit(opConfig)
 		assert.Equal(t, int32(10), *RevisionHistoryLimit())
 	})
 }
@@ -285,39 +249,36 @@ func TestIsReadyToReconcile(t *testing.T) {
 
 func TestObcAllowAdditionalConfigFields(t *testing.T) {
 	tests := []struct {
-		name             string
-		additionalConfig string
-		shouldAllow      []string
-		shouldDisallow   []string
+		name           string
+		data           map[string]string
+		shouldAllow    []string
+		shouldDisallow []string
 	}{
-		{"not set", "<notset>",
+		{"not set", map[string]string{},
 			[]string{"maxObjects", "maxSize"}, // default allowlist is unlikely to need changing EVER
 			[]string{"bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner", "random"}},
-		{"set to empty", "",
+		{"set to empty", map[string]string{"ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS": ""},
 			[]string{}, // admin can allow no quota options if desired
 			[]string{"maxObjects", "maxSize", "bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner", "random"}},
-		{"set to default", "maxObjects,maxSize",
+		{"set to default", map[string]string{"ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS": "maxObjects,maxSize"},
 			[]string{"maxObjects", "maxSize"},
 			[]string{"bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner", "random"}},
-		{"all quota fields", "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize",
+		{"all quota fields", map[string]string{"ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS": "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize"},
 			[]string{"maxObjects", "maxSize", "bucketMaxObjects", "bucketMaxSize"},
 			[]string{"bucketPolicy", "bucketLifecycle", "bucketOwner", "random"}},
-		{"all fields", "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize,bucketPolicy,bucketLifecycle,bucketOwner",
+		{"all fields", map[string]string{"ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS": "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize,bucketPolicy,bucketLifecycle,bucketOwner"},
 			[]string{"maxObjects", "maxSize", "bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner"},
 			[]string{"random"}},
 		// this mechanism doesn't do any field checking - that isn't it's job - it merely handles
 		// allow-listing essentially arbitrary config keys
-		{"all fields including unknown", "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize,bucketPolicy,bucketLifecycle,bucketOwner,random",
+		{"all fields including unknown", map[string]string{"ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS": "maxObjects,maxSize,bucketMaxObjects,bucketMaxSize,bucketPolicy,bucketLifecycle,bucketOwner,random"},
 			[]string{"maxObjects", "maxSize", "bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner", "random"},
 			[]string{"otherRandom"}},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			if tt.additionalConfig != "<notset>" {
-				os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", tt.additionalConfig)
-				defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-			}
-			SetObcAllowAdditionalConfigFields()
+			SetObcAllowAdditionalConfigFields(tt.data)
+
 			for _, toAllow := range tt.shouldAllow {
 				assert.True(t, ObcAdditionalConfigKeyIsAllowed(toAllow))
 			}
diff --git a/pkg/operator/ceph/controller_test.go b/pkg/operator/ceph/controller_test.go
index a9f08b672..ed4485611 100644
--- a/pkg/operator/ceph/controller_test.go
+++ b/pkg/operator/ceph/controller_test.go
@@ -23,6 +23,7 @@ import (
 	"time"
 
 	"github.com/coreos/pkg/capnslog"
+	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
 	rookclient "github.com/rook/rook/pkg/client/clientset/versioned/fake"
 	"github.com/rook/rook/pkg/client/clientset/versioned/scheme"
 	"github.com/rook/rook/pkg/clusterd"
@@ -30,7 +31,9 @@ import (
 	"github.com/rook/rook/pkg/operator/test"
 	"github.com/rook/rook/pkg/util/exec"
 	"github.com/stretchr/testify/assert"
+	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
 	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -61,6 +64,7 @@ func TestOperatorController(t *testing.T) {
 
 		// Register operator types with the runtime scheme.
 		s := scheme.Scheme
+		s.AddKnownTypes(cephv1.SchemeGroupVersion, &v1.ConfigMap{}, &v1.ConfigMapList{})
 
 		// Create a fake client to mock API calls.
 		cl := fake.NewClientBuilder().WithScheme(s).Build()
@@ -91,6 +95,7 @@ func TestOperatorController(t *testing.T) {
 
 		// Register operator types with the runtime scheme.
 		s := scheme.Scheme
+		s.AddKnownTypes(cephv1.SchemeGroupVersion, &v1.ConfigMap{}, &v1.ConfigMapList{})
 
 		// Create a fake client to mock API calls.
 		cl := fake.NewClientBuilder().WithScheme(s).Build()
@@ -123,12 +128,23 @@ func TestOperatorController(t *testing.T) {
 		// Register operator types with the runtime scheme.
 		// s := scheme.Scheme
 		s := clientgoscheme.Scheme
+		// s.AddKnownTypes(cephv1.SchemeGroupVersion, &v1.ConfigMap{})
 
-		os.Setenv("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS", "20")
-		defer os.Unsetenv("ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS")
+		opConfigCM := &v1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      controller.OperatorSettingConfigMapName,
+				Namespace: namespace,
+			},
+			Data: map[string]string{
+				"ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS": "20",
+			},
+		}
 
+		object := []runtime.Object{
+			opConfigCM,
+		}
 		// Create a fake client to mock API calls.
-		cl := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects().Build()
+		cl := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(object...).Build()
 
 		// Create a ReconcileCSI object with the scheme and fake client.
 		r := &ReconcileConfig{
@@ -157,6 +173,7 @@ func TestOperatorController(t *testing.T) {
 
 		// Register operator types with the runtime scheme.
 		s := scheme.Scheme
+		s.AddKnownTypes(cephv1.SchemeGroupVersion, &v1.ConfigMap{}, &v1.ConfigMapList{})
 
 		// Create a fake client to mock API calls.
 		cl := fake.NewClientBuilder().WithScheme(s).Build()
@@ -192,12 +209,23 @@ func TestOperatorController(t *testing.T) {
 		// Register operator types with the runtime scheme.
 		// s := scheme.Scheme
 		s := clientgoscheme.Scheme
+		// s.AddKnownTypes(cephv1.SchemeGroupVersion, &v1.ConfigMap{})
 
-		os.Setenv("ROOK_CEPH_ALLOW_LOOP_DEVICES", "true")
-		defer os.Unsetenv("ROOK_CEPH_ALLOW_LOOP_DEVICES")
+		opConfigCM := &v1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      controller.OperatorSettingConfigMapName,
+				Namespace: namespace,
+			},
+			Data: map[string]string{
+				"ROOK_CEPH_ALLOW_LOOP_DEVICES": "true",
+			},
+		}
 
+		object := []runtime.Object{
+			opConfigCM,
+		}
 		// Create a fake client to mock API calls.
-		cl := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects().Build()
+		cl := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(object...).Build()
 
 		// Create a ReconcileCSI object with the scheme and fake client.
 		r := &ReconcileConfig{
diff --git a/pkg/operator/ceph/csi/util_test.go b/pkg/operator/ceph/csi/util_test.go
index 74860696e..29f5cbced 100644
--- a/pkg/operator/ceph/csi/util_test.go
+++ b/pkg/operator/ceph/csi/util_test.go
@@ -47,27 +47,28 @@ func TestDeploymentTemplate(t *testing.T) {
 func TestGetPortFromConfig(t *testing.T) {
 	key := "TEST_CSI_PORT_ENV"
 	var defaultPort uint16 = 8000
+	data := map[string]string{}
 
 	// empty env variable
-	port, err := getPortFromConfig(key, defaultPort)
+	port, err := getPortFromConfig(data, key, defaultPort)
 	assert.Nil(t, err)
 	assert.Equal(t, port, defaultPort)
 
 	// valid port is set in env
 	t.Setenv(key, "9000")
-	port, err = getPortFromConfig(key, defaultPort)
+	port, err = getPortFromConfig(data, key, defaultPort)
 	assert.Nil(t, err)
 	assert.Equal(t, port, uint16(9000))
 
 	// higher port value is set in env
 	t.Setenv(key, "65536")
-	port, err = getPortFromConfig(key, defaultPort)
+	port, err = getPortFromConfig(data, key, defaultPort)
 	assert.Error(t, err)
 	assert.Equal(t, port, defaultPort)
 
 	// negative port is set in env
 	t.Setenv(key, "-1")
-	port, err = getPortFromConfig(key, defaultPort)
+	port, err = getPortFromConfig(data, key, defaultPort)
 	assert.Error(t, err)
 	assert.Equal(t, port, defaultPort)
 
@@ -79,6 +80,7 @@ func TestApplyingResourcesToRBDPlugin(t *testing.T) {
 	tp := templateParam{}
 	rbdPlugin, err := templateToDaemonSet("rbdplugin", RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
+	params := make(map[string]string)
 
 	// need to build using map[string]interface{} because the following resource
 	// doesn't serialise nicely
@@ -101,9 +103,8 @@ func TestApplyingResourcesToRBDPlugin(t *testing.T) {
 
 	resourceRaw, err := yaml.Marshal(resource)
 	assert.Nil(t, err)
-	os.Setenv(rbdPluginResource, string(resourceRaw))
-	defer os.Unsetenv(rbdPluginResource)
-	applyResourcesToContainers(rbdPluginResource, &rbdPlugin.Spec.Template.Spec)
+	params[rbdPluginResource] = string(resourceRaw)
+	applyResourcesToContainers(params, rbdPluginResource, &rbdPlugin.Spec.Template.Spec)
 	assert.Equal(t, rbdPlugin.Spec.Template.Spec.Containers[0].Resources.Requests.Memory().String(), "128Mi")
 	assert.Equal(t, rbdPlugin.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().String(), "256Mi")
 	assert.Equal(t, rbdPlugin.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu().String(), "100m")
@@ -112,6 +113,7 @@ func TestApplyingResourcesToRBDPlugin(t *testing.T) {
 
 func Test_applyVolumeToPodSpec(t *testing.T) {
 	// when no volumes specified
+	config := make(map[string]string)
 	configKey := "TEST_CSI_PLUGIN_VOLUME"
 	dsName := "test-ds"
 	tp := templateParam{
@@ -122,7 +124,7 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	defaultVolumes := 11
 	ds, err := templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes)
 
@@ -130,7 +132,7 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	tp.CSILogRotation = true
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes+2)
 	tp.CSILogRotation = false
 
@@ -143,11 +145,10 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	}
 	volumeRaw, err := yaml.Marshal(volumes)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeRaw))
-	defer os.Unsetenv(configKey)
+	config[configKey] = string(volumeRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes+1)
 	// add one more volume
@@ -157,10 +158,10 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	})
 	volumeRaw, err = yaml.Marshal(volumes)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeRaw))
+	config[configKey] = string(volumeRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes+2)
 	// override existing volume configuration
 	volumes[1].VolumeSource = corev1.VolumeSource{
@@ -168,10 +169,10 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	}
 	volumeRaw, err = yaml.Marshal(volumes)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeRaw))
+	config[configKey] = string(volumeRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes+2)
 	// remove existing volume configuration
 	volumes = []corev1.Volume{
@@ -182,15 +183,16 @@ func Test_applyVolumeToPodSpec(t *testing.T) {
 	}
 	volumeRaw, err = yaml.Marshal(volumes)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeRaw))
+	config[configKey] = string(volumeRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeToPodSpec(configKey, &ds.Spec.Template.Spec)
+	applyVolumeToPodSpec(config, configKey, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Volumes, defaultVolumes+1)
 }
 
 func Test_applyVolumeMountToContainer(t *testing.T) {
 	// when no volumes specified
+	config := make(map[string]string)
 	configKey := "TEST_CSI_PLUGIN_VOLUME_MOUNT"
 	dsName := "test-ds"
 	rbdContainerName := "csi-rbdplugin"
@@ -202,7 +204,7 @@ func Test_applyVolumeMountToContainer(t *testing.T) {
 	defaultVolumes := 10
 	ds, err := templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes)
 
@@ -210,7 +212,7 @@ func Test_applyVolumeMountToContainer(t *testing.T) {
 	tp.CSILogRotation = true
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes+1)
 	tp.CSILogRotation = false
 
@@ -223,11 +225,10 @@ func Test_applyVolumeMountToContainer(t *testing.T) {
 	}
 	volumeMountsRaw, err := yaml.Marshal(volumeMounts)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeMountsRaw))
-	defer os.Unsetenv(configKey)
+	config[configKey] = string(volumeMountsRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes+1)
 	// add one more volumemount
@@ -237,19 +238,19 @@ func Test_applyVolumeMountToContainer(t *testing.T) {
 	})
 	volumeMountsRaw, err = yaml.Marshal(volumeMounts)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeMountsRaw))
+	config[configKey] = string(volumeMountsRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes+2)
 	// override existing volume configuration
 	volumeMounts[1].MountPath = "/run/test/run"
 	volumeMountsRaw, err = yaml.Marshal(volumeMounts)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeMountsRaw))
+	config[configKey] = string(volumeMountsRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes+2)
 	// remove existing volume configuration
 	volumeMounts = []corev1.VolumeMount{
@@ -260,16 +261,17 @@ func Test_applyVolumeMountToContainer(t *testing.T) {
 	}
 	volumeMountsRaw, err = yaml.Marshal(volumeMounts)
 	assert.Nil(t, err)
-	os.Setenv(configKey, string(volumeMountsRaw))
+	config[configKey] = string(volumeMountsRaw)
 	ds, err = templateToDaemonSet(dsName, RBDPluginTemplatePath, tp)
 	assert.Nil(t, err)
-	applyVolumeMountToContainer(configKey, rbdContainerName, &ds.Spec.Template.Spec)
+	applyVolumeMountToContainer(config, configKey, rbdContainerName, &ds.Spec.Template.Spec)
 	assert.Len(t, ds.Spec.Template.Spec.Containers[1].VolumeMounts, defaultVolumes+1)
 }
 
 func Test_getImage(t *testing.T) {
 	type args struct {
-		userImage    string
+		data         map[string]string
+		settingName  string
 		defaultImage string
 	}
 	tests := []struct {
@@ -280,6 +282,8 @@ func Test_getImage(t *testing.T) {
 		{
 			name: "test with default image",
 			args: args{
+				data:         map[string]string{},
+				settingName:  "ROOK_CSI_CEPH_IMAGE",
 				defaultImage: "quay.io/cephcsi/cephcsi:v3.13.0",
 			},
 			want: DefaultCSIPluginImage,
@@ -287,7 +291,10 @@ func Test_getImage(t *testing.T) {
 		{
 			name: "test with user image",
 			args: args{
-				userImage:    "registry.io/private/cephcsi:v8",
+				data: map[string]string{
+					"ROOK_CSI_CEPH_IMAGE": "registry.io/private/cephcsi:v8",
+				},
+				settingName:  "ROOK_CSI_CEPH_IMAGE",
 				defaultImage: "quay.io/cephcsi/cephcsi:v3.11.0",
 			},
 			want: "registry.io/private/cephcsi:v8",
@@ -295,7 +302,10 @@ func Test_getImage(t *testing.T) {
 		{
 			name: "test with user image without version",
 			args: args{
-				userImage:    "registry.io/private/cephcsi",
+				data: map[string]string{
+					"ROOK_CSI_CEPH_IMAGE": "registry.io/private/cephcsi",
+				},
+				settingName:  "ROOK_CSI_CEPH_IMAGE",
 				defaultImage: "quay.io/cephcsi/cephcsi:v3.11.0",
 			},
 			want: "registry.io/private/cephcsi:v3.11.0",
@@ -303,11 +313,7 @@ func Test_getImage(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			if tt.args.userImage != "" {
-				os.Setenv("ROOK_CSI_CEPH_IMAGE", tt.args.userImage)
-				defer os.Unsetenv("ROOK_CSI_CEPH_IMAGE")
-			}
-			got := getImage("ROOK_CSI_CEPH_IMAGE", tt.args.defaultImage)
+			got := getImage(tt.args.data, tt.args.settingName, tt.args.defaultImage)
 			assert.Equal(t, tt.want, got)
 		})
 	}
diff --git a/pkg/operator/ceph/file/controller_test.go b/pkg/operator/ceph/file/controller_test.go
index 8f1700b35..293d370ab 100644
--- a/pkg/operator/ceph/file/controller_test.go
+++ b/pkg/operator/ceph/file/controller_test.go
@@ -254,7 +254,6 @@ func TestCephFilesystemController(t *testing.T) {
 			recorder:         record.NewFakeRecorder(5),
 			scheme:           s,
 			context:          c,
-			fsContexts:       make(map[string]*fsHealth),
 			opManagerContext: context.TODO(),
 		}
 		res, err := r.Reconcile(ctx, req)
diff --git a/pkg/operator/ceph/object/bucket/provisioner_test.go b/pkg/operator/ceph/object/bucket/provisioner_test.go
index 0e44132a4..a098b51f9 100644
--- a/pkg/operator/ceph/object/bucket/provisioner_test.go
+++ b/pkg/operator/ceph/object/bucket/provisioner_test.go
@@ -22,7 +22,6 @@ import (
 	"fmt"
 	"io"
 	"net/http"
-	"os"
 	"strings"
 	"testing"
 
@@ -502,7 +501,7 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("maxObjects field should be set", func(t *testing.T) {
-		opcontroller.SetObcAllowAdditionalConfigFields()
+		opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{})
 
 		spec, err := additionalConfigSpecFromMap(map[string]string{"maxObjects": "2"})
 		assert.NoError(t, err)
@@ -511,7 +510,7 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("maxSize field should be set", func(t *testing.T) {
-		opcontroller.SetObcAllowAdditionalConfigFields()
+		opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{})
 
 		spec, err := additionalConfigSpecFromMap(map[string]string{"maxSize": "3"})
 		assert.NoError(t, err)
@@ -520,10 +519,8 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("bucketMaxObjects field should be set", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", "bucketMaxObjects")
-		defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-		opcontroller.SetObcAllowAdditionalConfigFields()
-		defer opcontroller.SetObcAllowAdditionalConfigFields()
+		opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{"ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS": "bucketMaxObjects"})
+		defer opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{})
 
 		spec, err := additionalConfigSpecFromMap(map[string]string{"bucketMaxObjects": "4"})
 		assert.NoError(t, err)
@@ -531,10 +528,8 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("bucketMaxSize field should be set", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", "bucketMaxSize")
-		defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-		opcontroller.SetObcAllowAdditionalConfigFields()
-		defer opcontroller.SetObcAllowAdditionalConfigFields()
+		opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{"ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS": "bucketMaxSize"})
+		defer opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{})
 
 		spec, err := additionalConfigSpecFromMap(map[string]string{"bucketMaxSize": "5"})
 		assert.NoError(t, err)
@@ -542,10 +537,8 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("bucketPolicy field should be set", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", "bucketPolicy")
-		defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-		opcontroller.SetObcAllowAdditionalConfigFields()
-		defer opcontroller.SetObcAllowAdditionalConfigFields()
+		opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{"ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS": "bucketPolicy"})
+		defer opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{})
 
 		spec, err := additionalConfigSpecFromMap(map[string]string{"bucketPolicy": "foo"})
 		assert.NoError(t, err)
@@ -553,10 +546,8 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("bucketLifecycle field should be set", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", "bucketLifecycle")
-		defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-		opcontroller.SetObcAllowAdditionalConfigFields()
-		defer opcontroller.SetObcAllowAdditionalConfigFields()
+		opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{"ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS": "bucketLifecycle"})
+		defer opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{})
 
 		spec, err := additionalConfigSpecFromMap(map[string]string{"bucketLifecycle": "foo"})
 		assert.NoError(t, err)
@@ -564,10 +555,8 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("bucketOwner field should be set", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS", "bucketOwner")
-		defer os.Unsetenv("ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS")
-		opcontroller.SetObcAllowAdditionalConfigFields()
-		defer opcontroller.SetObcAllowAdditionalConfigFields()
+		opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{"ROOK_OBC_ALLOW_ADDITIONAL_CONFIG_FIELDS": "bucketOwner"})
+		defer opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{})
 
 		spec, err := additionalConfigSpecFromMap(map[string]string{"bucketOwner": "foo"})
 		assert.NoError(t, err)
@@ -575,7 +564,7 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("fields disallowed by default", func(t *testing.T) {
-		opcontroller.SetObcAllowAdditionalConfigFields()
+		opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{})
 
 		for _, configKey := range []string{"bucketMaxObjects", "bucketMaxSize", "bucketPolicy", "bucketLifecycle", "bucketOwner"} {
 			_, err := additionalConfigSpecFromMap(map[string]string{configKey: "foo"})
@@ -584,7 +573,7 @@ func TestProvisioner_additionalConfigSpecFromMap(t *testing.T) {
 	})
 
 	t.Run("does not fail on empty map", func(t *testing.T) {
-		opcontroller.SetObcAllowAdditionalConfigFields()
+		opcontroller.SetObcAllowAdditionalConfigFields(map[string]string{})
 
 		spec, err := additionalConfigSpecFromMap(map[string]string{})
 		assert.NoError(t, err)
diff --git a/pkg/operator/ceph/object/config_test.go b/pkg/operator/ceph/object/config_test.go
index 21ae599c6..f146ce617 100644
--- a/pkg/operator/ceph/object/config_test.go
+++ b/pkg/operator/ceph/object/config_test.go
@@ -17,7 +17,6 @@ limitations under the License.
 package object
 
 import (
-	"context"
 	"testing"
 
 	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
@@ -26,8 +25,6 @@ import (
 	cephver "github.com/rook/rook/pkg/operator/ceph/version"
 	"github.com/rook/rook/pkg/operator/test"
 	"github.com/stretchr/testify/assert"
-	v1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 func newConfig(t *testing.T) *clusterConfig {
@@ -185,49 +182,3 @@ func Test_clusterConfig_generateMonConfigOptions(t *testing.T) {
 		})
 	}
 }
-
-func TestRgwConfigFromSecret(t *testing.T) {
-	objContext := &Context{
-		Context: &clusterd.Context{
-			Clientset: test.New(t, 3),
-		},
-		clusterInfo: cephclient.AdminTestClusterInfo("rook-ceph"),
-	}
-
-	objectStore := simpleStore()
-	objectStore.Spec.Gateway.RgwConfigFromSecret = map[string]v1.SecretKeySelector{
-		"rgw_secret_conf_name": {
-			LocalObjectReference: v1.LocalObjectReference{
-				Name: "my-secret",
-			},
-			Key: "secKey",
-		},
-	}
-
-	c := &clusterConfig{
-		store:       objectStore,
-		context:     objContext.Context,
-		clusterInfo: objContext.clusterInfo,
-	}
-
-	rgwConfig := &rgwConfig{}
-
-	_, err := c.generateMonConfigOptions(rgwConfig)
-	assert.Error(t, err, "error if secret not exists")
-
-	s := &v1.Secret{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "my-secret",
-			Namespace: "rook-ceph",
-		},
-		Data: map[string][]byte{
-			"secKey": []byte("secVal"),
-		},
-	}
-	_, err = objContext.Context.Clientset.CoreV1().Secrets(objContext.clusterInfo.Namespace).Create(context.TODO(), s, metav1.CreateOptions{})
-	assert.NoError(t, err, "create secret")
-
-	got, err := c.generateMonConfigOptions(rgwConfig)
-	assert.NoError(t, err)
-	assert.EqualValues(t, "secVal", got["rgw_secret_conf_name"])
-}
diff --git a/pkg/operator/ceph/object/objectstore_test.go b/pkg/operator/ceph/object/objectstore_test.go
index 8359e9897..f384d6c70 100644
--- a/pkg/operator/ceph/object/objectstore_test.go
+++ b/pkg/operator/ceph/object/objectstore_test.go
@@ -517,43 +517,36 @@ func TestGetObjectBucketProvisioner(t *testing.T) {
 	t.Setenv(k8sutil.PodNamespaceEnvVar, testNamespace)
 
 	t.Run("watch ceph cluster namespace", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE", "true")
-		defer os.Unsetenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE")
-		bktprovisioner, err := GetObjectBucketProvisioner(testNamespace)
+		data := map[string]string{"ROOK_OBC_WATCH_OPERATOR_NAMESPACE": "true"}
+		bktprovisioner, err := GetObjectBucketProvisioner(data, testNamespace)
 		assert.Equal(t, fmt.Sprintf("%s.%s", testNamespace, bucketProvisionerName), bktprovisioner)
 		assert.NoError(t, err)
 	})
 
 	t.Run("watch all namespaces", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE", "false")
-		defer os.Unsetenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE")
-		bktprovisioner, err := GetObjectBucketProvisioner(testNamespace)
+		data := map[string]string{"ROOK_OBC_WATCH_OPERATOR_NAMESPACE": "false"}
+		bktprovisioner, err := GetObjectBucketProvisioner(data, testNamespace)
 		assert.Equal(t, bucketProvisionerName, bktprovisioner)
 		assert.NoError(t, err)
 	})
 
 	t.Run("prefix object provisioner", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_PROVISIONER_NAME_PREFIX", "my-prefix")
-		defer os.Unsetenv("ROOK_OBC_PROVISIONER_NAME_PREFIX")
-		bktprovisioner, err := GetObjectBucketProvisioner(testNamespace)
+		data := map[string]string{"ROOK_OBC_PROVISIONER_NAME_PREFIX": "my-prefix"}
+		bktprovisioner, err := GetObjectBucketProvisioner(data, testNamespace)
 		assert.Equal(t, "my-prefix."+bucketProvisionerName, bktprovisioner)
 		assert.NoError(t, err)
 	})
 
 	t.Run("watch ceph cluster namespace and prefix object provisioner", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE", "true")
-		os.Setenv("ROOK_OBC_PROVISIONER_NAME_PREFIX", "my-prefix")
-		defer os.Unsetenv("ROOK_OBC_WATCH_OPERATOR_NAMESPACE")
-		defer os.Unsetenv("ROOK_OBC_PROVISIONER_NAME_PREFIX")
-		bktprovisioner, err := GetObjectBucketProvisioner(testNamespace)
+		data := map[string]string{"ROOK_OBC_WATCH_OPERATOR_NAMESPACE": "true", "ROOK_OBC_PROVISIONER_NAME_PREFIX": "my-prefix"}
+		bktprovisioner, err := GetObjectBucketProvisioner(data, testNamespace)
 		assert.Equal(t, "my-prefix."+bucketProvisionerName, bktprovisioner)
 		assert.NoError(t, err)
 	})
 
 	t.Run("invalid prefix value for object provisioner", func(t *testing.T) {
-		os.Setenv("ROOK_OBC_PROVISIONER_NAME_PREFIX", "my-prefix.")
-		defer os.Unsetenv("ROOK_OBC_PROVISIONER_NAME_PREFIX")
-		_, err := GetObjectBucketProvisioner(testNamespace)
+		data := map[string]string{"ROOK_OBC_PROVISIONER_NAME_PREFIX": "my-prefix."}
+		_, err := GetObjectBucketProvisioner(data, testNamespace)
 		assert.Error(t, err)
 	})
 
diff --git a/pkg/operator/discover/discover_test.go b/pkg/operator/discover/discover_test.go
index ec19cc16e..667e88bdc 100644
--- a/pkg/operator/discover/discover_test.go
+++ b/pkg/operator/discover/discover_test.go
@@ -19,12 +19,12 @@ package discover
 
 import (
 	"context"
-	"os"
 	"testing"
 
 	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
 	"github.com/rook/rook/pkg/clusterd"
 	discoverDaemon "github.com/rook/rook/pkg/daemon/discover"
+	"github.com/rook/rook/pkg/operator/ceph/controller"
 	"github.com/rook/rook/pkg/operator/k8sutil"
 	"github.com/rook/rook/pkg/operator/test"
 
@@ -63,7 +63,7 @@ func TestStartDiscoveryDaemonset(t *testing.T) {
 	_, err := clientset.CoreV1().Pods("rook-system").Create(ctx, &pod, metav1.CreateOptions{})
 	assert.NoError(t, err)
 	// start a basic cluster
-	err = a.Start(ctx, namespace, "rook/rook:myversion", "mysa", false)
+	err = a.Start(ctx, namespace, "rook/rook:myversion", "mysa", map[string]string{}, false)
 	assert.Nil(t, err)
 
 	// check daemonset parameters
@@ -86,11 +86,21 @@ func TestStartDiscoveryDaemonset(t *testing.T) {
 	assert.Nil(t, agentDS.Spec.Template.Spec.Tolerations)
 
 	// Test with rook override configmap setting
-	os.Setenv("DISCOVER_TOLERATIONS", "- effect: NoSchedule\n  key: node-role.kubernetes.io/control-plane\n  operator: Exists\n- effect: NoExecute\n  key: node-role.kubernetes.io/etcd\n  operator: Exists")
-	defer os.Unsetenv("DISCOVER_TOLERATIONS")
+	opConfigCM := &v1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      controller.OperatorSettingConfigMapName,
+			Namespace: namespace,
+		},
+		Data: map[string]string{
+			"DISCOVER_TOLERATIONS": "- effect: NoSchedule\n  key: node-role.kubernetes.io/control-plane\n  operator: Exists\n- effect: NoExecute\n  key: node-role.kubernetes.io/etcd\n  operator: Exists",
+		},
+	}
+
+	cm, err := clientset.CoreV1().ConfigMaps(namespace).Create(ctx, opConfigCM, metav1.CreateOptions{})
+	assert.NoError(t, err)
 
 	// start a basic cluster
-	err = a.Start(ctx, namespace, "rook/rook:myversion", "mysa", false)
+	err = a.Start(ctx, namespace, "rook/rook:myversion", "mysa", cm.Data, false)
 	assert.Nil(t, err)
 
 	agentDS, err = clientset.AppsV1().DaemonSets(namespace).Get(ctx, "rook-discover", metav1.GetOptions{})
diff --git a/pkg/operator/k8sutil/configmap_test.go b/pkg/operator/k8sutil/configmap_test.go
index d4577b0fc..cc03f1fcc 100644
--- a/pkg/operator/k8sutil/configmap_test.go
+++ b/pkg/operator/k8sutil/configmap_test.go
@@ -63,7 +63,7 @@ func TestGetOperatorSetting(t *testing.T) {
 	k8s := fake.NewSimpleClientset()
 	ctx := context.TODO()
 
-	operatorSettingConfigMapName := "rook-ceph-operator-config"
+	operatorSettingConfigMapName := "rook-operator-config"
 	testNamespace := "test-namespace"
 	cm := &v1.ConfigMap{
 		ObjectMeta: metav1.ObjectMeta{
@@ -81,41 +81,63 @@ func TestGetOperatorSetting(t *testing.T) {
 	cmSettingValue := "storage=rook, worker"
 	defaultValue := ""
 
-	// ConfigMap is not yet applied so we expect the default value
-	setting := GetOperatorSetting(nodeAffinity, defaultValue)
-	assert.Equal(t, defaultValue, setting)
+	// ConfigMap is not found
+	setting, err := GetOperatorSetting(ctx, k8s, operatorSettingConfigMapName, nodeAffinity, defaultValue)
+	assert.NoError(t, err)
 
+	// Env Var doesn't exist
+	assert.Equal(t, defaultValue, setting)
 	// Env Var exists
 	t.Setenv(nodeAffinity, envSettingValue)
-	setting = GetOperatorSetting(nodeAffinity, defaultValue)
+	setting, err = GetOperatorSetting(ctx, k8s, operatorSettingConfigMapName, nodeAffinity, defaultValue)
+	assert.NoError(t, err)
 	assert.Equal(t, envSettingValue, setting)
 
 	// ConfigMap is found
 	t.Setenv("POD_NAMESPACE", testNamespace)
-	_, err := k8s.CoreV1().ConfigMaps(testNamespace).Create(ctx, cm, metav1.CreateOptions{})
-	assert.NoError(t, err)
-	err = ApplyOperatorSettingsConfigmap(ctx, k8s)
+	_, err = k8s.CoreV1().ConfigMaps(testNamespace).Create(ctx, cm, metav1.CreateOptions{})
 	assert.NoError(t, err)
 
 	// Setting exists in ConfigMap
-	setting = GetOperatorSetting(nodeAffinity, defaultValue)
+	setting, err = GetOperatorSetting(ctx, k8s, operatorSettingConfigMapName, nodeAffinity, defaultValue)
+	assert.NoError(t, err)
+	// Env Var exists
 	assert.Equal(t, cmSettingValue, setting)
-
 	// Env Var doesn't exist
 	err = os.Unsetenv(nodeAffinity)
 	assert.NoError(t, err)
 	assert.Equal(t, cmSettingValue, setting)
 
-	// Setting doesn't exist in ConfigMap, expect the default value
-	setting = GetOperatorSetting(podAffinity, defaultValue)
+	// Setting doesn't exist in ConfigMap
+	setting, err = GetOperatorSetting(ctx, k8s, operatorSettingConfigMapName, podAffinity, defaultValue)
+	assert.NoError(t, err)
+	// Env Var doesn't exist
 	assert.Equal(t, defaultValue, setting)
 	// Env Var exists
 	t.Setenv(podAffinity, envSettingValue)
-	setting = GetOperatorSetting(podAffinity, defaultValue)
+	setting, err = GetOperatorSetting(ctx, k8s, operatorSettingConfigMapName, podAffinity, defaultValue)
 	assert.NoError(t, err)
 	assert.Equal(t, envSettingValue, setting)
 }
 
+func TestLogChangedSettings(t *testing.T) {
+	key := "Node_Affinity"
+	value := "storage=rook, worker"
+
+	// When the key is not in the map, it should be added and return true for logging
+	ok := logChangedSettings(key, value)
+	assert.True(t, true, ok)
+
+	// When the key is already present in the map and value is same, it should return false for logging
+	ok = logChangedSettings(key, value)
+	assert.False(t, false, ok)
+
+	// When the key is already present in the map and value is updated, it should return true for logging
+	value = "storage=rook, test"
+	ok = logChangedSettings(key, value)
+	assert.True(t, true, ok)
+}
+
 func TestCreateOrUpdateConfigMap(t *testing.T) {
 	k8s := fake.NewSimpleClientset()
 	ctx := context.TODO()
diff --git a/tests/scripts/github-action-helper.sh b/tests/scripts/github-action-helper.sh
index a9f2f4759..c2a303993 100755
--- a/tests/scripts/github-action-helper.sh
+++ b/tests/scripts/github-action-helper.sh
@@ -273,8 +273,8 @@ function deploy_toolbox() {
 }
 
 function replace_ceph_image() {
-  local file="$1"                                # parameter 1: the file in which to replace the ceph image
-  local ceph_image="${2?ceph_image is required}" # parameter 2: the new ceph image to use
+  local file="$1"  # parameter 1: the file in which to replace the ceph image
+  local ceph_image="${2?ceph_image is required}"  # parameter 2: the new ceph image to use
 
   # check for ceph_image being an empty string
   if [ -z "$ceph_image" ]; then
@@ -562,7 +562,7 @@ function get_secret_key() {
 }
 
 function s3cmd() {
-  command timeout 150 s3cmd -v --config=s3cfg --access_key="${S3CMD_ACCESS_KEY}" --secret_key="${S3CMD_SECRET_KEY}" "$@"
+  command timeout 20 s3cmd -v --config=s3cfg --access_key="${S3CMD_ACCESS_KEY}" --secret_key="${S3CMD_SECRET_KEY}" "$@"
 }
 
 function write_object_read_from_replica_cluster() {
@@ -572,7 +572,6 @@ function write_object_read_from_replica_cluster() {
 
   local test_object_name="${test_bucket_name}-1mib-test.dat"
   fallocate -l 1M "$test_object_name"
-
   # ensure that test file has unique data
   echo "$test_object_name" >>"$test_object_name"
 
@@ -782,7 +781,7 @@ function test_object_separate_pools() {
     done
     if [[ "$found" == false ]]; then
       echo "Live pool $l is not an expected pool"
-      errors=$((errors + 1))
+      errors=$((errors+1))
     fi
   done
 
diff --git a/tests/scripts/pythonwebserver/server.py b/tests/scripts/pythonwebserver/server.py
index 6f0960310..eb2c841b0 100644
--- a/tests/scripts/pythonwebserver/server.py
+++ b/tests/scripts/pythonwebserver/server.py
@@ -5,7 +5,7 @@ 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
+	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,