Repository: FoundationDB/fdb-kubernetes-operator
Pull request #1121 (2022-03-16 19:06:18)

Improve our logging setup
# Description

Fixes: https://github.com/FoundationDB/fdb-kubernetes-operator/issues/1113
Fixes: https://github.com/FoundationDB/fdb-kubernetes-operator/issues/1112

## Type of change

*Please select one of the options below.*

- Bug fix (non-breaking change which fixes an issue)

# Discussion

-

# Testing

Local testing.

# Documentation

-

# Follow-up

-



Patch:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index f8eaee86..f806d26a 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -12,18 +12,18 @@
 - Documentation
 - Other
 
-# Discussion
+## Discussion
 
 *Are there any design details that you would like to discuss further?*
 
-# Testing
+## Testing
 
 *Please describe the tests that you ran to verify your changes. Unit tests?
 Manual testing?*
 
 *Do we need to perform additional testing once this is merged, or perform in a larger testing environment?*
 
-# Documentation
+## Documentation
 
 *Did you update relevant documentation within this repository?*
 
@@ -33,7 +33,7 @@ Manual testing?*
 
 *If this change is adding new safety checks or new potential failure modes, have we documented and how to debug potential issues?*
 
-# Follow-up
+## Follow-up
 
 *Are there any follow-up issues that we should pursue in the future?*
 
diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
index c2c06213..87bb373d 100644
--- a/.github/workflows/pull_request.yml
+++ b/.github/workflows/pull_request.yml
@@ -15,6 +15,10 @@ jobs:
     steps:
       - name: Checkout code
         uses: actions/checkout@v2
+      - name: Set up Go
+        uses: actions/setup-go@v1
+        with:
+          go-version: 1.17.6
       - uses: actions/cache@v2
         with:
           path: ~/go/pkg/mod
@@ -26,9 +30,7 @@ jobs:
       - name: Install dependencies
         run: sudo dpkg -i fdb.deb
       - name: Run golangci-lint
-        uses: golangci/golangci-lint-action@v2
-        with:
-          version: v1.42
+        run: make fmt lint
   build:
     name: Build
     runs-on: ubuntu-latest
@@ -46,6 +48,10 @@ jobs:
         go-version: 1.17.6
     - name: Check out code into the Go module directory
       uses: actions/checkout@v2
+      with:
+        fetch-depth: 0
+    - name: Fetch all tags
+      run: git fetch --force --tags
     - uses: actions/cache@v2
       with:
         path: ~/go/pkg/mod
@@ -54,29 +60,14 @@ jobs:
           ${{ runner.os }}-go
     - name: Get dependencies
       env:
-        KUBEBUILDER_VER: "2.3.2"
-        KUSTOMIZE_VER: "v3.9.4"
         KIND_VER: "v0.11.1"
       run: |
         # Only download all dependencies
-        go get -v -t -d ./...
-        curl --fail -sL "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/${KUSTOMIZE_VER}/kustomize_${KUSTOMIZE_VER}_linux_amd64.tar.gz" -o kustomize.tar.gz
-        os=$(go env GOOS)
-        arch=$(go env GOARCH)
-        curl --fail -L "https://github.com/kubernetes-sigs/kubebuilder/releases/download/v${KUBEBUILDER_VER}/kubebuilder_${KUBEBUILDER_VER}_${os}_${arch}.tar.gz" -o kubebuilder.tar.gz
         curl --fail -L "https://github.com/apple/foundationdb/releases/download/${{ matrix.fdbver }}/foundationdb-clients_${{ matrix.fdbver }}-1_amd64.deb" -o fdb.deb
         curl -Lo kind https://kind.sigs.k8s.io/dl/${KIND_VER}/kind-linux-amd64
     - name: Install dependencies
-      env:
-        KUBEBUILDER_VER: "2.3.2"
       run: |
         # Install dependencies either from cache or freshly downloaded
-        tar xvfz kustomize.tar.gz
-        sudo mv kustomize /usr/local/bin/
-        tar -xz -C /tmp/ -f kubebuilder.tar.gz
-        os=$(go env GOOS)
-        arch=$(go env GOARCH)
-        sudo mv "/tmp/kubebuilder_${KUBEBUILDER_VER}_${os}_${arch}" /usr/local/kubebuilder
         sudo dpkg -i fdb.deb
         # Install Kind and start a local Kubernetes cluster
         chmod +x kind
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 880bb9ca..bc256a5c 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -31,63 +31,23 @@ jobs:
           draft: false
           prerelease: false
   release-plugin:
-    runs-on: ${{ matrix.os }}
-    strategy:
-      matrix:
-        os:
-          - ubuntu-latest
-          - macOS-latest
+    runs-on: ubuntu-latest
     needs: create-release
     steps:
-      - uses: actions/checkout@v2
-      - name: Set package name
-        id: set_package_name
-        run: echo ::set-output name=package_name::kubectl-fdb-${{ needs.create-release.outputs.tag }}-$(echo ${RUNNER_OS} | tr [:upper:] [:lower:])
+      - name: Check out code into the Go module directory
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+      - name: Fetch all tags
+        run: git fetch --force --tags
       - name: Set up Go
         uses: actions/setup-go@v1
         with:
           go-version: 1.17.6
-      - name: Get FDB client
-        if: runner.os != 'Windows'
-        env:
-          FDB_VER: "6.2.29"
-        run: |
-          go get -v -t -d ./...
-          if [[ "${RUNNER_OS}" == "macOS" ]];then
-              curl --fail -L "https://github.com/apple/foundationdb/releases/download/${FDB_VER}/FoundationDB-${FDB_VER}.pkg" -o fdb.pkg
-              sudo installer -allowUntrusted -verbose -pkg ./fdb.pkg -target /
-              # required later for calculating the sha256
-              brew install coreutils
-          else
-              curl --fail -L "https://github.com/apple/foundationdb/releases/download/${FDB_VER}/foundationdb-clients_${FDB_VER}-1_amd64.deb" -o fdb.deb
-              sudo dpkg -i fdb.deb
-          fi
-      - name: Build
-        run: TAG=${{ needs.create-release.outputs.tag }} make plugin package
-      - name: Test binary
-        run: ./bin/kubectl-fdb version --client-only
-      - name: Create sha256 for assert
-        run: |
-          cp ./bin/kubectl-fdb ${{ steps.set_package_name.outputs.package_name }}
-          sha256sum ${{ steps.set_package_name.outputs.package_name }} > ./bin/${{ steps.set_package_name.outputs.package_name }}.sha256
-      - name: Upload Release asset
-        uses: actions/upload-release-asset@v1
+      - name: Release binaries
+        run: make release
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url: ${{ needs.create-release.outputs.upload_url }}
-          asset_path: ./bin/kubectl-fdb
-          asset_name: ${{ steps.set_package_name.outputs.package_name }}
-          asset_content_type: application/octet-stream
-      - name: Upload sha256 for asset
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url: ${{ needs.create-release.outputs.upload_url }}
-          asset_path: ./bin/${{ steps.set_package_name.outputs.package_name }}.sha256
-          asset_name: ${{ steps.set_package_name.outputs.package_name }}.sha256
-          asset_content_type: text/plain
   push_images:
     name: Push Docker images
     needs: create-release
diff --git a/.gitignore b/.gitignore
index 768ca689..7fd15474 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,5 @@ config/default/manager_image_patch.yaml-e
 
 # General temp directory
 tmp/
+
+dist/
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
new file mode 100644
index 00000000..33bb1930
--- /dev/null
+++ b/.goreleaser.yaml
@@ -0,0 +1,36 @@
+before:
+  hooks:
+    - go mod tidy
+builds:
+  - id: kubectl-fdb
+    env:
+      - CGO_ENABLED=0
+    goos:
+      - linux
+      - windows
+      - darwin
+    main: ./kubectl-fdb
+    binary: kubectl-fdb
+    ldflags:
+      - -s -w -X github.com/FoundationDB/fdb-kubernetes-operator/kubectl-fdb/cmd.pluginVersion={{.Version}}
+    goarch:
+      - amd64
+      - arm64
+archives:
+  - id: kubectl-fdb
+    builds:
+    - kubectl-fdb
+    format: binary
+    name_template: "{{ .Binary }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}"
+    replacements:
+      darwin: Darwin
+      linux: Linux
+      windows: Windows
+      amd64: x86_64
+checksum:
+  name_template: 'checksums.txt'
+snapshot:
+  name_template: "{{ incpatch .Version }}-next"
+# TODO(johscheuer): evaluate if we want to use this feature
+changelog:
+  skip: true
diff --git a/Makefile b/Makefile
index 46b16960..ac6da2c6 100644
--- a/Makefile
+++ b/Makefile
@@ -31,19 +31,20 @@ CONTROLLER_GEN_PKG?=sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.1
 CONTROLLER_GEN=$(GOBIN)/controller-gen
 KUSTOMIZE_PKG?=sigs.k8s.io/kustomize/kustomize/v3@v3.9.4
 KUSTOMIZE=$(GOBIN)/kustomize
-YQ_PKG?=github.com/mikefarah/yq/v4@v4.13.5
-YQ=$(GOBIN)/yq
 GOLANGCI_LINT_PKG=github.com/golangci/golangci-lint/cmd/golangci-lint@v1.42.1
 GOLANGCI_LINT=$(GOBIN)/golangci-lint
+GORELEASER_PKG=github.com/goreleaser/goreleaser@v1.6.3
+GORELEASER=$(GOBIN)/goreleaser
 BUILD_DEPS?=
 BUILDER?="docker"
+GINKGO_PARALLEL=8
 
 define godep
 BUILD_DEPS+=$(1)
 $($2):
 	(export TMP_DIR=$$$$(mktemp -d) ;\
 	cd $$$$TMP_DIR ;\
-	go get $$($2_PKG) ;\
+	go install $$($2_PKG) ;\
 	cd - ;\
 	rm -rf $$$$TMP_DIR )
 $(1): $$($2)
@@ -52,7 +53,7 @@ endef
 $(eval $(call godep,controller-gen,CONTROLLER_GEN))
 $(eval $(call godep,golangci-lint,GOLANGCI_LINT))
 $(eval $(call godep,kustomize,KUSTOMIZE))
-$(eval $(call godep,yq,YQ))
+$(eval $(call godep,goreleaser,GORELEASER))
 
 GO_SRC=$(shell find . -name "*.go" -not -name "zz_generated.*.go")
 GENERATED_GO=api/v1beta2/zz_generated.deepcopy.go
@@ -64,7 +65,7 @@ ifeq "$(TEST_RACE_CONDITIONS)" "1"
 	go_test_flags := $(go_test_flags) -race -timeout=30m
 endif
 
-all: deps generate fmt vet manager plugin manifests samples documentation test_if_changed
+all: deps generate fmt vet manager snapshot manifests samples documentation test_if_changed
 
 .PHONY: clean all manager samples documentation run install uninstall deploy manifests fmt vet generate container-build container-push rebuild-operator bounce lint
 
@@ -77,22 +78,20 @@ clean:
 	rm -r bin
 	rm -f $(SAMPLES)
 	rm -f config/rbac/role.yaml
+	rm -rf ./dist/*
 	find . -name "cover.out" -delete
 
 clean-deps:
-	@rm $(CONTROLLER_GEN) $(KUSTOMIZE) $(KUBEBUILDER) $(GOLANGCI_LINT)
-
-# Run tests
-test:
-ifneq "$(SKIP_TEST)" "1"
-	go test ${go_test_flags} ./... -coverprofile cover.out
-endif
+	@rm $(CONTROLLER_GEN) $(KUSTOMIZE) $(GOLANGCI_LINT) $(GORELEASER)
 
 test_if_changed: cover.out
 
+# Run tests
 cover.out: ${GO_ALL} ${MANIFESTS}
+
+test:
 ifneq "$(SKIP_TEST)" "1"
-	go test ${go_test_flags} ./... -coverprofile cover.out -tags test
+	go test ${go_test_flags} ./... -coverprofile cover.out --ginkgo.parallel.total $(GINKGO_PARALLEL)
 endif
 
 # Build manager binary
@@ -101,18 +100,13 @@ manager: bin/manager
 bin/manager: ${GO_SRC}
 	go build -ldflags="-s -w -X github.com/FoundationDB/fdb-kubernetes-operator/setup.operatorVersion=${TAG}" -o bin/manager main.go
 
-# package the plugin
-package: plugin bin/kubectl-fdb.tar.gz
-
-bin/kubectl-fdb.tar.gz:
-	cp ./kubectl-fdb/Readme.md ./bin
-	(cd ./bin; tar cfvz ./kubectl-fdb.tar.gz ./kubectl-fdb ./Readme.md)
-
 # Build kubectl-fdb binary
 plugin: bin/kubectl-fdb
 
-bin/kubectl-fdb: ${GO_SRC}
-	go build -ldflags="-s -w -X github.com/FoundationDB/fdb-kubernetes-operator/kubectl-fdb/cmd.pluginVersion=${TAG}" -o bin/kubectl-fdb ./kubectl-fdb
+bin/kubectl-fdb: ${GO_SRC} $(GORELEASER)
+	$(GORELEASER) build --single-target --skip-validate --rm-dist
+	@mkdir -p bin
+	@touch $@
 
 # Run against the configured Kubernetes cluster in ~/.kube/config
 run: generate manifests
@@ -145,16 +139,16 @@ fmt: bin/fmt_check
 
 bin/fmt_check: ${GO_ALL}
 	gofmt -w -s .
-	mkdir -p bin
-	@touch bin/fmt_check
+	@mkdir -p bin
+	@touch $@
 
 # Run go vet against code
 vet: bin/vet_check
 
 bin/vet_check: ${GO_ALL}
 	go vet ./...
-	mkdir -p bin
-	@touch bin/vet_check
+	@mkdir -p bin
+	@touch $@
 
 # Generate code
 generate: ${GENERATED_GO}
@@ -196,16 +190,38 @@ config/samples/client.yaml: config/tests/client/*.yaml
 bin/po-docgen: cmd/po-docgen/*.go
 	go build -o bin/po-docgen cmd/po-docgen/main.go  cmd/po-docgen/api.go
 
-docs/cluster_spec.md: bin/po-docgen api/v1beta2/foundationdbcluster_types.go
-	bin/po-docgen api api/v1beta2/foundationdbcluster_types.go > docs/cluster_spec.md
+CLUSTER_DOCS_INPUT=api/v1beta2/foundationdbcluster_types.go api/v1beta2/foundationdb_custom_parameter.go api/v1beta2/foundationdb_database_configuration.go api/v1beta2/foundationdb_process_class.go
+
+docs/cluster_spec.md: bin/po-docgen $(CLUSTER_DOCS_INPUT)
+	bin/po-docgen api $(CLUSTER_DOCS_INPUT) > $@
 
 docs/backup_spec.md: bin/po-docgen api/v1beta2/foundationdbbackup_types.go
-	bin/po-docgen api api/v1beta2/foundationdbbackup_types.go > docs/backup_spec.md
+	bin/po-docgen api api/v1beta2/foundationdbbackup_types.go api/v1beta2/foundationdb_custom_parameter.go > $@
 
 docs/restore_spec.md: bin/po-docgen api/v1beta2/foundationdbrestore_types.go
-	bin/po-docgen api api/v1beta2/foundationdbrestore_types.go > docs/restore_spec.md
+	bin/po-docgen api api/v1beta2/foundationdbrestore_types.go api/v1beta2/foundationdb_custom_parameter.go > $@
 
 documentation: docs/cluster_spec.md docs/backup_spec.md docs/restore_spec.md
 
-lint:
-	golangci-lint run ./...
+lint: bin/lint
+
+bin/lint: $(GOLANGCI_LINT) ${GO_SRC}
+	$(GOLANGCI_LINT) run ./...
+	@mkdir -p bin
+	@touch $@
+
+snapshot: bin/snapshot
+
+bin/snapshot: ${GO_SRC} $(GORELEASER)
+	$(GORELEASER) check
+	$(GORELEASER) release --snapshot --rm-dist
+	@mkdir -p bin
+	@touch $@
+
+release: bin/release
+
+bin/release: ${GO_SRC} $(GORELEASER)
+	$(GORELEASER) check
+	$(GORELEASER) release --rm-dist
+	@mkdir -p bin
+	@touch $@
diff --git a/README.md b/README.md
index 2dd5360f..8a261031 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,7 @@
 ![GitHub](https://img.shields.io/github/license/FoundationDB/fdb-kubernetes-operator)
 [![CI for main branch](https://github.com/FoundationDB/fdb-kubernetes-operator/actions/workflows/pull_request.yml/badge.svg)](https://github.com/FoundationDB/fdb-kubernetes-operator/actions/workflows/pull_request.yml)
 
-This project provides an operator for managing FoundationDB
-clusters on Kubernetes.
+This project provides an operator for managing FoundationDB clusters on Kubernetes.
 
 ## Running the Operator
 
@@ -26,10 +25,10 @@ kubectl apply -f https://raw.githubusercontent.com/foundationdb/fdb-kubernetes-o
 ```
 
 You can see logs from the operator by running
-`kubectl logs -f -l app=fdb-kubernetes-operator-controller-manager --container=manager`. To determine whether the reconciliation has completed, you can run `kubectl get foundationdbcluster my-cluster`. This will show the latest generation of the
+`kubectl logs -f -l app=fdb-kubernetes-operator-controller-manager --container=manager`. To determine whether the reconciliation has completed, you can run `kubectl get foundationdbcluster test-cluster`. This will show the latest generation of the
 spec and the last reconciled generation of the spec. Once reconciliation has completed, these values will be the same.
 
-Once the reconciliation is complete, you can run `kubectl exec -it my-cluster-log-1 -- fdbcli` to open up a CLI on your cluster.
+Once the reconciliation is complete, you can run `kubectl exec -it test-cluster-log-1 -- fdbcli` to open up a CLI on your cluster.
 
 You can also browse the [sample directory](config/samples) for more examples of different resource configurations.
 
@@ -37,36 +36,31 @@ For more information about using the operator, including detailed discussion of
 
 For more information on version compatibility, see our [compatibility guide](docs/compatibility.md).
 
-For more information on the fields you can define on the cluster resource, see
-the [API documentation](docs/cluster_spec.md).
+For more information on the fields you can define on the cluster resource, see the [API documentation](docs/cluster_spec.md).
 
 ## Local Development
 
 ### Environment Set-up
 
-1. Install GO on your machine, see the [Getting Started](https://golang.org/doc/install) guide for more information.
-2. Install KubeBuilder and its dependencies on your machine, see [The KubeBuilder Book](https://book.kubebuilder.io/quick-start.html) for more information (currently version `2.3.2` is used).
-3. Set your $GOPATH, e.x. `/Users/me/Code/go`.
-4. Install [kustomize](https://github.com/kubernetes-sigs/kustomize).
-5. Install the [foundationDB client package](https://www.foundationdb.org/download).
+1. Install Go on your machine, see the [Getting Started](https://golang.org/doc/install) guide for more information.
+1. Install the required dependencies with `make deps`.
+1. Install the [foundationDB client package](https://www.foundationdb.org/download).
 
 ### Running Locally
 
 To get this controller running in a local Kubernetes cluster:
 
-1. Change your current directory to $GOPATH/src/github.com using the
-   command `cd $GOPATH/src/github.com` and run `mkdir foundationdb`
-   to create the directory `foundationdb`.
-2. CD into the newly created directory and clone this github repo inside
-   `$GOPATH/src/github.com/foundationdb`.
-3. Run `config/test-certs/generate_secrets.bash` to set up a secret with
+1. The assumption is that you have local Kubernetes cluster running.
+   Depending on what solution you use some of the following steps might differ.
+1. Clone this repository onto your local machine.
+1. Run `config/test-certs/generate_secrets.bash` to set up a secret with
    self-signed test certs.
-4. Run `make rebuild-operator` to install the operator. By default, the
-   docker image is built for the platform where this command is executed.
-   To override the platform, for example, to build amd64 image on Apple M1,
-   you can set the BUILD_PLATFORM env variable
+1. Run `make rebuild-operator` to install the operator. By default, the
+   container image is built for the platform where this command is executed.
+   To override the platform, for example, to build an amd64 image on Apple M1,
+   you can set the `BUILD_PLATFORM` env variable
    `BUILD_PLATFORM="linux/amd64" make rebuild-operator`.
-5. Run `kubectl apply -k ./config/tests/base`
+1. Run `kubectl apply -k ./config/tests/base`
    to create a new FoundationDB cluster with the operator.
 
 ### Running locally with nerdctl
diff --git a/api/v1beta2/foundationdb_database_configuration.go b/api/v1beta2/foundationdb_database_configuration.go
index 47a6bce9..5e168f3f 100644
--- a/api/v1beta2/foundationdb_database_configuration.go
+++ b/api/v1beta2/foundationdb_database_configuration.go
@@ -217,6 +217,62 @@ func (configuration DatabaseConfiguration) getRegion(id string, priority int) Re
 	return matchingRegion
 }
 
+// GetRoleCountsWithDefaults gets the role counts from the cluster spec and
+// fills in default values for any role counts that are 0.
+//
+// The default Storage value will be 2F + 1, where F is the cluster's fault
+// tolerance.
+//
+// The default Logs value will be 3.
+//
+// The default Proxies value will be 3.
+//
+// The default Resolvers value will be 1.
+//
+// The default RemoteLogs value will be equal to the Logs value when the
+// UsableRegions is greater than 1. It will be equal to -1 when the
+// UsableRegions is less than or equal to 1.
+//
+// The default LogRouters value will be equal to 3 times the Logs value when
+// the UsableRegions is greater than 1. It will be equal to -1 when the
+// UsableRegions is less than or equal to 1.
+func (configuration *DatabaseConfiguration) GetRoleCountsWithDefaults(faultTolerance int) RoleCounts {
+	counts := configuration.RoleCounts.DeepCopy()
+	if counts.Storage == 0 {
+		counts.Storage = 2*faultTolerance + 1
+	}
+	if counts.Logs == 0 {
+		counts.Logs = 3
+	}
+	if counts.Proxies == 0 {
+		counts.Proxies = 3
+	}
+	if counts.CommitProxies == 0 {
+		counts.CommitProxies = 2
+	}
+	if counts.GrvProxies == 0 {
+		counts.GrvProxies = 1
+	}
+	if counts.Resolvers == 0 {
+		counts.Resolvers = 1
+	}
+	if counts.RemoteLogs == 0 {
+		if configuration.UsableRegions > 1 {
+			counts.RemoteLogs = counts.Logs
+		} else {
+			counts.RemoteLogs = -1
+		}
+	}
+	if counts.LogRouters == 0 {
+		if configuration.UsableRegions > 1 {
+			counts.LogRouters = counts.Logs
+		} else {
+			counts.LogRouters = -1
+		}
+	}
+	return *counts
+}
+
 // GetNextConfigurationChange produces the next marginal change that should
 // be made to transform this configuration into another configuration.
 //
@@ -454,6 +510,31 @@ func (configuration DatabaseConfiguration) getRegionPriorities() map[string]int
 	return priorities
 }
 
+// AreSeparatedProxiesConfigured returns true if grv_proxies and
+// commit_proxies are greater than 0 (explicitly set) and Proxies is set
+// to 0
+func (configuration DatabaseConfiguration) AreSeparatedProxiesConfigured() bool {
+	counts := configuration.RoleCounts
+	return counts.Proxies == 0 && (counts.GrvProxies > 0 && counts.CommitProxies > 0)
+}
+
+// GetProxiesString returns a string that contains the correct fdbcli
+// commands for use inside the database `configure` command.
+//
+// If the version argument supports the separate grv/commit proxy roles
+// and they have been configured as decided by
+// AreSeparatedProxiesConfigured(), then this function will return the
+// string "commit_proxies=%d grv_proxies=%d", otherwise just
+// "proxies=%d" using the correct counts of the configuration object.
+//
+func (configuration DatabaseConfiguration) GetProxiesString(version Version) string {
+	counts := configuration.GetRoleCountsWithDefaults(DesiredFaultTolerance(configuration.RedundancyMode))
+	if version.HasSeparatedProxies() && configuration.AreSeparatedProxiesConfigured() {
+		return fmt.Sprintf(" commit_proxies=%d grv_proxies=%d", counts.CommitProxies, counts.GrvProxies)
+	}
+	return fmt.Sprintf(" proxies=%d", counts.Proxies)
+}
+
 // FillInDefaultsFromStatus adds in missing fields from the database
 // configuration in the database status to make sure they match the fields that
 // will appear in the cluster spec.
@@ -472,18 +553,29 @@ func (configuration DatabaseConfiguration) FillInDefaultsFromStatus() DatabaseCo
 }
 
 // GetConfigurationString gets the CLI command for configuring a database.
-func (configuration DatabaseConfiguration) GetConfigurationString() (string, error) {
+func (configuration DatabaseConfiguration) GetConfigurationString(version string) (string, error) {
 	configurationString := fmt.Sprintf("%s %s", configuration.RedundancyMode, configuration.StorageEngine)
 
+	fdbVersion, err := ParseFdbVersion(version)
+	if err != nil {
+		return configurationString, err
+	}
+
 	counts := configuration.RoleCounts.Map()
 	configurationString += fmt.Sprintf(" usable_regions=%d", configuration.UsableRegions)
 	// TODO: roleNames !
 	for _, role := range roleNames {
+		if role == "proxies" || role == "commit_proxies" || role == "grv_proxies" {
+			continue
+		}
+
 		if role != ProcessClassStorage {
 			configurationString += fmt.Sprintf(" %s=%d", role, counts[role])
 		}
 	}
 
+	configurationString += configuration.GetProxiesString(fdbVersion)
+
 	flags := configuration.VersionFlags.Map()
 	for flag, value := range flags {
 		if value != 0 {
@@ -588,12 +680,14 @@ const (
 
 // RoleCounts represents the roles whose counts can be customized.
 type RoleCounts struct {
-	Storage    int `json:"storage,omitempty"`
-	Logs       int `json:"logs,omitempty"`
-	Proxies    int `json:"proxies,omitempty"`
-	Resolvers  int `json:"resolvers,omitempty"`
-	LogRouters int `json:"log_routers,omitempty"`
-	RemoteLogs int `json:"remote_logs,omitempty"`
+	Storage       int `json:"storage,omitempty"`
+	Logs          int `json:"logs,omitempty"`
+	Proxies       int `json:"proxies,omitempty"`
+	CommitProxies int `json:"commit_proxies,omitempty"`
+	GrvProxies    int `json:"grv_proxies,omitempty"`
+	Resolvers     int `json:"resolvers,omitempty"`
+	LogRouters    int `json:"log_routers,omitempty"`
+	RemoteLogs    int `json:"remote_logs,omitempty"`
 }
 
 // Map returns a map from process classes to the desired count for that role
@@ -687,6 +781,8 @@ type ProcessCounts struct {
 	Resolution        int `json:"resolution,omitempty"`
 	Tester            int `json:"tester,omitempty"`
 	Proxy             int `json:"proxy,omitempty"`
+	CommitProxy       int `json:"commit_proxy,omitempty"`
+	GrvProxy          int `json:"grv_proxy,omitempty"`
 	Master            int `json:"master,omitempty"`
 	Stateless         int `json:"stateless,omitempty"`
 	Log               int `json:"log,omitempty"`
diff --git a/api/v1beta2/foundationdb_version.go b/api/v1beta2/foundationdb_version.go
index d2c824c6..2eb45052 100644
--- a/api/v1beta2/foundationdb_version.go
+++ b/api/v1beta2/foundationdb_version.go
@@ -140,6 +140,12 @@ func (version Version) HasNonBlockingExcludes(useNonBlockingExcludes bool) bool
 	return version.IsAtLeast(Version{Major: 6, Minor: 3, Patch: 5}) && useNonBlockingExcludes
 }
 
+// HasSeparatedProxies determines if a version has support for separate
+// grv/commit proxies
+func (version Version) HasSeparatedProxies() bool {
+	return version.IsAtLeast(Version{Major: 7, Minor: 0, Patch: 0})
+}
+
 // NextMajorVersion returns the next major version of FoundationDB.
 func (version Version) NextMajorVersion() Version {
 	return Version{Major: version.Major + 1, Minor: 0, Patch: 0}
diff --git a/api/v1beta2/foundationdbcluster_types.go b/api/v1beta2/foundationdbcluster_types.go
index be91a526..37ba3327 100644
--- a/api/v1beta2/foundationdbcluster_types.go
+++ b/api/v1beta2/foundationdbcluster_types.go
@@ -943,34 +943,7 @@ func (cluster *FoundationDBCluster) GetProcessSettings(processClass ProcessClass
 // the UsableRegions is greater than 1. It will be equal to -1 when the
 // UsableRegions is less than or equal to 1.
 func (cluster *FoundationDBCluster) GetRoleCountsWithDefaults() RoleCounts {
-	counts := cluster.Spec.DatabaseConfiguration.RoleCounts.DeepCopy()
-	if counts.Storage == 0 {
-		counts.Storage = 2*cluster.DesiredFaultTolerance() + 1
-	}
-	if counts.Logs == 0 {
-		counts.Logs = 3
-	}
-	if counts.Proxies == 0 {
-		counts.Proxies = 3
-	}
-	if counts.Resolvers == 0 {
-		counts.Resolvers = 1
-	}
-	if counts.RemoteLogs == 0 {
-		if cluster.Spec.DatabaseConfiguration.UsableRegions > 1 {
-			counts.RemoteLogs = counts.Logs
-		} else {
-			counts.RemoteLogs = -1
-		}
-	}
-	if counts.LogRouters == 0 {
-		if cluster.Spec.DatabaseConfiguration.UsableRegions > 1 {
-			counts.LogRouters = counts.Logs
-		} else {
-			counts.LogRouters = -1
-		}
-	}
-	return *counts
+	return cluster.Spec.DatabaseConfiguration.GetRoleCountsWithDefaults(cluster.DesiredFaultTolerance())
 }
 
 // calculateProcessCount determines the process count from a given role count.
@@ -1076,10 +1049,23 @@ func (cluster *FoundationDBCluster) GetProcessCountsWithDefaults() (ProcessCount
 	if processCounts.Stateless == 0 {
 		primaryStatelessCount := cluster.calculateProcessCountFromRole(1, processCounts.Master) +
 			cluster.calculateProcessCountFromRole(1, processCounts.ClusterController) +
-			cluster.calculateProcessCountFromRole(roleCounts.Proxies, processCounts.Proxy) +
 			cluster.calculateProcessCountFromRole(roleCounts.Resolvers, processCounts.Resolution)
 		primaryStatelessCount += cluster.calculateProcessCountFromRole(1, processCounts.Ratekeeper) +
 			cluster.calculateProcessCountFromRole(1, processCounts.DataDistributor)
+
+		fdbVersion, err := ParseFdbVersion(cluster.Spec.Version)
+		if err != nil {
+			return *processCounts, err
+		}
+
+		if fdbVersion.HasSeparatedProxies() {
+			primaryStatelessCount += cluster.calculateProcessCountFromRole(roleCounts.GrvProxies, processCounts.GrvProxy)
+			primaryStatelessCount += cluster.calculateProcessCountFromRole(roleCounts.CommitProxies, processCounts.CommitProxy)
+		} else {
+			primaryStatelessCount += cluster.calculateProcessCountFromRole(roleCounts.Proxies, processCounts.Proxy)
+
+		}
+
 		processCounts.Stateless = cluster.calculateProcessCount(true,
 			primaryStatelessCount,
 			cluster.calculateProcessCountFromRole(roleCounts.LogRouters),
@@ -1736,8 +1722,10 @@ func (cluster *FoundationDBCluster) GetMaxConcurrentAutomaticReplacements() int
 // CoordinatorSelectionSetting defines the process class and the priority of it.
 // A higher priority means that the process class is preferred over another.
 type CoordinatorSelectionSetting struct {
+	// ProcessClass defines the process class to associate with priority with.
 	ProcessClass ProcessClass `json:"processClass,omitempty"`
-	Priority     int          `json:"priority,omitempty"`
+	// Priority defines the ordering of different process classes.
+	Priority int `json:"priority,omitempty"`
 }
 
 // IsEligibleAsCandidate checks if the given process has the right process class to be considered a valid coordinator.
@@ -1994,3 +1982,39 @@ func (cluster *FoundationDBCluster) GetUseUnifiedImage() bool {
 func (cluster *FoundationDBCluster) GetIgnoreTerminatingPodsSeconds() int {
 	return pointer.IntDeref(cluster.Spec.AutomationOptions.IgnoreTerminatingPodsSeconds, int((10 * time.Minute).Seconds()))
 }
+
+// AddProcessGroupsToRemovalList adds the provided process group IDs to the remove list.
+// If a process group ID is already present on that list it won't be added a second time.
+func (cluster *FoundationDBCluster) AddProcessGroupsToRemovalList(processGroupIDs []string) {
+	removals := map[string]None{}
+
+	for _, id := range cluster.Spec.ProcessGroupsToRemove {
+		removals[id] = None{}
+	}
+
+	for _, processGroupID := range processGroupIDs {
+		if _, ok := removals[processGroupID]; ok {
+			continue
+		}
+
+		cluster.Spec.ProcessGroupsToRemove = append(cluster.Spec.ProcessGroupsToRemove, processGroupID)
+	}
+}
+
+// AddProcessGroupsToRemovalWithoutExclusionList adds the provided process group IDs to the remove without exclusion list.
+// If a process group ID is already present on that list it won't be added a second time.
+func (cluster *FoundationDBCluster) AddProcessGroupsToRemovalWithoutExclusionList(processGroupIDs []string) {
+	removals := map[string]None{}
+
+	for _, id := range cluster.Spec.ProcessGroupsToRemoveWithoutExclusion {
+		removals[id] = None{}
+	}
+
+	for _, processGroupID := range processGroupIDs {
+		if _, ok := removals[processGroupID]; ok {
+			continue
+		}
+
+		cluster.Spec.ProcessGroupsToRemoveWithoutExclusion = append(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion, processGroupID)
+	}
+}
diff --git a/cmd/po-docgen/api.go b/cmd/po-docgen/api.go
index 736c758f..cf95f2e3 100644
--- a/cmd/po-docgen/api.go
+++ b/cmd/po-docgen/api.go
@@ -26,29 +26,29 @@ import (
 )
 
 const (
-	firstParagraph = `<br>
-# API Docs
+	firstParagraph = `# API Docs
+
 This Document documents the types introduced by the FoundationDB Operator to be consumed by users.
 > Note this document is generated from code comments. When contributing a change to this document please do so by changing the code comments.`
 )
 
 var (
 	links = map[string]string{
-		"metav1.ObjectMeta":            "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectmeta-v1-meta",
-		"metav1.ListMeta":              "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#listmeta-v1-meta",
-		"metav1.LabelSelector":         "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#labelselector-v1-meta",
-		"corev1.ResourceRequirements":  "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#resourcerequirements-v1-core",
-		"corev1.LocalObjectReference":  "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#localobjectreference-v1-core",
-		"corev1.SecretKeySelector":     "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#secretkeyselector-v1-core",
-		"corev1.PersistentVolumeClaim": "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#persistentvolumeclaim-v1-core",
-		"corev1.EmptyDirVolumeSource":  "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#emptydirvolumesource-v1-core",
-		"corev1.Container":             "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#container-v1-core",
-		"corev1.PodSecurityContext":    "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#podsecuritycontext-v1-core",
-		"corev1.SecurityContext":       "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#securitycontext-v1-core",
-		"corev1.EnvVar":                "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#envvar-v1-core",
-		"corev1.VolumeMount":           "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#volumemount-v1-core",
-		"corev1.PodTemplateSpec":       "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#podtemplatespec-v1-core",
-		"corev1.ConfigMap":             "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#configmap-v1-core",
+		"metav1.ObjectMeta":            "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta",
+		"metav1.ListMeta":              "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta",
+		"metav1.LabelSelector":         "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#labelselector-v1-meta",
+		"corev1.ResourceRequirements":  "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#resourcerequirements-v1-core",
+		"corev1.LocalObjectReference":  "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#localobjectreference-v1-core",
+		"corev1.SecretKeySelector":     "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#secretkeyselector-v1-core",
+		"corev1.PersistentVolumeClaim": "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#persistentvolumeclaim-v1-core",
+		"corev1.EmptyDirVolumeSource":  "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#emptydirvolumesource-v1-core",
+		"corev1.Container":             "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#container-v1-core",
+		"corev1.PodSecurityContext":    "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core",
+		"corev1.SecurityContext":       "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core",
+		"corev1.EnvVar":                "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#envvar-v1-core",
+		"corev1.VolumeMount":           "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#volumemount-v1-core",
+		"corev1.PodTemplateSpec":       "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podtemplatespec-v1-core",
+		"corev1.ConfigMap":             "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#configmap-v1-core",
 	}
 
 	selfLinks = map[string]string{}
@@ -61,7 +61,7 @@ func toSectionLink(name string) string {
 }
 
 func printTOC(types []KubeTypes) {
-	fmt.Printf("\n## Table of Contents\n")
+	fmt.Printf("\n## Table of Contents\n\n")
 	for _, t := range types {
 		strukt := t[0]
 		if len(t) > 1 {
@@ -97,11 +97,14 @@ func printAPIDocs(paths []string) {
 			}
 			fmt.Println("")
 			fmt.Println("[Back to TOC](#table-of-contents)")
+		} else {
+			fmt.Printf("\n## %s\n\n%s\n\n", strukt.Name, strukt.Doc)
+			fmt.Println("[Back to TOC](#table-of-contents)")
 		}
 	}
 }
 
-// Pair of strings. We keed the name of fields and the doc
+// Pair of strings. We keep the name of fields and the doc
 type Pair struct {
 	Name, Doc, Type string
 	Mandatory       bool
@@ -121,12 +124,14 @@ func ParseDocumentationFrom(srcs []string) []KubeTypes {
 		pkg := astFrom(src)
 
 		for _, kubType := range pkg.Types {
+
 			if structType, ok := kubType.Decl.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType); ok {
 				var ks KubeTypes
 				ks = append(ks, Pair{kubType.Name, fmtRawDoc(kubType.Doc), "", false})
 
 				for _, field := range structType.Fields.List {
 					typeString := fieldType(field.Type)
+
 					fieldMandatory := fieldRequired(field)
 					if n := fieldName(field); n != "-" {
 						fieldDoc := fmtRawDoc(field.Doc.Text())
@@ -134,6 +139,9 @@ func ParseDocumentationFrom(srcs []string) []KubeTypes {
 					}
 				}
 				docForTypes = append(docForTypes, ks)
+			} else if i, ok := kubType.Decl.Specs[0].(*ast.TypeSpec).Type.(*ast.Ident); ok {
+				// Add documentation for typed string that are not structs.
+				docForTypes = append(docForTypes, KubeTypes{Pair{kubType.Name, fmtRawDoc(kubType.Doc), i.Name, false}})
 			}
 		}
 	}
diff --git a/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml b/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml
index 18ff978c..6e139e2f 100644
--- a/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml
+++ b/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml
@@ -9423,6 +9423,10 @@ spec:
                 type: string
               databaseConfiguration:
                 properties:
+                  commit_proxies:
+                    type: integer
+                  grv_proxies:
+                    type: integer
                   log_routers:
                     type: integer
                   log_spill:
@@ -9589,12 +9593,16 @@ spec:
                     type: integer
                   cluster_controller:
                     type: integer
+                  commit_proxy:
+                    type: integer
                   coordinator:
                     type: integer
                   data_distributor:
                     type: integer
                   fast_restore:
                     type: integer
+                  grv_proxy:
+                    type: integer
                   log:
                     type: integer
                   master:
@@ -12807,6 +12815,10 @@ spec:
                 type: string
               databaseConfiguration:
                 properties:
+                  commit_proxies:
+                    type: integer
+                  grv_proxies:
+                    type: integer
                   log_routers:
                     type: integer
                   log_spill:
diff --git a/config/deployment/manager.yaml b/config/deployment/manager.yaml
index b20d833c..4c7692a1 100644
--- a/config/deployment/manager.yaml
+++ b/config/deployment/manager.yaml
@@ -66,7 +66,7 @@ spec:
       containers:
       - command:
         - /manager
-        image: foundationdb/fdb-kubernetes-operator:v0.51.1
+        image: foundationdb/fdb-kubernetes-operator:v1.1.0
         name: manager
         env:
           - name: WATCH_NAMESPACE
diff --git a/config/development/images.yaml b/config/development/images.yaml
new file mode 100644
index 00000000..66bfc09c
--- /dev/null
+++ b/config/development/images.yaml
@@ -0,0 +1,43 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: controller-manager
+spec:
+  template:
+    spec:
+      initContainers:
+        # Install client libraries for unreleased versions of FDB
+        - name: foundationdb-kubernetes-init-7-1
+          image: foundationdb/foundationdb-kubernetes-sidecar:7.1.0-rc2-1
+          args:
+            - "--copy-binary"
+            - "fdbcli"
+            - "--copy-binary"
+            - "fdbbackup"
+            - "--copy-binary"
+            - "fdbrestore"
+            - "--output-dir"
+            - "/var/output-files/7.1.0-rc2"
+            - "--init-mode"
+          volumeMounts:
+            - name: fdb-binaries
+              mountPath: /var/output-files
+        # Install this library in a special location to force the operator to
+        # use it as the primary library.
+        - name: foundationdb-kubernetes-init-7-1-primary
+          image: foundationdb/foundationdb-kubernetes-sidecar:7.1.0-rc2-1
+          args:
+            - "--copy-library"
+            - "7.1"
+            - "--output-dir"
+            - "/var/output-files/primary"
+            - "--init-mode"
+          volumeMounts:
+            - name: fdb-binaries
+              mountPath: /var/output-files
+      containers:
+        - name: manager
+          imagePullPolicy: IfNotPresent
+          env:
+            - name: LD_LIBRARY_PATH
+              value: /usr/bin/fdb/primary/lib
diff --git a/config/development/kustomization.yaml b/config/development/kustomization.yaml
index 5c260194..6687475b 100644
--- a/config/development/kustomization.yaml
+++ b/config/development/kustomization.yaml
@@ -5,6 +5,7 @@ patchesStrategicMerge:
 - test_certs.yaml
 - backup_credentials.yaml
 - debug_logs.yaml
+- images.yaml
 
 resources:
 - ../deployment
diff --git a/config/samples/deployment.yaml b/config/samples/deployment.yaml
index 3a9fac58..29d666ba 100644
--- a/config/samples/deployment.yaml
+++ b/config/samples/deployment.yaml
@@ -150,7 +150,7 @@ spec:
           valueFrom:
             fieldRef:
               fieldPath: metadata.namespace
-        image: foundationdb/fdb-kubernetes-operator:v0.51.1
+        image: foundationdb/fdb-kubernetes-operator:v1.1.0
         name: manager
         ports:
         - containerPort: 8080
diff --git a/controllers/admin_client_mock.go b/controllers/admin_client_mock.go
index 49fd1a5f..32801d54 100644
--- a/controllers/admin_client_mock.go
+++ b/controllers/admin_client_mock.go
@@ -315,7 +315,7 @@ func (client *mockAdminClient) GetStatus() (*fdbv1beta2.FoundationDBStatus, erro
 }
 
 // ConfigureDatabase changes the database configuration
-func (client *mockAdminClient) ConfigureDatabase(configuration fdbv1beta2.DatabaseConfiguration, _ bool) error {
+func (client *mockAdminClient) ConfigureDatabase(configuration fdbv1beta2.DatabaseConfiguration, _ bool, _ string) error {
 	adminClientMutex.Lock()
 	defer adminClientMutex.Unlock()
 
diff --git a/controllers/cluster_controller.go b/controllers/cluster_controller.go
index 8e78509b..e0867a31 100644
--- a/controllers/cluster_controller.go
+++ b/controllers/cluster_controller.go
@@ -723,7 +723,7 @@ func checkCoordinatorValidity(cluster *fdbv1beta2.FoundationDBCluster, status *f
 
 // newFdbPodClient builds a client for working with an FDB Pod
 func newFdbPodClient(cluster *fdbv1beta2.FoundationDBCluster, pod *corev1.Pod) (podclient.FdbPodClient, error) {
-	return internal.NewFdbPodClient(cluster, pod)
+	return internal.NewFdbPodClient(cluster, pod, log.WithValues("namespace", cluster.Namespace, "cluster", cluster.Name, "pod", pod.Name))
 }
 
 func (r *FoundationDBClusterReconciler) getCoordinatorSet(cluster *fdbv1beta2.FoundationDBCluster) (map[string]struct{}, error) {
diff --git a/controllers/update_database_configuration.go b/controllers/update_database_configuration.go
index 97cffb0e..35896ab4 100644
--- a/controllers/update_database_configuration.go
+++ b/controllers/update_database_configuration.go
@@ -76,7 +76,7 @@ func (u updateDatabaseConfiguration) reconcile(ctx context.Context, r *Foundatio
 		} else {
 			nextConfiguration = currentConfiguration.GetNextConfigurationChange(desiredConfiguration)
 		}
-		configurationString, _ := nextConfiguration.GetConfigurationString()
+		configurationString, _ := nextConfiguration.GetConfigurationString(cluster.Spec.Version)
 		var enabled = cluster.Spec.AutomationOptions.ConfigureDatabase
 
 		if !dataHealthy {
@@ -109,7 +109,7 @@ func (u updateDatabaseConfiguration) reconcile(ctx context.Context, r *Foundatio
 		r.Recorder.Event(cluster, corev1.EventTypeNormal, "ConfiguringDatabase",
 			fmt.Sprintf("Setting database configuration to `%s`", configurationString),
 		)
-		err = adminClient.ConfigureDatabase(nextConfiguration, initialConfig)
+		err = adminClient.ConfigureDatabase(nextConfiguration, initialConfig, cluster.Spec.Version)
 		if err != nil {
 			return &requeue{curError: err}
 		}
diff --git a/controllers/update_status.go b/controllers/update_status.go
index 873831dd..10b5dae8 100644
--- a/controllers/update_status.go
+++ b/controllers/update_status.go
@@ -424,7 +424,7 @@ func validateProcessGroups(ctx context.Context, r *FoundationDBClusterReconciler
 
 		pod := pods[0]
 
-		processGroup.AddAddresses(podmanager.GetPublicIPs(pod), processGroup.IsMarkedForRemoval() || !status.Health.Available)
+		processGroup.AddAddresses(podmanager.GetPublicIPs(pod, log), processGroup.IsMarkedForRemoval() || !status.Health.Available)
 		processCount := 1
 
 		if processGroup.IsMarkedForRemoval() && pod.ObjectMeta.DeletionTimestamp != nil {
diff --git a/docs/backup_spec.md b/docs/backup_spec.md
index 4a214700..4a1b6ce2 100644
--- a/docs/backup_spec.md
+++ b/docs/backup_spec.md
@@ -1,9 +1,10 @@
-<br>
 # API Docs
+
 This Document documents the types introduced by the FoundationDB Operator to be consumed by users.
 > Note this document is generated from code comments. When contributing a change to this document please do so by changing the code comments.
 
 ## Table of Contents
+
 * [BackupGenerationStatus](#backupgenerationstatus)
 * [BlobStoreConfiguration](#blobstoreconfiguration)
 * [FoundationDBBackup](#foundationdbbackup)
@@ -29,6 +30,12 @@ BackupGenerationStatus stores information on which generations have reached diff
 
 [Back to TOC](#table-of-contents)
 
+## BackupState
+
+BackupState defines the desired state of a backup
+
+[Back to TOC](#table-of-contents)
+
 ## BlobStoreConfiguration
 
 BlobStoreConfiguration describes the blob store configuration.
@@ -38,7 +45,7 @@ BlobStoreConfiguration describes the blob store configuration.
 | backupName | The name for the backup. If empty defaults to .metadata.name. | string | false |
 | accountName | The account name to use with the backup destination. | string | true |
 | bucket | The backup bucket to write to. The default is \"fdb-backups\". | string | false |
-| urlParameters | Additional URL parameters passed to the blobstore URL. | []URLParameter | false |
+| urlParameters | Additional URL parameters passed to the blobstore URL. | [][URLParameter](#urlparameter) | false |
 
 [Back to TOC](#table-of-contents)
 
@@ -48,7 +55,7 @@ FoundationDBBackup is the Schema for the foundationdbbackups API
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| metadata |  | [metav1.ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectmeta-v1-meta) | false |
+| metadata |  | [metav1.ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta) | false |
 | spec |  | [FoundationDBBackupSpec](#foundationdbbackupspec) | false |
 | status |  | [FoundationDBBackupStatus](#foundationdbbackupstatus) | false |
 
@@ -60,7 +67,7 @@ FoundationDBBackupList contains a list of FoundationDBBackup objects
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| metadata |  | [metav1.ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#listmeta-v1-meta) | false |
+| metadata |  | [metav1.ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta) | false |
 | items |  | [][FoundationDBBackup](#foundationdbbackup) | true |
 
 [Back to TOC](#table-of-contents)
@@ -73,11 +80,11 @@ FoundationDBBackupSpec describes the desired state of the backup for a cluster.
 | ----- | ----------- | ------ | -------- |
 | version | The version of FoundationDB that the backup agents should run. | string | true |
 | clusterName | The cluster this backup is for. | string | true |
-| backupState | The desired state of the backup. The default is Running. | BackupState | false |
+| backupState | The desired state of the backup. The default is Running. | [BackupState](#backupstate) | false |
 | agentCount | AgentCount defines the number of backup agents to run. The default is run 2 agents. | *int | false |
 | snapshotPeriodSeconds | The time window between new snapshots. This is measured in seconds. The default is 864,000, or 10 days. | *int | false |
-| backupDeploymentMetadata | BackupDeploymentMetadata allows customizing labels and annotations on the deployment for the backup agents. | *[metav1.ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectmeta-v1-meta) | false |
-| podTemplateSpec | PodTemplateSpec allows customizing the pod template for the backup agents. | *[corev1.PodTemplateSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#podtemplatespec-v1-core) | false |
+| backupDeploymentMetadata | BackupDeploymentMetadata allows customizing labels and annotations on the deployment for the backup agents. | *[metav1.ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta) | false |
+| podTemplateSpec | PodTemplateSpec allows customizing the pod template for the backup agents. | *[corev1.PodTemplateSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podtemplatespec-v1-core) | false |
 | customParameters | CustomParameters defines additional parameters to pass to the backup agents. | FoundationDBCustomParameters | false |
 | allowTagOverride | This setting defines if a user provided image can have it's own tag rather than getting the provided version appended. You have to ensure that the specified version in the Spec is compatible with the given version in your custom image. | *bool | false |
 | blobStoreConfiguration | This is the configuration of the target blobstore for this backup. | *[BlobStoreConfiguration](#blobstoreconfiguration) | false |
@@ -132,3 +139,15 @@ FoundationDBLiveBackupStatusState provides the state of a backup in the backup s
 | Running | Running determines whether the backup is currently running. | bool | false |
 
 [Back to TOC](#table-of-contents)
+
+## URLParameter
+
+URLParameter defines a single URL parameter to pass to the blobstore.
+
+[Back to TOC](#table-of-contents)
+
+## FoundationDBCustomParameter
+
+FoundationDBCustomParameter defines a single custom knob
+
+[Back to TOC](#table-of-contents)
diff --git a/docs/changelog/v1.1.0.md b/docs/changelog/v1.1.0.md
new file mode 100644
index 00000000..2b60c9bb
--- /dev/null
+++ b/docs/changelog/v1.1.0.md
@@ -0,0 +1,16 @@
+# v1.1.0
+
+## Changes
+
+* Unify kustomize setup and reduce duplication [#1084](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1084).
+* Pass configured logger into fdbclient [#1111](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1111).
+* Add an image pull policy in the development operator config [#1115](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1115).
+* Changes to support coordinator changes when using DNS names [#1122](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1122).
+* Remove the usage of go get and unused dep [#1124](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1124).
+* Fix flaky unit test and bug in process group removal [#1129](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1129).
+* Add authentication support for OpenStack, Azure and GCP [#1130](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1130).
+* Run specs in parallel [#1125](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1125).
+* Remove unused deps and use make to install required versions [#1127](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1127).
+* Correct the generation of our spec documentation to include structs from different files [#1137](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1137).
+* Make use of [goreleaser](https://goreleaser.com) for our plugin [#1139](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1139).
+* Add support for grv and commit proxies for FDB 7.0+ [#1114](https://github.com/FoundationDB/fdb-kubernetes-operator/pull/1114).
diff --git a/docs/cluster_spec.md b/docs/cluster_spec.md
index 0d9bb195..08bae706 100644
--- a/docs/cluster_spec.md
+++ b/docs/cluster_spec.md
@@ -1,9 +1,10 @@
-<br>
 # API Docs
+
 This Document documents the types introduced by the FoundationDB Operator to be consumed by users.
 > Note this document is generated from code comments. When contributing a change to this document please do so by changing the code comments.
 
 ## Table of Contents
+
 * [AutomaticReplacementOptions](#automaticreplacementoptions)
 * [BuggifyConfig](#buggifyconfig)
 * [ClusterGenerationStatus](#clustergenerationstatus)
@@ -27,6 +28,12 @@ This Document documents the types introduced by the FoundationDB Operator to be
 * [ProcessSettings](#processsettings)
 * [RequiredAddressSet](#requiredaddressset)
 * [RoutingConfig](#routingconfig)
+* [DataCenter](#datacenter)
+* [DatabaseConfiguration](#databaseconfiguration)
+* [ProcessCounts](#processcounts)
+* [Region](#region)
+* [RoleCounts](#rolecounts)
+* [VersionFlags](#versionflags)
 
 ## AutomaticReplacementOptions
 
@@ -120,8 +127,8 @@ CoordinatorSelectionSetting defines the process class and the priority of it. A
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| processClass |  | ProcessClass | false |
-| priority |  | int | false |
+| processClass | ProcessClass defines the process class to associate with priority with. | [ProcessClass](#processclass) | false |
+| priority | Priority defines the ordering of different process classes. | int | false |
 
 [Back to TOC](#table-of-contents)
 
@@ -131,7 +138,7 @@ FoundationDBCluster is the Schema for the foundationdbclusters API
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| metadata |  | [metav1.ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectmeta-v1-meta) | false |
+| metadata |  | [metav1.ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta) | false |
 | spec |  | [FoundationDBClusterSpec](#foundationdbclusterspec) | false |
 | status |  | [FoundationDBClusterStatus](#foundationdbclusterstatus) | false |
 
@@ -150,10 +157,10 @@ FoundationDBClusterAutomationOptions provides flags for enabling or disabling op
 | useNonBlockingExcludes | UseNonBlockingExcludes defines whether the operator is allowed to use non blocking exclude commands. The default is false. | *bool | false |
 | ignoreTerminatingPodsSeconds | IgnoreTerminatingPodsSeconds defines how long a Pod has to be in the Terminating Phase before we ignore it during reconciliation. This prevents Pod that are stuck in Terminating to block further reconciliation. | *int | false |
 | maxConcurrentReplacements | MaxConcurrentReplacements defines how many process groups can be concurrently replaced if they are misconfigured. If the value will be set to 0 this will block replacements and these misconfigured Pods must be replaced manually or by another process. For each reconcile loop the operator calculates the maximum number of possible replacements by taken this value as the upper limit and removes all ongoing replacements that have not finished. Which means if the value is set to 5 and we have 4 ongoing replacements (process groups marked with remove but not excluded) the operator is allowed to replace on further process group. | *int | false |
-| deletionMode | DeletionMode defines the deletion mode for this cluster. This can be PodUpdateModeNone, PodUpdateModeAll, PodUpdateModeZone or PodUpdateModeProcessGroup. The DeletionMode defines how Pods are deleted in order to update them or when they are removed. | PodUpdateMode | false |
-| removalMode | RemovalMode defines the removal mode for this cluster. This can be PodUpdateModeNone, PodUpdateModeAll, PodUpdateModeZone or PodUpdateModeProcessGroup. The RemovalMode defines how process groups are deleted in order when they are marked for removal. | PodUpdateMode | false |
+| deletionMode | DeletionMode defines the deletion mode for this cluster. This can be PodUpdateModeNone, PodUpdateModeAll, PodUpdateModeZone or PodUpdateModeProcessGroup. The DeletionMode defines how Pods are deleted in order to update them or when they are removed. | [PodUpdateMode](#podupdatemode) | false |
+| removalMode | RemovalMode defines the removal mode for this cluster. This can be PodUpdateModeNone, PodUpdateModeAll, PodUpdateModeZone or PodUpdateModeProcessGroup. The RemovalMode defines how process groups are deleted in order when they are marked for removal. | [PodUpdateMode](#podupdatemode) | false |
 | waitBetweenRemovalsSeconds | WaitBetweenRemovalsSeconds defines how long to wait between the last removal and the next removal. This is only an upper limit if the process group and the according resources are deleted faster than the provided duration the operator will move on with the next removal. The idea is to prevent a race condition were the operator deletes a resource but the Kubernetes API is slower to trigger the actual deletion, and we are running into a situation where the fault tolerance check still includes the already deleted processes. Defaults to 60. | *int | false |
-| podUpdateStrategy | PodUpdateStrategy defines how Pod spec changes are rolled out either by replacing Pods or by deleting Pods. The default for this is ReplaceTransactionSystem. | PodUpdateStrategy | false |
+| podUpdateStrategy | PodUpdateStrategy defines how Pod spec changes are rolled out either by replacing Pods or by deleting Pods. The default for this is ReplaceTransactionSystem. | [PodUpdateStrategy](#podupdatestrategy) | false |
 
 [Back to TOC](#table-of-contents)
 
@@ -177,7 +184,7 @@ FoundationDBClusterList contains a list of FoundationDBCluster objects
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| metadata |  | [metav1.ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#listmeta-v1-meta) | false |
+| metadata |  | [metav1.ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta) | false |
 | items |  | [][FoundationDBCluster](#foundationdbcluster) | true |
 
 [Back to TOC](#table-of-contents)
@@ -189,15 +196,15 @@ FoundationDBClusterSpec defines the desired state of a cluster.
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
 | version | Version defines the version of FoundationDB the cluster should run. | string | true |
-| databaseConfiguration | DatabaseConfiguration defines the database configuration. | DatabaseConfiguration | false |
-| processes | Processes defines process-level settings. | map[ProcessClass][ProcessSettings](#processsettings) | false |
-| processCounts | ProcessCounts defines the number of processes to configure for each process class. You can generally omit this, to allow the operator to infer the process counts based on the database configuration. | ProcessCounts | false |
+| databaseConfiguration | DatabaseConfiguration defines the database configuration. | [DatabaseConfiguration](#databaseconfiguration) | false |
+| processes | Processes defines process-level settings. | map[[ProcessClass](#processclass)][ProcessSettings](#processsettings) | false |
+| processCounts | ProcessCounts defines the number of processes to configure for each process class. You can generally omit this, to allow the operator to infer the process counts based on the database configuration. | [ProcessCounts](#processcounts) | false |
 | seedConnectionString | SeedConnectionString provides a connection string for the initial reconciliation.  After the initial reconciliation, this will not be used. | string | false |
 | partialConnectionString | PartialConnectionString provides a way to specify part of the connection string (e.g. the database name and coordinator generation) without specifying the entire string. This does not allow for setting the coordinator IPs. If `SeedConnectionString` is set, `PartialConnectionString` will have no effect. They cannot be used together. | [ConnectionString](#connectionstring) | false |
 | faultDomain | FaultDomain defines the rules for what fault domain to replicate across. | [FoundationDBClusterFaultDomain](#foundationdbclusterfaultdomain) | false |
 | processGroupsToRemove | ProcessGroupsToRemove defines the process groups that we should remove from the cluster. This list contains the process group IDs. | []string | false |
 | processGroupsToRemoveWithoutExclusion | ProcessGroupsToRemoveWithoutExclusion defines the process groups that we should remove from the cluster without excluding them. This list contains the process group IDs.  This should be used for cases where a pod does not have an IP address and you want to remove it and destroy its volume without confirming the data is fully replicated. | []string | false |
-| configMap | ConfigMap allows customizing the config map the operator creates. | *[corev1.ConfigMap](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#configmap-v1-core) | false |
+| configMap | ConfigMap allows customizing the config map the operator creates. | *[corev1.ConfigMap](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#configmap-v1-core) | false |
 | mainContainer | MainContainer defines customization for the foundationdb container. | [ContainerOverrides](#containeroverrides) | false |
 | sidecarContainer | SidecarContainer defines customization for the foundationdb-kubernetes-sidecar container. | [ContainerOverrides](#containeroverrides) | false |
 | trustedCAs | TrustedCAs defines a list of root CAs the cluster should trust, in PEM format. | []string | false |
@@ -228,7 +235,7 @@ FoundationDBClusterStatus defines the observed state of FoundationDBCluster
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| databaseConfiguration | DatabaseConfiguration provides the running configuration of the database. | DatabaseConfiguration | false |
+| databaseConfiguration | DatabaseConfiguration provides the running configuration of the database. | [DatabaseConfiguration](#databaseconfiguration) | false |
 | generations | Generations provides information about the latest generation to be reconciled, or to reach other stages at which reconciliation can halt. | [ClusterGenerationStatus](#clustergenerationstatus) | false |
 | health | Health provides information about the health of the database. | [ClusterHealth](#clusterhealth) | false |
 | requiredAddresses | RequiredAddresses define that addresses that we need to enable for the processes in the cluster. | [RequiredAddressSet](#requiredaddressset) | false |
@@ -240,7 +247,7 @@ FoundationDBClusterStatus defines the observed state of FoundationDBCluster
 | configured | Configured defines whether we have configured the database yet. | bool | false |
 | hasListenIPsForAllPods | HasListenIPsForAllPods defines whether every pod has an environment variable for its listen address. | bool | false |
 | storageServersPerDisk | StorageServersPerDisk defines the storageServersPerPod observed in the cluster. If there are more than one value in the slice the reconcile phase is not finished. | []int | false |
-| imageTypes | ImageTypes defines the kinds of images that are in use in the cluster. If there is more than one value in the slice the reconcile phase is not finished. | []ImageType | false |
+| imageTypes | ImageTypes defines the kinds of images that are in use in the cluster. If there is more than one value in the slice the reconcile phase is not finished. | [][ImageType](#imagetype) | false |
 | processGroups | ProcessGroups contain information about a process group. This information is used in multiple places to trigger the according action. | []*[ProcessGroupStatus](#processgroupstatus) | false |
 | locks | Locks contains information about the locking system. | [LockSystemStatus](#locksystemstatus) | false |
 
@@ -259,6 +266,12 @@ ImageConfig provides a policy for customizing an image.  When multiple image con
 
 [Back to TOC](#table-of-contents)
 
+## ImageType
+
+ImageType defines a single kind of images used in the cluster.
+
+[Back to TOC](#table-of-contents)
+
 ## LabelConfig
 
 LabelConfig allows customizing labels used by the operator.
@@ -307,17 +320,35 @@ LockSystemStatus provides a summary of the status of the locking system.
 
 [Back to TOC](#table-of-contents)
 
+## PodUpdateMode
+
+PodUpdateMode defines the deletion mode for the cluster
+
+[Back to TOC](#table-of-contents)
+
+## PodUpdateStrategy
+
+PodUpdateStrategy defines how Pod spec changes should be applied.
+
+[Back to TOC](#table-of-contents)
+
 ## ProcessGroupCondition
 
 ProcessGroupCondition represents a degraded condition that a process group is in.
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| type | Name of the condition | ProcessGroupConditionType | false |
+| type | Name of the condition | [ProcessGroupConditionType](#processgroupconditiontype) | false |
 | timestamp | Timestamp when the Condition was observed | int64 | false |
 
 [Back to TOC](#table-of-contents)
 
+## ProcessGroupConditionType
+
+ProcessGroupConditionType represents a concrete ProcessGroupCondition.
+
+[Back to TOC](#table-of-contents)
+
 ## ProcessGroupStatus
 
 ProcessGroupStatus represents the status of a ProcessGroup.
@@ -325,7 +356,7 @@ ProcessGroupStatus represents the status of a ProcessGroup.
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
 | processGroupID | ProcessGroupID represents the ID of the process group | string | false |
-| processClass | ProcessClass represents the class the process group has. | ProcessClass | false |
+| processClass | ProcessClass represents the class the process group has. | [ProcessClass](#processclass) | false |
 | addresses | Addresses represents the list of addresses the process group has been known to have. | []string | false |
 | removalTimestamp | RemoveTimestamp if not empty defines when the process group was marked for removal. | *metav1.Time | false |
 | exclusionTimestamp | ExcludedTimestamp defines when the process group has been fully excluded. This is only used within the reconciliation process, and should not be considered authoritative. | *metav1.Time | false |
@@ -340,12 +371,18 @@ ProcessSettings defines process-level settings.
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| podTemplate | PodTemplate allows customizing the pod. If a container image with a tag is specified the operator will throw an error and stop processing the cluster. | *[corev1.PodTemplateSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#podtemplatespec-v1-core) | false |
-| volumeClaimTemplate | VolumeClaimTemplate allows customizing the persistent volume claim for the pod. | *[corev1.PersistentVolumeClaim](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#persistentvolumeclaim-v1-core) | false |
+| podTemplate | PodTemplate allows customizing the pod. If a container image with a tag is specified the operator will throw an error and stop processing the cluster. | *[corev1.PodTemplateSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podtemplatespec-v1-core) | false |
+| volumeClaimTemplate | VolumeClaimTemplate allows customizing the persistent volume claim for the pod. | *[corev1.PersistentVolumeClaim](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#persistentvolumeclaim-v1-core) | false |
 | customParameters | CustomParameters defines additional parameters to pass to the fdbserver process. | FoundationDBCustomParameters | false |
 
 [Back to TOC](#table-of-contents)
 
+## PublicIPSource
+
+PublicIPSource models options for how a pod gets its public IP.
+
+[Back to TOC](#table-of-contents)
+
 ## RequiredAddressSet
 
 RequiredAddressSet provides settings for which addresses we need to listen on.
@@ -364,9 +401,128 @@ RoutingConfig allows configuring routing to our pods, and services that sit in f
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
 | headlessService | Headless determines whether we want to run a headless service for the cluster. | *bool | false |
-| publicIPSource | PublicIPSource specifies what source a process should use to get its public IPs.  This supports the values `pod` and `service`. | *PublicIPSource | false |
+| publicIPSource | PublicIPSource specifies what source a process should use to get its public IPs.  This supports the values `pod` and `service`. | *[PublicIPSource](#publicipsource) | false |
 | podIPFamily | PodIPFamily tells the pod which family of IP addresses to use. You can use 4 to represent IPv4, and 6 to represent IPv6. This feature is only supported in FDB 7.0 or later, and requires dual-stack support in your Kubernetes environment. | *int | false |
 | useDNSInClusterFile | UseDNSInClusterFile determines whether to use DNS names rather than IP addresses to identify coordinators in the cluster file. NOTE: This is an experimental feature, and is not supported in the latest stable version of FoundationDB. | *bool | false |
 | dnsDomain | DNSDomain defines the cluster domain used in a DNS name generated for a service. The default is `cluster.local`. | *string | false |
 
 [Back to TOC](#table-of-contents)
+
+## FoundationDBCustomParameter
+
+FoundationDBCustomParameter defines a single custom knob
+
+[Back to TOC](#table-of-contents)
+
+## DataCenter
+
+DataCenter represents a data center in the region configuration
+
+| Field | Description | Scheme | Required |
+| ----- | ----------- | ------ | -------- |
+| id | The ID of the data center. This must match the dcid locality field. | string | false |
+| priority | The priority of this data center when we have to choose a location. Higher priorities are preferred over lower priorities. | int | false |
+| satellite | Satellite indicates whether the data center is serving as a satellite for the region. A value of 1 indicates that it is a satellite, and a value of 0 indicates that it is not a satellite. | int | false |
+
+[Back to TOC](#table-of-contents)
+
+## DatabaseConfiguration
+
+DatabaseConfiguration represents the configuration of the database
+
+| Field | Description | Scheme | Required |
+| ----- | ----------- | ------ | -------- |
+| redundancy_mode | RedundancyMode defines the core replication factor for the database. | [RedundancyMode](#redundancymode) | false |
+| storage_engine | StorageEngine defines the storage engine the database uses. | [StorageEngine](#storageengine) | false |
+| usable_regions | UsableRegions defines how many regions the database should store data in. | int | false |
+| regions | Regions defines the regions that the database can replicate in. | [][Region](#region) | false |
+| RoleCounts | RoleCounts defines how many processes the database should recruit for each role. | [RoleCounts](#rolecounts) | true |
+| VersionFlags | VersionFlags defines internal flags for testing new features in the database. | [VersionFlags](#versionflags) | true |
+
+[Back to TOC](#table-of-contents)
+
+## ProcessCounts
+
+ProcessCounts represents the number of processes we have for each valid process class.  If one of the counts in the spec is set to 0, we will infer the process count for that class from the role counts. If one of the counts in the spec is set to -1, we will not create any processes for that class. See GetProcessCountsWithDefaults for more information on the rules for inferring process counts.
+
+| Field | Description | Scheme | Required |
+| ----- | ----------- | ------ | -------- |
+| unset |  | int | false |
+| storage |  | int | false |
+| transaction |  | int | false |
+| resolution |  | int | false |
+| tester |  | int | false |
+| proxy |  | int | false |
+| commit_proxy |  | int | false |
+| grv_proxy |  | int | false |
+| master |  | int | false |
+| stateless |  | int | false |
+| log |  | int | false |
+| cluster_controller |  | int | false |
+| router |  | int | false |
+| fast_restore |  | int | false |
+| data_distributor |  | int | false |
+| coordinator |  | int | false |
+| ratekeeper |  | int | false |
+| storage_cache |  | int | false |
+| backup |  | int | false |
+
+[Back to TOC](#table-of-contents)
+
+## RedundancyMode
+
+RedundancyMode defines the core replication factor for the database
+
+[Back to TOC](#table-of-contents)
+
+## Region
+
+Region represents a region in the database configuration
+
+| Field | Description | Scheme | Required |
+| ----- | ----------- | ------ | -------- |
+| datacenters | The data centers in this region. | [][DataCenter](#datacenter) | false |
+| satellite_logs | The number of satellite logs that we should recruit. | int | false |
+| satellite_redundancy_mode | The replication strategy for satellite logs. | string | false |
+
+[Back to TOC](#table-of-contents)
+
+## RoleCounts
+
+RoleCounts represents the roles whose counts can be customized.
+
+| Field | Description | Scheme | Required |
+| ----- | ----------- | ------ | -------- |
+| storage |  | int | false |
+| logs |  | int | false |
+| proxies |  | int | false |
+| commit_proxies |  | int | false |
+| grv_proxies |  | int | false |
+| resolvers |  | int | false |
+| log_routers |  | int | false |
+| remote_logs |  | int | false |
+
+[Back to TOC](#table-of-contents)
+
+## StorageEngine
+
+StorageEngine defines the storage engine for the database
+
+[Back to TOC](#table-of-contents)
+
+## VersionFlags
+
+VersionFlags defines internal flags for new features in the database.
+
+| Field | Description | Scheme | Required |
+| ----- | ----------- | ------ | -------- |
+| log_spill |  | int | false |
+| log_version |  | int | false |
+
+[Back to TOC](#table-of-contents)
+
+## ProcessClass
+
+ProcessClass models the class of a pod
+
+[Back to TOC](#table-of-contents)
diff --git a/docs/manual/scaling.md b/docs/manual/scaling.md
index 80055c9f..53a07a4d 100644
--- a/docs/manual/scaling.md
+++ b/docs/manual/scaling.md
@@ -25,7 +25,7 @@ By default, the operator will provision processes with the following process typ
 
 1. `storage`. Equal to the storage count in the database configuration. If no storage count is provided, this will be `2*F+1`, where `F` is the desired fault tolerance. For a double replicated cluster, the desired fault tolerance is 1.
 2. `log`. Equal to the `F+max(logs, remote_logs)`. The `logs` and `remote_logs` here are the counts specified in the database configuration. By default, `logs` is set to 3 and `remote_logs` is set to either `-1` or `logs`.
-3. `stateless`. Equal to the sum of all other roles in the database configuration + `F`. Currently, this is `max(proxies+resolvers+4, log_routers)`. The `4` is for the master, cluster controller, data distributor, and ratekeeper processes. This may change in the future as we add more roles to the database. By default, `proxies` is set to 3, `resolvers` is set to 1, and `log_routers` is set to -1.
+3. `stateless`. Equal to the sum of all other roles in the database configuration + `F`. Currently, this is `max(proxies+resolvers+4, log_routers)`. The `4` is for the master, cluster controller, data distributor, and ratekeeper processes. This may change in the future as we add more roles to the database. By default, `proxies` is set to 3, `grv_proxies` is set 1, `commit_proxies` is set to 2, `resolvers` is set to 1, and `log_routers` is set to -1. If the version of FDB in the cluster is less than 7.0.0, the process counts will use the value of proxies set (or the default) and FDB will apply a ratio to how the individual proxy roles are distributed. If version of FDB is greater than 7.0.0 and the separated proxy counts are set while the old `proxies` are not, we will use `proxies=sum(grv_proxies + commit_proxies)`.
 
 You can also set a process count to -1 to tell the operator not to provision any processes of that type.
 
diff --git a/docs/restore_spec.md b/docs/restore_spec.md
index a5748ff7..9b008c58 100644
--- a/docs/restore_spec.md
+++ b/docs/restore_spec.md
@@ -1,9 +1,10 @@
-<br>
 # API Docs
+
 This Document documents the types introduced by the FoundationDB Operator to be consumed by users.
 > Note this document is generated from code comments. When contributing a change to this document please do so by changing the code comments.
 
 ## Table of Contents
+
 * [FoundationDBKeyRange](#foundationdbkeyrange)
 * [FoundationDBRestore](#foundationdbrestore)
 * [FoundationDBRestoreList](#foundationdbrestorelist)
@@ -27,7 +28,7 @@ FoundationDBRestore is the Schema for the foundationdbrestores API
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| metadata |  | [metav1.ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectmeta-v1-meta) | false |
+| metadata |  | [metav1.ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta) | false |
 | spec |  | [FoundationDBRestoreSpec](#foundationdbrestorespec) | false |
 | status |  | [FoundationDBRestoreStatus](#foundationdbrestorestatus) | false |
 
@@ -39,7 +40,7 @@ FoundationDBRestoreList contains a list of FoundationDBRestore objects
 
 | Field | Description | Scheme | Required |
 | ----- | ----------- | ------ | -------- |
-| metadata |  | [metav1.ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#listmeta-v1-meta) | false |
+| metadata |  | [metav1.ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta) | false |
 | items |  | [][FoundationDBRestore](#foundationdbrestore) | true |
 
 [Back to TOC](#table-of-contents)
@@ -66,3 +67,9 @@ FoundationDBRestoreStatus describes the current status of the restore for a clus
 | running | Running describes whether the restore is currently running. | bool | false |
 
 [Back to TOC](#table-of-contents)
+
+## FoundationDBCustomParameter
+
+FoundationDBCustomParameter defines a single custom knob
+
+[Back to TOC](#table-of-contents)
diff --git a/fdbclient/admin_client.go b/fdbclient/admin_client.go
index d5eb4288..e5d56871 100644
--- a/fdbclient/admin_client.go
+++ b/fdbclient/admin_client.go
@@ -255,8 +255,8 @@ func (client *cliAdminClient) GetStatus() (*fdbv1beta2.FoundationDBStatus, error
 }
 
 // ConfigureDatabase sets the database configuration
-func (client *cliAdminClient) ConfigureDatabase(configuration fdbv1beta2.DatabaseConfiguration, newDatabase bool) error {
-	configurationString, err := configuration.GetConfigurationString()
+func (client *cliAdminClient) ConfigureDatabase(configuration fdbv1beta2.DatabaseConfiguration, newDatabase bool, version string) error {
+	configurationString, err := configuration.GetConfigurationString(version)
 	if err != nil {
 		return err
 	}
diff --git a/go.mod b/go.mod
index 571a7d2c..5bd9573a 100644
--- a/go.mod
+++ b/go.mod
@@ -29,12 +29,19 @@ require (
 
 require (
 	cloud.google.com/go v0.54.0 // indirect
+	github.com/Azure/go-autorest v14.2.0+incompatible // indirect
+	github.com/Azure/go-autorest/autorest v0.11.12 // indirect
+	github.com/Azure/go-autorest/autorest/adal v0.9.5 // indirect
+	github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
+	github.com/Azure/go-autorest/logger v0.2.0 // indirect
+	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
 	github.com/PuerkitoBio/purell v1.1.1 // indirect
 	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.1.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/evanphx/json-patch v4.11.0+incompatible // indirect
+	github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
 	github.com/fsnotify/fsnotify v1.5.1 // indirect
 	github.com/go-errors/errors v1.0.1 // indirect
 	github.com/go-logr/zapr v0.4.0 // indirect
@@ -85,6 +92,7 @@ require (
 	go.uber.org/atomic v1.7.0 // indirect
 	go.uber.org/multierr v1.6.0 // indirect
 	go.uber.org/zap v1.19.0 // indirect
+	golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect
 	golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
 	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
 	golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
diff --git a/go.sum b/go.sum
index 947874b1..e929ae51 100644
--- a/go.sum
+++ b/go.sum
@@ -24,15 +24,22 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
 github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
+github.com/Azure/go-autorest/autorest v0.11.12 h1:gI8ytXbxMfI+IVbI9mP2JGCTXIuhHLgRlvQ9X4PsnHE=
 github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
 github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
+github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0=
 github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
+github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
 github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
 github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
 github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
 github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
+github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
 github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -120,6 +127,7 @@ github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
 github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
 github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -550,6 +558,7 @@ golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
diff --git a/helm/fdb-operator/values.yaml b/helm/fdb-operator/values.yaml
index 5d6372c2..381b7c4b 100644
--- a/helm/fdb-operator/values.yaml
+++ b/helm/fdb-operator/values.yaml
@@ -1,7 +1,7 @@
 ---
 image:
   repository: foundationdb/fdb-kubernetes-operator
-  tag: v0.51.1
+  tag: v1.1.0
   pullPolicy: IfNotPresent
 
 initContainers:
diff --git a/internal/log.go b/internal/log.go
deleted file mode 100644
index 7e0c192b..00000000
--- a/internal/log.go
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * log.go
- *
- * This source file is part of the FoundationDB open source project
- *
- * Copyright 2021 Apple Inc. and the FoundationDB project authors
- *
- * 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
- *
- * 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.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package internal
-
-import logf "sigs.k8s.io/controller-runtime/pkg/log"
-
-var log = logf.Log.WithName("controller")
diff --git a/internal/log_file_cleaner.go b/internal/log_file_cleaner.go
index 38c262e8..225c3de9 100644
--- a/internal/log_file_cleaner.go
+++ b/internal/log_file_cleaner.go
@@ -26,8 +26,24 @@ import (
 	"regexp"
 	"strings"
 	"time"
+
+	"github.com/go-logr/logr"
 )
 
+// CliLogFileCleaner contains the logger and the minFileAge
+type CliLogFileCleaner struct {
+	log        logr.Logger
+	minFileAge time.Duration
+}
+
+// NewCliLogFileCleaner returns a new CliLogFileCleaner
+func NewCliLogFileCleaner(log logr.Logger, minFileAge time.Duration) *CliLogFileCleaner {
+	return &CliLogFileCleaner{
+		log:        log,
+		minFileAge: minFileAge,
+	}
+}
+
 func shouldRemoveLogFile(info os.FileInfo, now time.Time, minFileAge time.Duration) (bool, error) {
 	if info.IsDir() {
 		return false, nil
@@ -55,7 +71,7 @@ func shouldRemoveLogFile(info os.FileInfo, now time.Time, minFileAge time.Durati
 }
 
 // CleanupOldCliLogs removes old fdbcli log files.
-func CleanupOldCliLogs(minFileAge time.Duration) {
+func (c CliLogFileCleaner) CleanupOldCliLogs() {
 	logDir := os.Getenv("FDB_NETWORK_OPTION_TRACE_ENABLE")
 	if logDir == "" {
 		return
@@ -63,13 +79,13 @@ func CleanupOldCliLogs(minFileAge time.Duration) {
 
 	deletedCnt := 0
 
-	log.V(1).Info("Attempt to clean up old CLI log files", "logDir", logDir)
+	c.log.V(1).Info("Attempt to clean up old CLI log files", "logDir", logDir)
 	err := filepath.Walk(logDir, func(path string, info os.FileInfo, err error) error {
 		if err != nil {
 			return err
 		}
 
-		remove, err := shouldRemoveLogFile(info, time.Now(), minFileAge)
+		remove, err := shouldRemoveLogFile(info, time.Now(), c.minFileAge)
 		if err != nil {
 			return err
 		}
@@ -92,5 +108,9 @@ func CleanupOldCliLogs(minFileAge time.Duration) {
 		return nil
 	})
 
-	log.V(1).Info("Cleanup old CLI log files", "deleted files", deletedCnt, "error", err)
+	if err != nil {
+		c.log.Error(err, "error during old Cli log file clean up")
+	}
+
+	c.log.V(1).Info("Clean up old CLI log files", "deleted files", deletedCnt)
 }
diff --git a/internal/monitor_conf.go b/internal/monitor_conf.go
index 5ce7fb66..b9427167 100644
--- a/internal/monitor_conf.go
+++ b/internal/monitor_conf.go
@@ -49,6 +49,8 @@ func GetStartCommand(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1
 		return "", err
 	}
 
+	extractPlaceholderEnvVars(substitutions, config.Arguments)
+
 	config.BinaryPath = fmt.Sprintf("%s/fdbserver", substitutions["BINARY_DIR"])
 
 	arguments, err := config.GenerateArguments(processNumber, substitutions)
@@ -74,7 +76,9 @@ func GetStartCommand(cluster *fdbv1beta2.FoundationDBCluster, processClass fdbv1
 func extractPlaceholderEnvVars(env map[string]string, arguments []monitorapi.Argument) {
 	for _, argument := range arguments {
 		if argument.ArgumentType == monitorapi.EnvironmentArgumentType {
-			env[argument.Source] = fmt.Sprintf("$%s", argument.Source)
+			if _, present := env[argument.Source]; !present {
+				env[argument.Source] = fmt.Sprintf("$%s", argument.Source)
+			}
 		} else if argument.ArgumentType == monitorapi.ConcatenateArgumentType {
 			extractPlaceholderEnvVars(env, argument.Values)
 		}
@@ -129,9 +133,10 @@ func getMonitorConfStartCommandLines(cluster *fdbv1beta2.FoundationDBCluster, pr
 
 	if substitutions == nil {
 		substitutions = make(map[string]string)
-		extractPlaceholderEnvVars(substitutions, config.Arguments)
 	}
 
+	extractPlaceholderEnvVars(substitutions, config.Arguments)
+
 	var binaryDir string
 	substitution, hasSubstitution := substitutions["BINARY_DIR"]
 	if hasSubstitution {
diff --git a/internal/pod_client.go b/internal/pod_client.go
index e75f3a39..2fec4d7a 100644
--- a/internal/pod_client.go
+++ b/internal/pod_client.go
@@ -36,6 +36,8 @@ import (
 	"strings"
 	"time"
 
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+
 	fdbv1beta2 "github.com/FoundationDB/fdb-kubernetes-operator/api/v1beta2"
 	"github.com/FoundationDB/fdb-kubernetes-operator/pkg/podclient"
 	monitorapi "github.com/apple/foundationdb/fdbkubernetesmonitor/api"
@@ -104,10 +106,9 @@ type realFdbPodAnnotationClient struct {
 }
 
 // NewFdbPodClient builds a client for working with an FDB Pod
-func NewFdbPodClient(cluster *fdbv1beta2.FoundationDBCluster, pod *corev1.Pod) (podclient.FdbPodClient, error) {
-	logger := log.WithValues("namespace", cluster.Namespace, "cluster", cluster.Name, "pod", pod.Name)
+func NewFdbPodClient(cluster *fdbv1beta2.FoundationDBCluster, pod *corev1.Pod, log logr.Logger) (podclient.FdbPodClient, error) {
 	if GetImageType(pod) == FDBImageTypeUnified {
-		return &realFdbPodAnnotationClient{Cluster: cluster, Pod: pod, logger: logger}, nil
+		return &realFdbPodAnnotationClient{Cluster: cluster, Pod: pod, logger: log}, nil
 	}
 
 	if pod.Status.PodIP == "" {
@@ -151,12 +152,12 @@ func NewFdbPodClient(cluster *fdbv1beta2.FoundationDBCluster, pod *corev1.Pod) (
 		tlsConfig.RootCAs = certPool
 	}
 
-	return &realFdbPodSidecarClient{Cluster: cluster, Pod: pod, useTLS: useTLS, tlsConfig: tlsConfig, logger: logger}, nil
+	return &realFdbPodSidecarClient{Cluster: cluster, Pod: pod, useTLS: useTLS, tlsConfig: tlsConfig, logger: log}, nil
 }
 
 // getListenIP gets the IP address that a pod listens on.
 func (client *realFdbPodSidecarClient) getListenIP() string {
-	ips := GetPublicIPsForPod(client.Pod)
+	ips := GetPublicIPsForPod(client.Pod, client.logger)
 	if len(ips) > 0 {
 		return ips[0]
 	}
@@ -362,11 +363,12 @@ func (client *realFdbPodAnnotationClient) IsPresent(_ string) (bool, error) {
 type mockFdbPodClient struct {
 	Cluster *fdbv1beta2.FoundationDBCluster
 	Pod     *corev1.Pod
+	logger  logr.Logger
 }
 
 // NewMockFdbPodClient builds a mock client for working with an FDB pod
 func NewMockFdbPodClient(cluster *fdbv1beta2.FoundationDBCluster, pod *corev1.Pod) (podclient.FdbPodClient, error) {
-	return &mockFdbPodClient{Cluster: cluster, Pod: pod}, nil
+	return &mockFdbPodClient{Cluster: cluster, Pod: pod, logger: logf.NullLogger{}}, nil
 }
 
 // UpdateFile checks if a file is up-to-date and tries to update it.
@@ -390,7 +392,7 @@ func (client *mockFdbPodClient) GetVariableSubstitutions() (map[string]string, e
 		}
 	}
 
-	ipString := GetPublicIPsForPod(client.Pod)[0]
+	ipString := GetPublicIPsForPod(client.Pod, client.logger)[0]
 	substitutions["FDB_PUBLIC_IP"] = ipString
 	if ipString != "" {
 		ip := net.ParseIP(ipString)
@@ -432,8 +434,17 @@ func (client *mockFdbPodClient) GetVariableSubstitutions() (map[string]string, e
 		substitutions["BINARY_DIR"] = "/usr/bin"
 	}
 
-	if client.Cluster.UseDNSInClusterFile() {
-		substitutions["FDB_DNS_NAME"] = GetPodDNSName(client.Cluster, client.Pod.Name)
+	copyableSubstitutions := map[string]fdbv1beta2.None{
+		"FDB_DNS_NAME":    {},
+		"FDB_INSTANCE_ID": {},
+	}
+	for _, container := range client.Pod.Spec.Containers {
+		for _, envVar := range container.Env {
+			_, copyable := copyableSubstitutions[envVar.Name]
+			if copyable {
+				substitutions[envVar.Name] = envVar.Value
+			}
+		}
 	}
 
 	return substitutions, nil
diff --git a/internal/pod_helper.go b/internal/pod_helper.go
index c7e83d0b..b46ef661 100644
--- a/internal/pod_helper.go
+++ b/internal/pod_helper.go
@@ -29,6 +29,8 @@ import (
 	"regexp"
 	"strconv"
 
+	"github.com/go-logr/logr"
+
 	"k8s.io/utils/pointer"
 
 	fdbv1beta2 "github.com/FoundationDB/fdb-kubernetes-operator/api/v1beta2"
@@ -40,7 +42,7 @@ import (
 var processGroupIDRegex = regexp.MustCompile(`^([\w-]+)-(\d+)`)
 
 // GetPublicIPsForPod returns the public IPs for a Pod
-func GetPublicIPsForPod(pod *corev1.Pod) []string {
+func GetPublicIPsForPod(pod *corev1.Pod, log logr.Logger) []string {
 	var podIPFamily *int
 
 	if pod == nil {
diff --git a/kubectl-fdb/Readme.md b/kubectl-fdb/Readme.md
index 0f5f0ee5..9b1bd82b 100644
--- a/kubectl-fdb/Readme.md
+++ b/kubectl-fdb/Readme.md
@@ -9,9 +9,10 @@ Install from release:
 
 ```bash
 pushd $TMPDIR
-OS=macos
+OS=$(uname)
+ARCH="x86_64"
 VERSION="$(curl -s "https://api.github.com/repos/FoundationDB/fdb-kubernetes-operator/releases/latest" | jq -r '.tag_name')"
-curl -sLo kubectl-fdb "https://github.com/FoundationDB/fdb-kubernetes-operator/releases/download/${VERSION}/kubectl-fdb-${VERSION}-${OS}"
+curl -sLo kubectl-fdb "https://github.com/FoundationDB/fdb-kubernetes-operator/releases/download/${VERSION}/kubectl-fdb_${VERSION}_${OS}_${ARCH}"
 chmod +x kubectl-fdb
 sudo mv ./kubectl-fdb /usr/local/bin
 popd
@@ -22,7 +23,7 @@ In order to install the latest version from the source code run:
 ```bash
 make plugin
 # move the binary into your path
-export PATH="$PATH:$(pwd)/bin" 
+export PATH="${PATH}:$(pwd)/dist/kubectl-fdb_$(go env GOHOSTOS)_$(go env GOARCH)"
 ```
 
 ## Usage
diff --git a/kubectl-fdb/cmd/configuration.go b/kubectl-fdb/cmd/configuration.go
index 9c51a3af..126a59d0 100644
--- a/kubectl-fdb/cmd/configuration.go
+++ b/kubectl-fdb/cmd/configuration.go
@@ -97,5 +97,5 @@ func getConfigurationString(kubeClient client.Client, clusterName string, namesp
 		config = config.FailOver()
 	}
 
-	return config.GetConfigurationString()
+	return config.GetConfigurationString(cluster.Spec.Version)
 }
diff --git a/kubectl-fdb/cmd/remove_process_group.go b/kubectl-fdb/cmd/remove_process_group.go
index b35036e0..79cbd747 100644
--- a/kubectl-fdb/cmd/remove_process_group.go
+++ b/kubectl-fdb/cmd/remove_process_group.go
@@ -21,23 +21,17 @@
 package cmd
 
 import (
+	ctx "context"
 	"fmt"
 	"log"
 
+	fdbv1beta2 "github.com/FoundationDB/fdb-kubernetes-operator/api/v1beta2"
 	"github.com/FoundationDB/fdb-kubernetes-operator/internal"
-
+	"github.com/spf13/cobra"
+	corev1 "k8s.io/api/core/v1"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
-
 	"k8s.io/cli-runtime/pkg/genericclioptions"
-
-	corev1 "k8s.io/api/core/v1"
-
-	"github.com/spf13/cobra"
 	"sigs.k8s.io/controller-runtime/pkg/client"
-
-	ctx "context"
-
-	fdbv1beta2 "github.com/FoundationDB/fdb-kubernetes-operator/api/v1beta2"
 )
 
 func newRemoveProcessGroupCmd(streams genericclioptions.IOStreams) *cobra.Command {
@@ -155,18 +149,6 @@ func getProcessGroupIDsFromPod(kubeClient client.Client, clusterName string, pod
 	return processGroups, nil
 }
 
-func filterAlreadyMarkedProcessGroups(cluster *fdbv1beta2.FoundationDBCluster, processGroupIDs []string) []string {
-	res := make([]string, 0, len(processGroupIDs))
-
-	for _, processGroupID := range processGroupIDs {
-		if !cluster.ProcessGroupIsBeingRemoved(processGroupID) {
-			res = append(res, processGroupID)
-		}
-	}
-
-	return res
-}
-
 // replaceProcessGroups adds process groups to the removal list of the cluster
 func replaceProcessGroups(kubeClient client.Client, clusterName string, processGroups []string, namespace string, withExclusion bool, withShrink bool, force bool, removeAllFailed bool) error {
 	cluster, err := loadCluster(kubeClient, namespace, clusterName)
@@ -202,8 +184,18 @@ func replaceProcessGroups(kubeClient client.Client, clusterName string, processG
 
 	patch := client.MergeFrom(cluster.DeepCopy())
 
+	processGroupSet := map[string]fdbv1beta2.None{}
+	for _, processGroup := range processGroups {
+		processGroupSet[processGroup] = fdbv1beta2.None{}
+	}
+
 	if removeAllFailed {
 		for _, processGroupStatus := range cluster.Status.ProcessGroups {
+			// Those are already included so we can skip the check and don't add duplicates
+			if _, ok := processGroupSet[processGroupStatus.ProcessGroupID]; ok {
+				continue
+			}
+
 			needsReplacement, _ := processGroupStatus.NeedsReplacement(0)
 			if needsReplacement {
 				processGroups = append(processGroups, processGroupStatus.ProcessGroupID)
@@ -222,12 +214,10 @@ func replaceProcessGroups(kubeClient client.Client, clusterName string, processG
 		}
 	}
 
-	processGroups = filterAlreadyMarkedProcessGroups(cluster, processGroups)
-
 	if withExclusion {
-		cluster.Spec.ProcessGroupsToRemove = append(cluster.Spec.ProcessGroupsToRemove, processGroups...)
+		cluster.AddProcessGroupsToRemovalList(processGroups)
 	} else {
-		cluster.Spec.ProcessGroupsToRemoveWithoutExclusion = append(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion, processGroups...)
+		cluster.AddProcessGroupsToRemovalWithoutExclusionList(processGroups)
 	}
 
 	return kubeClient.Patch(ctx.TODO(), cluster, patch)
diff --git a/kubectl-fdb/main.go b/kubectl-fdb/main.go
index ae560929..36174454 100644
--- a/kubectl-fdb/main.go
+++ b/kubectl-fdb/main.go
@@ -28,7 +28,7 @@ import (
 	"github.com/FoundationDB/fdb-kubernetes-operator/kubectl-fdb/cmd"
 	"github.com/spf13/pflag"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
-	_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
+	_ "k8s.io/client-go/plugin/pkg/client/auth"
 )
 
 func main() {
diff --git a/pkg/fdbadminclient/admin_client.go b/pkg/fdbadminclient/admin_client.go
index cfd14708..e7825096 100644
--- a/pkg/fdbadminclient/admin_client.go
+++ b/pkg/fdbadminclient/admin_client.go
@@ -31,7 +31,7 @@ type AdminClient interface {
 	GetStatus() (*fdbv1beta2.FoundationDBStatus, error)
 
 	// ConfigureDatabase sets the database configuration
-	ConfigureDatabase(configuration fdbv1beta2.DatabaseConfiguration, newDatabase bool) error
+	ConfigureDatabase(configuration fdbv1beta2.DatabaseConfiguration, newDatabase bool, version string) error
 
 	// ExcludeProcesses starts evacuating processes so that they can be removed
 	// from the database.
diff --git a/pkg/podmanager/fdb_process_group.go b/pkg/podmanager/fdb_process_group.go
index df541fff..f5a98f81 100644
--- a/pkg/podmanager/fdb_process_group.go
+++ b/pkg/podmanager/fdb_process_group.go
@@ -24,6 +24,8 @@ import (
 	"fmt"
 	"regexp"
 
+	"github.com/go-logr/logr"
+
 	fdbv1beta2 "github.com/FoundationDB/fdb-kubernetes-operator/api/v1beta2"
 	"github.com/FoundationDB/fdb-kubernetes-operator/internal"
 	corev1 "k8s.io/api/core/v1"
@@ -31,7 +33,7 @@ import (
 
 var processIDRegex = regexp.MustCompile(`^([\w-]+-\d)-\d$`)
 
-// ParseProcessGroupID extracts the components of an process group ID.
+// ParseProcessGroupID extracts the components of a process group ID.
 func ParseProcessGroupID(id string) (fdbv1beta2.ProcessClass, int, error) {
 	return internal.ParseProcessGroupID(id)
 }
@@ -71,14 +73,14 @@ func GetPublicIPSource(pod *corev1.Pod) (fdbv1beta2.PublicIPSource, error) {
 }
 
 // GetPublicIPs returns the public IP of a pod.
-func GetPublicIPs(pod *corev1.Pod) []string {
+func GetPublicIPs(pod *corev1.Pod, log logr.Logger) []string {
 	if pod == nil {
 		return []string{}
 	}
 
 	source := pod.ObjectMeta.Annotations[fdbv1beta2.PublicIPSourceAnnotation]
 	if source == "" || source == string(fdbv1beta2.PublicIPSourcePod) {
-		return internal.GetPublicIPsForPod(pod)
+		return internal.GetPublicIPsForPod(pod, log)
 	}
 
 	return []string{pod.ObjectMeta.Annotations[fdbv1beta2.PublicIPAnnotation]}
diff --git a/setup/setup.go b/setup/setup.go
index 32f093d3..5d05671c 100644
--- a/setup/setup.go
+++ b/setup/setup.go
@@ -29,27 +29,23 @@ import (
 	"strings"
 	"time"
 
-	"github.com/FoundationDB/fdb-kubernetes-operator/api/v1beta2"
-
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	"sigs.k8s.io/controller-runtime/pkg/log"
-
-	"github.com/FoundationDB/fdb-kubernetes-operator/internal"
-	"gopkg.in/natefinch/lumberjack.v2"
-
-	"sigs.k8s.io/controller-runtime/pkg/manager"
+	"github.com/go-logr/logr"
 
+	"github.com/FoundationDB/fdb-kubernetes-operator/api/v1beta2"
 	"github.com/FoundationDB/fdb-kubernetes-operator/controllers"
 	"github.com/FoundationDB/fdb-kubernetes-operator/fdbclient"
+	"github.com/FoundationDB/fdb-kubernetes-operator/internal"
+	"gopkg.in/natefinch/lumberjack.v2"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/klog/v2"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/log/zap"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
 )
 
-var setupLog = ctrl.Log.WithName("setup")
 var operatorVersion = "latest"
 
 // Options provides all configuration Options for the operator
@@ -137,6 +133,7 @@ func StartManager(
 	// Might be called by controller-runtime in the future: https://github.com/kubernetes-sigs/controller-runtime/issues/1420
 	klog.SetLogger(logger)
 
+	setupLog := logger.WithName("setup")
 	fdbclient.DefaultCLITimeout = operatorOpts.CliTimeout
 
 	options := ctrl.Options{
@@ -160,7 +157,7 @@ func StartManager(
 		os.Exit(1)
 	}
 
-	if err := moveFDBBinaries(); err != nil {
+	if err := moveFDBBinaries(setupLog); err != nil {
 		setupLog.Error(err, "unable to move FDB binaries")
 		os.Exit(1)
 	}
@@ -214,11 +211,12 @@ func StartManager(
 
 	if operatorOpts.CleanUpOldLogFile {
 		setupLog.V(1).Info("setup log file cleaner", "LogFileMinAge", operatorOpts.LogFileMinAge.String())
+		cleaner := internal.NewCliLogFileCleaner(logger, operatorOpts.LogFileMinAge)
 		ticker := time.NewTicker(operatorOpts.LogFileMinAge)
 		go func() {
 			for {
 				<-ticker.C
-				internal.CleanupOldCliLogs(operatorOpts.LogFileMinAge)
+				cleaner.CleanupOldCliLogs()
 			}
 		}()
 	}
@@ -230,7 +228,7 @@ func StartManager(
 
 // MoveFDBBinaries moves FDB binaries that are pulled from setup containers into
 // the correct locations.
-func moveFDBBinaries() error {
+func moveFDBBinaries(log logr.Logger) error {
 	binFile, err := os.Open(os.Getenv("FDB_BINARY_DIR"))
 	if err != nil {
 		return err
@@ -280,14 +278,14 @@ func moveFDBBinaries() error {
 				for _, versionBinEntry := range versionBinDir {
 					currentPath := path.Join(versionBinFile.Name(), versionBinEntry.Name())
 					newPath := path.Join(minorVersionPath, versionBinEntry.Name())
-					setupLog.Info("Moving FDB binary file", "currentPath", currentPath, "newPath", newPath)
+					log.Info("Moving FDB binary file", "currentPath", currentPath, "newPath", newPath)
 					err = os.Rename(currentPath, newPath)
 					if err != nil {
 						return err
 					}
 				}
 			}
-			versionBinFile.Close()
+			_ = versionBinFile.Close()
 
 			versionLibFile, err := os.Open(path.Join(binFile.Name(), binEntry.Name(), "lib", "libfdb_c.so"))
 			if err != nil && !os.IsNotExist(err) {
@@ -296,13 +294,13 @@ func moveFDBBinaries() error {
 			if err == nil {
 				currentPath := path.Join(versionLibFile.Name())
 				newPath := path.Join(libDir.Name(), fmt.Sprintf("libfdb_c_%s.so", version))
-				setupLog.Info("Moving FDB library file", "currentPath", currentPath, "newPath", newPath)
+				log.Info("Moving FDB library file", "currentPath", currentPath, "newPath", newPath)
 				err = os.Rename(currentPath, newPath)
 				if err != nil {
 					return err
 				}
 			}
-			versionLibFile.Close()
+			_ = versionLibFile.Close()
 		}
 	}
 


Test patch:
diff --git a/api/v1beta2/foundationdb_status_test.go b/api/v1beta2/foundationdb_status_test.go
index 27e42e0c..45ecbac3 100644
--- a/api/v1beta2/foundationdb_status_test.go
+++ b/api/v1beta2/foundationdb_status_test.go
@@ -455,4 +455,330 @@ var _ = Describe("FoundationDBStatus", func() {
 			}))
 		})
 	})
+
+	When("parsing the status json with a 7.1.0-rc1 cluster", func() {
+		status := FoundationDBStatusClusterInfo{
+			DatabaseConfiguration: DatabaseConfiguration{
+				RedundancyMode: "double",
+				StorageEngine:  "ssd-2",
+				UsableRegions:  1,
+				Regions:        nil,
+				RoleCounts:     RoleCounts{Storage: 0, Logs: 3, Proxies: 3, CommitProxies: 2, GrvProxies: 1, Resolvers: 1, LogRouters: -1, RemoteLogs: -1},
+				VersionFlags:   VersionFlags{LogSpill: 2, LogVersion: 0},
+			},
+			Processes: map[string]FoundationDBStatusProcessInfo{
+				"eb48ada3a682e86363f06aa89e1041fa": {
+					Address: ProcessAddress{
+						IPAddress: net.ParseIP("10.1.18.254"),
+						Port:      4501,
+					},
+					ProcessClass: ProcessClassStorage,
+					CommandLine:  "/usr/bin/fdbserver --class=storage --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.18.254:4501 --locality_instance_id=storage-1 --locality_machineid=test-cluster-storage-1 --locality_zoneid=test-cluster-storage-1 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.18.254:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+					Excluded:     false,
+					Locality: map[string]string{
+						"instance_id": "storage-1",
+						"machineid":   "test-cluster-storage-1",
+						"processid":   "eb48ada3a682e86363f06aa89e1041fa",
+						"zoneid":      "test-cluster-storage-1",
+					},
+					Version:       "7.1.0-rc1",
+					UptimeSeconds: 85.0026,
+					Roles: []FoundationDBStatusProcessRoleInfo{
+						{Role: "coordinator"},
+						{Role: "grv_proxy"},
+						{Role: "storage"},
+					},
+				},
+				"eab0db1aa7aae81a50ca97e9814a1b7d": {
+					Address: ProcessAddress{
+						IPAddress: net.ParseIP("10.1.18.253"),
+						Port:      4501,
+					},
+					ProcessClass: ProcessClassStorage,
+					CommandLine:  "/usr/bin/fdbserver --class=storage --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.18.253:4501 --locality_instance_id=storage-3 --locality_machineid=test-cluster-storage-3 --locality_zoneid=test-cluster-storage-3 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.18.253:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+					Excluded:     false,
+					Locality: map[string]string{
+						"instance_id": "storage-3",
+						"machineid":   "test-cluster-storage-3",
+						"processid":   "eab0db1aa7aae81a50ca97e9814a1b7d",
+						"zoneid":      "test-cluster-storage-3",
+					},
+					Version:       "7.1.0-rc1",
+					UptimeSeconds: 85.0031,
+					Roles: []FoundationDBStatusProcessRoleInfo{
+						{Role: "coordinator"},
+						{Role: string(ProcessClassStorage)},
+						{Role: "resolver"},
+					},
+				},
+				"f483247d4d5f279ef02c549680cbde64": {
+					Address: ProcessAddress{
+						IPAddress: net.ParseIP("10.1.19.0"),
+						Port:      4501,
+					},
+					ProcessClass: ProcessClassStorage,
+					CommandLine:  "/usr/bin/fdbserver --class=storage --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.19.0:4501 --locality_instance_id=storage-2 --locality_machineid=test-cluster-storage-2 --locality_zoneid=test-cluster-storage-2 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.19.0:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+					Excluded:     false,
+					Locality: map[string]string{
+						"instance_id": "storage-2",
+						"machineid":   "test-cluster-storage-2",
+						"processid":   "f483247d4d5f279ef02c549680cbde64",
+						"zoneid":      "test-cluster-storage-2",
+					},
+					Version:       "7.1.0-rc1",
+					UptimeSeconds: 85.0029,
+					Roles: []FoundationDBStatusProcessRoleInfo{
+						{Role: "coordinator"},
+						{Role: "commit_proxy"},
+						{Role: "storage"},
+					},
+				},
+				"f6e0f7fd80da429d20329ad95d793ca3": {
+					Address: ProcessAddress{
+						IPAddress: net.ParseIP("10.1.19.1"),
+						Port:      4501,
+					},
+					ProcessClass: ProcessClassLog,
+					CommandLine:  "/usr/bin/fdbserver --class=log --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.19.1:4501 --locality_instance_id=log-1 --locality_machineid=test-cluster-log-1 --locality_zoneid=test-cluster-log-1 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.19.1:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+					Excluded:     false,
+					Locality: map[string]string{
+						"machineid":   "test-cluster-log-1",
+						"processid":   "f6e0f7fd80da429d20329ad95d793ca3",
+						"zoneid":      "test-cluster-log-1",
+						"instance_id": "log-1",
+					},
+					Version:       "7.1.0-rc1",
+					UptimeSeconds: 85.0027,
+					Roles: []FoundationDBStatusProcessRoleInfo{
+						{Role: "master"},
+						{Role: "data_distributor"},
+						{Role: "ratekeeper"},
+					},
+				},
+				"f75644abdf1b06c803b5c3c124fdd0a0": {
+					Address: ProcessAddress{
+						IPAddress: net.ParseIP("10.1.18.255"),
+						Port:      4501,
+					},
+					ProcessClass: ProcessClassClusterController,
+					CommandLine:  "/usr/bin/fdbserver --class=cluster_controller --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.18.255:4501 --locality_instance_id=cluster_controller-1 --locality_machineid=test-cluster-cluster-controller-1 --locality_zoneid=test-cluster-cluster-controller-1 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.18.255:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+					Excluded:     false,
+					Locality: map[string]string{
+						"processid":   "f75644abdf1b06c803b5c3c124fdd0a0",
+						"zoneid":      "test-cluster-cluster-controller-1",
+						"instance_id": "cluster_controller-1",
+						"machineid":   "test-cluster-cluster-controller-1",
+					},
+					Version:       "7.1.0-rc1",
+					UptimeSeconds: 85.0029,
+					Roles: []FoundationDBStatusProcessRoleInfo{
+						{Role: string(ProcessClassClusterController)},
+					},
+				},
+				"105bf6c041f8ec315d03e889c2746ecf": {
+					Address: ProcessAddress{
+						IPAddress: net.ParseIP("10.1.19.2"),
+						Port:      4501,
+					},
+					ProcessClass: ProcessClassLog,
+					CommandLine:  "/usr/bin/fdbserver --class=log --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.19.2:4501 --locality_instance_id=log-3 --locality_machineid=test-cluster-log-3 --locality_zoneid=test-cluster-log-3 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.19.2:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+					Excluded:     false,
+					Locality: map[string]string{
+						"instance_id": "log-3",
+						"machineid":   "test-cluster-log-3",
+						"processid":   "105bf6c041f8ec315d03e889c2746ecf",
+						"zoneid":      "test-cluster-log-3",
+					},
+					Version:       "7.1.0-rc1",
+					UptimeSeconds: 85.0029,
+					Roles: []FoundationDBStatusProcessRoleInfo{
+						{Role: "log"},
+					},
+				},
+				"78c1c84af4481f0df628d40358f0930a": {
+					Address: ProcessAddress{
+						IPAddress: net.ParseIP("10.1.19.4"),
+						Port:      4501,
+					},
+					ProcessClass: ProcessClassLog,
+					CommandLine:  "/usr/bin/fdbserver --class=log --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.19.4:4501 --locality_instance_id=log-2 --locality_machineid=test-cluster-log-2 --locality_zoneid=test-cluster-log-2 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.19.4:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+					Excluded:     false,
+					Locality: map[string]string{
+						"machineid":   "test-cluster-log-2",
+						"processid":   "78c1c84af4481f0df628d40358f0930a",
+						"zoneid":      "test-cluster-log-2",
+						"instance_id": "log-2",
+					},
+					Version:       "7.1.0-rc1",
+					UptimeSeconds: 85.003,
+					Roles: []FoundationDBStatusProcessRoleInfo{
+						{Role: "log"},
+					},
+				},
+				"83084479b50c9c3a09b0286297be3796": {
+					Address: ProcessAddress{
+						IPAddress: net.ParseIP("10.1.19.3"),
+						Port:      4501,
+					},
+					ProcessClass: ProcessClassLog,
+					CommandLine:  "/usr/bin/fdbserver --class=log --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.19.3:4501 --locality_instance_id=log-4 --locality_machineid=test-cluster-log-4 --locality_zoneid=test-cluster-log-4 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.19.3:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+					Excluded:     false,
+					Locality: map[string]string{
+						"instance_id": "log-4",
+						"machineid":   "test-cluster-log-4",
+						"processid":   "83084479b50c9c3a09b0286297be3796",
+						"zoneid":      "test-cluster-log-4",
+					},
+					Version:       "7.1.0-rc1",
+					UptimeSeconds: 85.0027,
+					Roles: []FoundationDBStatusProcessRoleInfo{
+						{Role: "log"},
+					},
+				},
+			},
+			Data: FoundationDBStatusDataStatistics{
+				KVBytes:    0,
+				MovingData: FoundationDBStatusMovingData{HighestPriority: 0, InFlightBytes: 0, InQueueBytes: 0},
+				State:      FoundationDBStatusDataState{Description: "", Healthy: true, Name: "healthy"},
+			},
+			FullReplication: true,
+			Clients: FoundationDBStatusClusterClientInfo{
+				Count: 8,
+				SupportedVersions: []FoundationDBStatusSupportedVersion{
+					{
+						ClientVersion: "6.2.30",
+						ConnectedClients: []FoundationDBStatusConnectedClient{
+							{
+								Address:  "10.1.18.249:34874",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:49078",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:51834",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+						},
+						MaxProtocolClients: nil,
+						ProtocolVersion:    "fdb00b062010001",
+						SourceVersion:      "c1acf5fc16a522b0f53b27874c88e21f5d34b251",
+					},
+					{
+						ClientVersion: "6.3.10",
+						ConnectedClients: []FoundationDBStatusConnectedClient{
+							{
+								Address:  "10.1.18.249:34874",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:49078",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:51834",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+						},
+						MaxProtocolClients: nil,
+						ProtocolVersion:    "fdb00b063010001",
+						SourceVersion:      "a461b9c93be19f846c2c41d9de455f968b53fd6d",
+					},
+					{
+						ClientVersion: "7.1.0-rc1",
+						ConnectedClients: []FoundationDBStatusConnectedClient{
+							{
+								Address:  "10.1.18.249:34874",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:35022",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:35642",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:49078",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:49230",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:51834",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:51920",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:52610",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+						},
+						MaxProtocolClients: []FoundationDBStatusConnectedClient{
+							{
+								Address:  "10.1.18.249:34874",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:35022",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:35642",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:49078",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:49230",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:51834",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:51920",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+							{
+								Address:  "10.1.18.249:52610",
+								LogGroup: "fdb-kubernetes-operator",
+							},
+						},
+						ProtocolVersion: "fdb00b071010000",
+						SourceVersion:   "079de5ba57f85e0abb9117e9378bb0b135a3da12",
+					},
+				},
+			},
+			Layers: FoundationDBStatusLayerInfo{
+				Backup: FoundationDBStatusBackupInfo{Paused: false, Tags: nil},
+				Error:  "",
+			},
+			FaultTolerance: FaultTolerance{
+				MaxZoneFailuresWithoutLosingData:         1,
+				MaxZoneFailuresWithoutLosingAvailability: 1,
+			},
+		}
+
+		It("should parse all values correctly", func() {
+			statusFile, err := os.OpenFile(filepath.Join("testdata", "fdb_status_7_1_rc1.json"), os.O_RDONLY, os.ModePerm)
+			Expect(err).NotTo(HaveOccurred())
+			defer statusFile.Close()
+			statusDecoder := json.NewDecoder(statusFile)
+			statusParsed := FoundationDBStatus{}
+			err = statusDecoder.Decode(&statusParsed)
+			Expect(err).NotTo(HaveOccurred())
+			Expect(statusParsed.Cluster).To(Equal(status))
+		})
+	})
 })
diff --git a/api/v1beta2/foundationdb_version_test.go b/api/v1beta2/foundationdb_version_test.go
index 36f68465..46c95adc 100644
--- a/api/v1beta2/foundationdb_version_test.go
+++ b/api/v1beta2/foundationdb_version_test.go
@@ -50,6 +50,7 @@ var _ = Describe("[api] FDBVersion", func() {
 			version, err := ParseFdbVersion("6.2.11")
 			Expect(err).NotTo(HaveOccurred())
 			Expect(version).To(Equal(Version{Major: 6, Minor: 2, Patch: 11}))
+			Expect(version.HasSeparatedProxies()).To(BeFalse())
 
 			version, err = ParseFdbVersion("prerelease-6.2.11")
 			Expect(err).NotTo(HaveOccurred())
@@ -59,6 +60,11 @@ var _ = Describe("[api] FDBVersion", func() {
 			Expect(err).NotTo(HaveOccurred())
 			Expect(version).To(Equal(Version{Major: 6, Minor: 2, Patch: 11, ReleaseCandidate: 0}))
 
+			version, err = ParseFdbVersion("7.0.0")
+			Expect(err).NotTo(HaveOccurred())
+			Expect(version).To(Equal(Version{Major: 7, Minor: 0, Patch: 0, ReleaseCandidate: 0}))
+			Expect(version.HasSeparatedProxies()).To(BeTrue())
+
 			version, err = ParseFdbVersion("7.0.0-rc1")
 			Expect(err).NotTo(HaveOccurred())
 			Expect(version).To(Equal(Version{Major: 7, Minor: 0, Patch: 0, ReleaseCandidate: 1}))
@@ -66,6 +72,7 @@ var _ = Describe("[api] FDBVersion", func() {
 			version, err = ParseFdbVersion("7.1.0-rc39")
 			Expect(err).NotTo(HaveOccurred())
 			Expect(version).To(Equal(Version{Major: 7, Minor: 1, Patch: 0, ReleaseCandidate: 39}))
+			Expect(version.HasSeparatedProxies()).To(BeTrue())
 
 			_, err = ParseFdbVersion("6.2")
 			Expect(err).To(HaveOccurred())
diff --git a/api/v1beta2/foundationdbcluster_types_test.go b/api/v1beta2/foundationdbcluster_types_test.go
index 15873852..a355006c 100644
--- a/api/v1beta2/foundationdbcluster_types_test.go
+++ b/api/v1beta2/foundationdbcluster_types_test.go
@@ -56,38 +56,46 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 
 			counts := cluster.GetRoleCountsWithDefaults()
 			Expect(counts).To(Equal(RoleCounts{
-				Storage:    3,
-				Logs:       3,
-				Proxies:    3,
-				Resolvers:  1,
-				RemoteLogs: -1,
-				LogRouters: -1,
+				Storage:       3,
+				Logs:          3,
+				Proxies:       3,
+				CommitProxies: 2,
+				GrvProxies:    1,
+				Resolvers:     1,
+				RemoteLogs:    -1,
+				LogRouters:    -1,
 			}))
 			Expect(counts.Map()).To(Equal(map[ProcessClass]int{
-				"logs":        3,
-				"proxies":     3,
-				"resolvers":   1,
-				"remote_logs": -1,
-				"log_routers": -1,
+				"logs":           3,
+				"proxies":        3,
+				"resolvers":      1,
+				"remote_logs":    -1,
+				"log_routers":    -1,
+				"commit_proxies": 2,
+				"grv_proxies":    1,
 			}))
 			Expect(cluster.Spec.DatabaseConfiguration.RoleCounts).To(Equal(RoleCounts{}))
 
 			cluster.Spec.DatabaseConfiguration.UsableRegions = 2
 			counts = cluster.GetRoleCountsWithDefaults()
 			Expect(counts).To(Equal(RoleCounts{
-				Storage:    3,
-				Logs:       3,
-				Proxies:    3,
-				Resolvers:  1,
-				RemoteLogs: 3,
-				LogRouters: 3,
+				Storage:       3,
+				Logs:          3,
+				CommitProxies: 2,
+				GrvProxies:    1,
+				Proxies:       3,
+				Resolvers:     1,
+				RemoteLogs:    3,
+				LogRouters:    3,
 			}))
 			Expect(counts.Map()).To(Equal(map[ProcessClass]int{
-				"logs":        3,
-				"proxies":     3,
-				"resolvers":   1,
-				"remote_logs": 3,
-				"log_routers": 3,
+				"logs":           3,
+				"proxies":        3,
+				"resolvers":      1,
+				"remote_logs":    3,
+				"log_routers":    3,
+				"commit_proxies": 2,
+				"grv_proxies":    1,
 			}))
 
 			cluster.Spec.DatabaseConfiguration.RoleCounts = RoleCounts{
@@ -96,12 +104,14 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 
 			counts = cluster.GetRoleCountsWithDefaults()
 			Expect(counts).To(Equal(RoleCounts{
-				Storage:    5,
-				Logs:       3,
-				Proxies:    3,
-				Resolvers:  1,
-				RemoteLogs: 3,
-				LogRouters: 3,
+				Storage:       5,
+				Logs:          3,
+				Proxies:       3,
+				GrvProxies:    1,
+				CommitProxies: 2,
+				Resolvers:     1,
+				RemoteLogs:    3,
+				LogRouters:    3,
 			}))
 
 			cluster.Spec.DatabaseConfiguration.RoleCounts = RoleCounts{
@@ -109,12 +119,14 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 			}
 			counts = cluster.GetRoleCountsWithDefaults()
 			Expect(counts).To(Equal(RoleCounts{
-				Storage:    3,
-				Logs:       8,
-				Proxies:    3,
-				Resolvers:  1,
-				RemoteLogs: 8,
-				LogRouters: 8,
+				Storage:       3,
+				Logs:          8,
+				CommitProxies: 2,
+				GrvProxies:    1,
+				Proxies:       3,
+				Resolvers:     1,
+				RemoteLogs:    8,
+				LogRouters:    8,
 			}))
 
 			cluster.Spec.DatabaseConfiguration.RoleCounts = RoleCounts{
@@ -124,12 +136,14 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 			}
 			counts = cluster.GetRoleCountsWithDefaults()
 			Expect(counts).To(Equal(RoleCounts{
-				Storage:    3,
-				Logs:       4,
-				Proxies:    3,
-				Resolvers:  1,
-				RemoteLogs: 5,
-				LogRouters: 6,
+				Storage:       3,
+				Logs:          4,
+				Proxies:       3,
+				GrvProxies:    1,
+				CommitProxies: 2,
+				Resolvers:     1,
+				RemoteLogs:    5,
+				LogRouters:    6,
 			}))
 		})
 	})
@@ -146,10 +160,12 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 					DatabaseConfiguration: DatabaseConfiguration{
 						RedundancyMode: RedundancyModeDouble,
 						RoleCounts: RoleCounts{
-							Storage:   5,
-							Logs:      3,
-							Proxies:   3,
-							Resolvers: 1,
+							Storage:       5,
+							Logs:          3,
+							Proxies:       3,
+							GrvProxies:    4,
+							CommitProxies: 12,
+							Resolvers:     1,
 						},
 					},
 				},
@@ -208,6 +224,15 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				Log:       5,
 				Stateless: 9,
 			}))
+
+			cluster.Spec.Version = "7.1.0-rc2"
+			counts, err = cluster.GetProcessCountsWithDefaults()
+			Expect(err).NotTo(HaveOccurred())
+			Expect(counts).To(Equal(ProcessCounts{
+				Storage:   5,
+				Log:       5,
+				Stateless: 22,
+			}))
 		})
 	})
 
@@ -566,11 +591,13 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				StorageEngine:  "ssd-2",
 				UsableRegions:  1,
 				RoleCounts: RoleCounts{
-					Logs:       4,
-					Proxies:    5,
-					Resolvers:  1,
-					LogRouters: -1,
-					RemoteLogs: -1,
+					Logs:          4,
+					Proxies:       5,
+					CommitProxies: 2,
+					GrvProxies:    1,
+					Resolvers:     1,
+					LogRouters:    -1,
+					RemoteLogs:    -1,
 				},
 			}))
 
@@ -581,27 +608,33 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				StorageEngine:  "ssd-2",
 				UsableRegions:  1,
 				RoleCounts: RoleCounts{
-					Logs:       3,
-					Proxies:    3,
-					Resolvers:  1,
-					LogRouters: -1,
-					RemoteLogs: -1,
+					Logs:          3,
+					Proxies:       3,
+					CommitProxies: 2,
+					GrvProxies:    1,
+					Resolvers:     1,
+					LogRouters:    -1,
+					RemoteLogs:    -1,
 				},
 			}))
 		})
 	})
 
-	When("getting the  configuration string", func() {
+	When("getting the configuration string", func() {
 		It("should be parsed correctly", func() {
 			configuration := DatabaseConfiguration{
 				RedundancyMode: RedundancyModeDouble,
 				StorageEngine:  "ssd",
 				UsableRegions:  1,
 				RoleCounts: RoleCounts{
-					Logs: 5,
+					Logs:          5,
+					Proxies:       1,
+					CommitProxies: 2,
+					GrvProxies:    2,
 				},
 			}
-			Expect(configuration.GetConfigurationString()).To(Equal("double ssd usable_regions=1 logs=5 proxies=0 resolvers=0 log_routers=0 remote_logs=0 regions=[]"))
+			Expect(configuration.AreSeparatedProxiesConfigured()).To(BeFalse())
+			Expect(configuration.GetConfigurationString("6.3.24")).To(Equal("double ssd usable_regions=1 logs=5 resolvers=0 log_routers=0 remote_logs=0 proxies=1 regions=[]"))
 
 			configuration.Regions = []Region{{
 				DataCenters: []DataCenter{{
@@ -611,11 +644,62 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 				}},
 				SatelliteLogs: 2,
 			}}
-			Expect(configuration.GetConfigurationString()).To(Equal("double ssd usable_regions=1 logs=5 proxies=0 resolvers=0 log_routers=0 remote_logs=0 regions=[{\\\"datacenters\\\":[{\\\"id\\\":\\\"iad\\\",\\\"priority\\\":1}],\\\"satellite_logs\\\":2}]"))
+			Expect(configuration.GetConfigurationString("6.3.24")).To(Equal("double ssd usable_regions=1 logs=5 resolvers=0 log_routers=0 remote_logs=0 proxies=1 regions=[{\\\"datacenters\\\":[{\\\"id\\\":\\\"iad\\\",\\\"priority\\\":1}],\\\"satellite_logs\\\":2}]"))
 			configuration.Regions = nil
 
 			configuration.VersionFlags.LogSpill = 3
-			Expect(configuration.GetConfigurationString()).To(Equal("double ssd usable_regions=1 logs=5 proxies=0 resolvers=0 log_routers=0 remote_logs=0 log_spill:=3 regions=[]"))
+			Expect(configuration.GetConfigurationString("6.3.24")).To(Equal("double ssd usable_regions=1 logs=5 resolvers=0 log_routers=0 remote_logs=0 proxies=1 log_spill:=3 regions=[]"))
+
+			Expect(configuration.GetConfigurationString("7.0.0")).To(Equal("double ssd usable_regions=1 logs=5 resolvers=0 log_routers=0 remote_logs=0 proxies=1 log_spill:=3 regions=[]"))
+			Expect(configuration.GetConfigurationString("7.1.0-rc1")).To(Equal("double ssd usable_regions=1 logs=5 resolvers=0 log_routers=0 remote_logs=0 proxies=1 log_spill:=3 regions=[]"))
+		})
+
+		When("CommitProxies and GrvProxies are not configured", func() {
+			configuration := DatabaseConfiguration{
+				RedundancyMode: RedundancyModeDouble,
+				StorageEngine:  "ssd",
+				UsableRegions:  1,
+				RoleCounts: RoleCounts{
+					Logs:    5,
+					Proxies: 1,
+				},
+			}
+			It("should be parsed correctly", func() {
+				Expect(configuration.GetConfigurationString("6.3.24")).To(Equal("double ssd usable_regions=1 logs=5 resolvers=0 log_routers=0 remote_logs=0 proxies=1 regions=[]"))
+				Expect(configuration.GetConfigurationString("7.0.0")).To(Equal("double ssd usable_regions=1 logs=5 resolvers=0 log_routers=0 remote_logs=0 proxies=1 regions=[]"))
+			})
+
+			It("should have no proxies configured", func() {
+				version, _ := ParseFdbVersion("7.0.0")
+				Expect(configuration.AreSeparatedProxiesConfigured()).To(BeFalse())
+				Expect(configuration.GetProxiesString(version)).To(Equal(" proxies=1"))
+			})
+		})
+
+		When("CommitProxies and GrvProxies are configured but not proxies", func() {
+			configuration := DatabaseConfiguration{
+				RedundancyMode: RedundancyModeDouble,
+				StorageEngine:  "ssd",
+				UsableRegions:  1,
+				RoleCounts: RoleCounts{
+					Logs:          5,
+					CommitProxies: 2,
+					GrvProxies:    1,
+				},
+			}
+			It("should have old proxies configuration with version FDB < 7.0.0 ", func() {
+				version, _ := ParseFdbVersion("6.3.24")
+				Expect(configuration.AreSeparatedProxiesConfigured()).To(BeTrue())
+				Expect(configuration.GetProxiesString(version)).To(Equal(" proxies=3"))
+				Expect(configuration.GetConfigurationString("6.3.24")).To(Equal("double ssd usable_regions=1 logs=5 resolvers=0 log_routers=0 remote_logs=0 proxies=3 regions=[]"))
+			})
+
+			It("should have separated proxies configured with FDB > 7.0.0", func() {
+				version, _ := ParseFdbVersion("7.0.0")
+				Expect(configuration.AreSeparatedProxiesConfigured()).To(BeTrue())
+				Expect(configuration.GetProxiesString(version)).To(Equal(" commit_proxies=2 grv_proxies=1"))
+				Expect(configuration.GetConfigurationString("7.0.0")).To(Equal("double ssd usable_regions=1 logs=5 resolvers=0 log_routers=0 remote_logs=0 commit_proxies=2 grv_proxies=1 regions=[]"))
+			})
 		})
 	})
 
@@ -807,7 +891,7 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 		})
 	})
 
-	When("changing tto fearless dr without initial regions", func() {
+	When("changing to fearless dr without initial regions", func() {
 		It("should return the fearless config", func() {
 			currentConfig := DatabaseConfiguration{
 				RedundancyMode: RedundancyModeDouble,
@@ -2450,11 +2534,13 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 							StorageEngine:  "ssd-2",
 							UsableRegions:  1,
 							RoleCounts: RoleCounts{
-								Logs:       3,
-								Proxies:    3,
-								Resolvers:  1,
-								LogRouters: -1,
-								RemoteLogs: -1,
+								Logs:          3,
+								Proxies:       3,
+								GrvProxies:    1,
+								CommitProxies: 2,
+								Resolvers:     1,
+								LogRouters:    -1,
+								RemoteLogs:    -1,
 							},
 						},
 						Generations: ClusterGenerationStatus{
@@ -3691,4 +3777,82 @@ var _ = Describe("[api] FoundationDBCluster", func() {
 			),
 		)
 	})
+
+	When("adding a process to the removal list", func() {
+		var cluster *FoundationDBCluster
+
+		BeforeEach(func() {
+			cluster = &FoundationDBCluster{}
+		})
+
+		When("removing with exclusion", func() {
+			When("a np process group is already included in the list", func() {
+				It("should add the process group to the removal list", func() {
+					removals := []string{"test1"}
+					cluster.AddProcessGroupsToRemovalList(removals)
+					Expect(cluster.Spec.ProcessGroupsToRemove).To(ContainElements(removals))
+					Expect(len(cluster.Spec.ProcessGroupsToRemove)).To(Equal(len(removals)))
+					Expect(len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(Equal(0))
+				})
+			})
+
+			When("a process group is already included in the list", func() {
+				BeforeEach(func() {
+					cluster.Spec.ProcessGroupsToRemove = append(cluster.Spec.ProcessGroupsToRemove, "test1")
+				})
+
+				It("should only add the missing process groups", func() {
+					Expect(cluster.Spec.ProcessGroupsToRemove).To(ContainElements("test1"))
+					removals := []string{"test1", "test2"}
+					cluster.AddProcessGroupsToRemovalList(removals)
+					Expect(cluster.Spec.ProcessGroupsToRemove).To(ContainElements(removals))
+					Expect(len(cluster.Spec.ProcessGroupsToRemove)).To(Equal(len(removals)))
+					Expect(len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(Equal(0))
+				})
+			})
+		})
+
+		When("removing without exclusion", func() {
+			When("a process group is not already included in the list", func() {
+				It("should add the process group to the removal list", func() {
+					removals := []string{"test1"}
+					cluster.AddProcessGroupsToRemovalWithoutExclusionList(removals)
+					Expect(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements(removals))
+					Expect(len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(Equal(len(removals)))
+					Expect(len(cluster.Spec.ProcessGroupsToRemove)).To(Equal(0))
+				})
+			})
+
+			When("a process group is already included in the list", func() {
+				BeforeEach(func() {
+					cluster.Spec.ProcessGroupsToRemoveWithoutExclusion = append(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion, "test1")
+					Expect(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements("test1"))
+				})
+
+				It("should only add the missing process groups", func() {
+					removals := []string{"test1", "test2"}
+					cluster.AddProcessGroupsToRemovalWithoutExclusionList(removals)
+					Expect(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements(removals))
+					Expect(len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(Equal(len(removals)))
+					Expect(len(cluster.Spec.ProcessGroupsToRemove)).To(Equal(0))
+				})
+			})
+
+			When("a process group is already included in the with exclusion list", func() {
+				BeforeEach(func() {
+					cluster.Spec.ProcessGroupsToRemove = append(cluster.Spec.ProcessGroupsToRemove, "test1")
+					Expect(cluster.Spec.ProcessGroupsToRemove).To(ContainElements("test1"))
+					Expect(len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(Equal(0))
+				})
+
+				It("should only add the missing process groups", func() {
+					removals := []string{"test1", "test2"}
+					cluster.AddProcessGroupsToRemovalWithoutExclusionList(removals)
+					Expect(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements(removals))
+					Expect(len(cluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(Equal(len(removals)))
+					Expect(len(cluster.Spec.ProcessGroupsToRemove)).To(Equal(1))
+				})
+			})
+		})
+	})
 })
diff --git a/api/v1beta2/testdata/fdb_status_7_1_rc1.json b/api/v1beta2/testdata/fdb_status_7_1_rc1.json
new file mode 100644
index 00000000..d78bf4ce
--- /dev/null
+++ b/api/v1beta2/testdata/fdb_status_7_1_rc1.json
@@ -0,0 +1,1649 @@
+{
+    "client" : {
+        "cluster_file" : {
+            "path" : "/var/dynamic-conf/fdb.cluster",
+            "up_to_date" : true
+        },
+        "coordinators" : {
+            "coordinators" : [
+                {
+                    "address" : "10.1.18.253:4501",
+                    "protocol" : "0fdb00b071010000",
+                    "reachable" : true
+                },
+                {
+                    "address" : "10.1.18.254:4501",
+                    "protocol" : "0fdb00b071010000",
+                    "reachable" : true
+                },
+                {
+                    "address" : "10.1.19.0:4501",
+                    "protocol" : "0fdb00b071010000",
+                    "reachable" : true
+                }
+            ],
+            "quorum_reachable" : true
+        },
+        "database_status" : {
+            "available" : true,
+            "healthy" : true
+        },
+        "messages" : [
+        ],
+        "timestamp" : 1646933244
+    },
+    "cluster" : {
+        "active_primary_dc" : "",
+        "active_tss_count" : 0,
+        "bounce_impact" : {
+            "can_clean_bounce" : true
+        },
+        "clients" : {
+            "count" : 8,
+            "supported_versions" : [
+                {
+                    "client_version" : "6.2.30",
+                    "connected_clients" : [
+                        {
+                            "address" : "10.1.18.249:34874",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:49078",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:51834",
+                            "log_group" : "fdb-kubernetes-operator"
+                        }
+                    ],
+                    "count" : 3,
+                    "protocol_version" : "fdb00b062010001",
+                    "source_version" : "c1acf5fc16a522b0f53b27874c88e21f5d34b251"
+                },
+                {
+                    "client_version" : "6.3.10",
+                    "connected_clients" : [
+                        {
+                            "address" : "10.1.18.249:34874",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:49078",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:51834",
+                            "log_group" : "fdb-kubernetes-operator"
+                        }
+                    ],
+                    "count" : 3,
+                    "protocol_version" : "fdb00b063010001",
+                    "source_version" : "a461b9c93be19f846c2c41d9de455f968b53fd6d"
+                },
+                {
+                    "client_version" : "7.1.0-rc1",
+                    "connected_clients" : [
+                        {
+                            "address" : "10.1.18.249:34874",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:35022",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:35642",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:49078",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:49230",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:51834",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:51920",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:52610",
+                            "log_group" : "fdb-kubernetes-operator"
+                        }
+                    ],
+                    "count" : 8,
+                    "max_protocol_clients" : [
+                        {
+                            "address" : "10.1.18.249:34874",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:35022",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:35642",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:49078",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:49230",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:51834",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:51920",
+                            "log_group" : "fdb-kubernetes-operator"
+                        },
+                        {
+                            "address" : "10.1.18.249:52610",
+                            "log_group" : "fdb-kubernetes-operator"
+                        }
+                    ],
+                    "max_protocol_count" : 8,
+                    "protocol_version" : "fdb00b071010000",
+                    "source_version" : "079de5ba57f85e0abb9117e9378bb0b135a3da12"
+                }
+            ]
+        },
+        "cluster_controller_timestamp" : 1646933244,
+        "configuration" : {
+            "backup_worker_enabled" : 0,
+            "commit_proxies" : 2,
+            "coordinators_count" : 3,
+            "excluded_servers" : [
+            ],
+            "grv_proxies" : 1,
+            "log_routers" : -1,
+            "log_spill" : 2,
+            "logs" : 3,
+            "perpetual_storage_wiggle" : 0,
+            "perpetual_storage_wiggle_locality" : "0",
+            "proxies" : 3,
+            "redundancy_mode" : "double",
+            "remote_logs" : -1,
+            "resolvers" : 1,
+            "storage_engine" : "ssd-2",
+            "storage_migration_type" : "disabled",
+            "usable_regions" : 1
+        },
+        "connection_string" : "test_cluster:aHeD9ocNXOUxi0dyzU3k7Bhg53SpyrBV@10.1.18.253:4501,10.1.18.254:4501,10.1.19.0:4501",
+        "data" : {
+            "average_partition_size_bytes" : 400000,
+            "least_operating_space_bytes_log_server" : 77427512760,
+            "least_operating_space_bytes_storage_server" : 77427513204,
+            "moving_data" : {
+                "highest_priority" : 0,
+                "in_flight_bytes" : 0,
+                "in_queue_bytes" : 0,
+                "total_written_bytes" : 0
+            },
+            "partitions_count" : 1,
+            "state" : {
+                "healthy" : true,
+                "min_replicas_remaining" : 2,
+                "name" : "healthy"
+            },
+            "system_kv_size_bytes" : 0,
+            "team_trackers" : [
+                {
+                    "in_flight_bytes" : 0,
+                    "primary" : true,
+                    "state" : {
+                        "healthy" : true,
+                        "min_replicas_remaining" : 2,
+                        "name" : "healthy"
+                    },
+                    "unhealthy_servers" : 0
+                }
+            ],
+            "total_disk_used_bytes" : 629203472,
+            "total_kv_size_bytes" : 0
+        },
+        "database_available" : true,
+        "database_lock_state" : {
+            "locked" : false
+        },
+        "datacenter_lag" : {
+            "seconds" : 0,
+            "versions" : 0
+        },
+        "degraded_processes" : 0,
+        "fault_tolerance" : {
+            "max_zone_failures_without_losing_availability" : 1,
+            "max_zone_failures_without_losing_data" : 1
+        },
+        "full_replication" : true,
+        "generation" : 2,
+        "incompatible_connections" : [
+        ],
+        "latency_probe" : {
+            "batch_priority_transaction_start_seconds" : 0.00241208,
+            "commit_seconds" : 0.0045864599999999997,
+            "immediate_priority_transaction_start_seconds" : 0.0022053699999999999,
+            "read_seconds" : 0.00039434399999999998,
+            "transaction_start_seconds" : 0.00389361
+        },
+        "layers" : {
+            "_valid" : true
+        },
+        "logs" : [
+            {
+                "begin_version" : 0,
+                "current" : true,
+                "epoch" : 2,
+                "log_fault_tolerance" : 1,
+                "log_interfaces" : [
+                    {
+                        "address" : "10.1.19.3:4501",
+                        "healthy" : true,
+                        "id" : "31754d1d7d8d6f05"
+                    },
+                    {
+                        "address" : "10.1.19.4:4501",
+                        "healthy" : true,
+                        "id" : "56cf105980ec2b07"
+                    },
+                    {
+                        "address" : "10.1.19.2:4501",
+                        "healthy" : true,
+                        "id" : "2c66a861b33b2697"
+                    }
+                ],
+                "log_replication_factor" : 2,
+                "log_write_anti_quorum" : 0,
+                "possibly_losing_data" : false
+            }
+        ],
+        "machines" : {
+            "test-cluster-cluster-controller-1" : {
+                "address" : "10.1.18.255",
+                "contributing_workers" : 1,
+                "cpu" : {
+                    "logical_core_utilization" : 0.058885100000000003
+                },
+                "excluded" : false,
+                "locality" : {
+                    "instance_id" : "cluster_controller-1",
+                    "machineid" : "test-cluster-cluster-controller-1",
+                    "processid" : "f75644abdf1b06c803b5c3c124fdd0a0",
+                    "zoneid" : "test-cluster-cluster-controller-1"
+                },
+                "machine_id" : "test-cluster-cluster-controller-1",
+                "memory" : {
+                    "committed_bytes" : 2285780992,
+                    "free_bytes" : 14505558016,
+                    "total_bytes" : 16791339008
+                },
+                "network" : {
+                    "megabits_received" : {
+                        "hz" : 0.21671900000000002
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.22023900000000002
+                    },
+                    "tcp_segments_retransmitted" : {
+                        "hz" : 0
+                    }
+                }
+            },
+            "test-cluster-log-1" : {
+                "address" : "10.1.19.1",
+                "contributing_workers" : 1,
+                "cpu" : {
+                    "logical_core_utilization" : 0.058623400000000006
+                },
+                "excluded" : false,
+                "locality" : {
+                    "instance_id" : "log-1",
+                    "machineid" : "test-cluster-log-1",
+                    "processid" : "f6e0f7fd80da429d20329ad95d793ca3",
+                    "zoneid" : "test-cluster-log-1"
+                },
+                "machine_id" : "test-cluster-log-1",
+                "memory" : {
+                    "committed_bytes" : 2286039040,
+                    "free_bytes" : 14505299968,
+                    "total_bytes" : 16791339008
+                },
+                "network" : {
+                    "megabits_received" : {
+                        "hz" : 0.344115
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.26798900000000003
+                    },
+                    "tcp_segments_retransmitted" : {
+                        "hz" : 0
+                    }
+                }
+            },
+            "test-cluster-log-2" : {
+                "address" : "10.1.19.4",
+                "contributing_workers" : 1,
+                "cpu" : {
+                    "logical_core_utilization" : 0.058623400000000006
+                },
+                "excluded" : false,
+                "locality" : {
+                    "instance_id" : "log-2",
+                    "machineid" : "test-cluster-log-2",
+                    "processid" : "78c1c84af4481f0df628d40358f0930a",
+                    "zoneid" : "test-cluster-log-2"
+                },
+                "machine_id" : "test-cluster-log-2",
+                "memory" : {
+                    "committed_bytes" : 2286039040,
+                    "free_bytes" : 14505299968,
+                    "total_bytes" : 16791339008
+                },
+                "network" : {
+                    "megabits_received" : {
+                        "hz" : 0.166773
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.103298
+                    },
+                    "tcp_segments_retransmitted" : {
+                        "hz" : 0
+                    }
+                }
+            },
+            "test-cluster-log-3" : {
+                "address" : "10.1.19.2",
+                "contributing_workers" : 1,
+                "cpu" : {
+                    "logical_core_utilization" : 0.058623400000000006
+                },
+                "excluded" : false,
+                "locality" : {
+                    "instance_id" : "log-3",
+                    "machineid" : "test-cluster-log-3",
+                    "processid" : "105bf6c041f8ec315d03e889c2746ecf",
+                    "zoneid" : "test-cluster-log-3"
+                },
+                "machine_id" : "test-cluster-log-3",
+                "memory" : {
+                    "committed_bytes" : 2286039040,
+                    "free_bytes" : 14505299968,
+                    "total_bytes" : 16791339008
+                },
+                "network" : {
+                    "megabits_received" : {
+                        "hz" : 0.088260000000000005
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.085034600000000002
+                    },
+                    "tcp_segments_retransmitted" : {
+                        "hz" : 0
+                    }
+                }
+            },
+            "test-cluster-log-4" : {
+                "address" : "10.1.19.3",
+                "contributing_workers" : 1,
+                "cpu" : {
+                    "logical_core_utilization" : 0.058623400000000006
+                },
+                "excluded" : false,
+                "locality" : {
+                    "instance_id" : "log-4",
+                    "machineid" : "test-cluster-log-4",
+                    "processid" : "83084479b50c9c3a09b0286297be3796",
+                    "zoneid" : "test-cluster-log-4"
+                },
+                "machine_id" : "test-cluster-log-4",
+                "memory" : {
+                    "committed_bytes" : 2286039040,
+                    "free_bytes" : 14505299968,
+                    "total_bytes" : 16791339008
+                },
+                "network" : {
+                    "megabits_received" : {
+                        "hz" : 0.091471200000000003
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.091644000000000003
+                    },
+                    "tcp_segments_retransmitted" : {
+                        "hz" : 0
+                    }
+                }
+            },
+            "test-cluster-storage-1" : {
+                "address" : "10.1.18.254",
+                "contributing_workers" : 1,
+                "cpu" : {
+                    "logical_core_utilization" : 0.058608
+                },
+                "excluded" : false,
+                "locality" : {
+                    "instance_id" : "storage-1",
+                    "machineid" : "test-cluster-storage-1",
+                    "processid" : "eb48ada3a682e86363f06aa89e1041fa",
+                    "zoneid" : "test-cluster-storage-1"
+                },
+                "machine_id" : "test-cluster-storage-1",
+                "memory" : {
+                    "committed_bytes" : 2286297088,
+                    "free_bytes" : 14505041920,
+                    "total_bytes" : 16791339008
+                },
+                "network" : {
+                    "megabits_received" : {
+                        "hz" : 0.267594
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.36186400000000002
+                    },
+                    "tcp_segments_retransmitted" : {
+                        "hz" : 0
+                    }
+                }
+            },
+            "test-cluster-storage-2" : {
+                "address" : "10.1.19.0",
+                "contributing_workers" : 1,
+                "cpu" : {
+                    "logical_core_utilization" : 0.0583768
+                },
+                "excluded" : false,
+                "locality" : {
+                    "instance_id" : "storage-2",
+                    "machineid" : "test-cluster-storage-2",
+                    "processid" : "f483247d4d5f279ef02c549680cbde64",
+                    "zoneid" : "test-cluster-storage-2"
+                },
+                "machine_id" : "test-cluster-storage-2",
+                "memory" : {
+                    "committed_bytes" : 2286297088,
+                    "free_bytes" : 14505041920,
+                    "total_bytes" : 16791339008
+                },
+                "network" : {
+                    "megabits_received" : {
+                        "hz" : 0.113719
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.14154
+                    },
+                    "tcp_segments_retransmitted" : {
+                        "hz" : 0
+                    }
+                }
+            },
+            "test-cluster-storage-3" : {
+                "address" : "10.1.18.253",
+                "contributing_workers" : 1,
+                "cpu" : {
+                    "logical_core_utilization" : 0.058608200000000006
+                },
+                "excluded" : false,
+                "locality" : {
+                    "instance_id" : "storage-3",
+                    "machineid" : "test-cluster-storage-3",
+                    "processid" : "eab0db1aa7aae81a50ca97e9814a1b7d",
+                    "zoneid" : "test-cluster-storage-3"
+                },
+                "machine_id" : "test-cluster-storage-3",
+                "memory" : {
+                    "committed_bytes" : 2286297088,
+                    "free_bytes" : 14505041920,
+                    "total_bytes" : 16791339008
+                },
+                "network" : {
+                    "megabits_received" : {
+                        "hz" : 0.106423
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.12354800000000001
+                    },
+                    "tcp_segments_retransmitted" : {
+                        "hz" : 0
+                    }
+                }
+            }
+        },
+        "messages" : [
+        ],
+        "page_cache" : {
+            "log_hit_rate" : 1,
+            "storage_hit_rate" : 1
+        },
+        "processes" : {
+            "105bf6c041f8ec315d03e889c2746ecf" : {
+                "address" : "10.1.19.2:4501",
+                "class_source" : "command_line",
+                "class_type" : "log",
+                "command_line" : "/usr/bin/fdbserver --class=log --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.19.2:4501 --locality_instance_id=log-3 --locality_machineid=test-cluster-log-3 --locality_zoneid=test-cluster-log-3 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.19.2:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+                "cpu" : {
+                    "usage_cores" : 0.012726600000000001
+                },
+                "disk" : {
+                    "busy" : 0.0101993,
+                    "free_bytes" : 84178165760,
+                    "reads" : {
+                        "counter" : 6598328,
+                        "hz" : 0,
+                        "sectors" : 0
+                    },
+                    "total_bytes" : 135012552704,
+                    "writes" : {
+                        "counter" : 2167046,
+                        "hz" : 19.598700000000001,
+                        "sectors" : 1600
+                    }
+                },
+                "excluded" : false,
+                "fault_domain" : "test-cluster-log-3",
+                "locality" : {
+                    "instance_id" : "log-3",
+                    "machineid" : "test-cluster-log-3",
+                    "processid" : "105bf6c041f8ec315d03e889c2746ecf",
+                    "zoneid" : "test-cluster-log-3"
+                },
+                "machine_id" : "test-cluster-log-3",
+                "memory" : {
+                    "available_bytes" : 8589934592,
+                    "limit_bytes" : 8589934592,
+                    "unused_allocated_memory" : 0,
+                    "used_bytes" : 216772608
+                },
+                "messages" : [
+                ],
+                "network" : {
+                    "connection_errors" : {
+                        "hz" : 0
+                    },
+                    "connections_closed" : {
+                        "hz" : 0
+                    },
+                    "connections_established" : {
+                        "hz" : 0
+                    },
+                    "current_connections" : 5,
+                    "megabits_received" : {
+                        "hz" : 0.038947900000000001
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.054306900000000005
+                    },
+                    "tls_policy_failures" : {
+                        "hz" : 0
+                    }
+                },
+                "roles" : [
+                    {
+                        "data_version" : 74805090,
+                        "durable_bytes" : {
+                            "counter" : 255,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "id" : "2c66a861b33b2697",
+                        "input_bytes" : {
+                            "counter" : 1512,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "kvstore_available_bytes" : 84178214912,
+                        "kvstore_free_bytes" : 84178214912,
+                        "kvstore_total_bytes" : 135012552704,
+                        "kvstore_used_bytes" : 104861752,
+                        "queue_disk_available_bytes" : 84178214912,
+                        "queue_disk_free_bytes" : 84178214912,
+                        "queue_disk_total_bytes" : 135012552704,
+                        "queue_disk_used_bytes" : 389120,
+                        "role" : "log"
+                    }
+                ],
+                "run_loop_busy" : 0.0081051000000000005,
+                "uptime_seconds" : 85.002899999999997,
+                "version" : "7.1.0-rc1"
+            },
+            "78c1c84af4481f0df628d40358f0930a" : {
+                "address" : "10.1.19.4:4501",
+                "class_source" : "command_line",
+                "class_type" : "log",
+                "command_line" : "/usr/bin/fdbserver --class=log --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.19.4:4501 --locality_instance_id=log-2 --locality_machineid=test-cluster-log-2 --locality_zoneid=test-cluster-log-2 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.19.4:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+                "cpu" : {
+                    "usage_cores" : 0.0137228
+                },
+                "disk" : {
+                    "busy" : 0.0101996,
+                    "free_bytes" : 84178165760,
+                    "reads" : {
+                        "counter" : 6598328,
+                        "hz" : 0,
+                        "sectors" : 0
+                    },
+                    "total_bytes" : 135012552704,
+                    "writes" : {
+                        "counter" : 2167046,
+                        "hz" : 19.5991,
+                        "sectors" : 1600
+                    }
+                },
+                "excluded" : false,
+                "fault_domain" : "test-cluster-log-2",
+                "locality" : {
+                    "instance_id" : "log-2",
+                    "machineid" : "test-cluster-log-2",
+                    "processid" : "78c1c84af4481f0df628d40358f0930a",
+                    "zoneid" : "test-cluster-log-2"
+                },
+                "machine_id" : "test-cluster-log-2",
+                "memory" : {
+                    "available_bytes" : 8589934592,
+                    "limit_bytes" : 8589934592,
+                    "unused_allocated_memory" : 0,
+                    "used_bytes" : 197763072
+                },
+                "messages" : [
+                ],
+                "network" : {
+                    "connection_errors" : {
+                        "hz" : 0
+                    },
+                    "connections_closed" : {
+                        "hz" : 0
+                    },
+                    "connections_established" : {
+                        "hz" : 0
+                    },
+                    "current_connections" : 6,
+                    "megabits_received" : {
+                        "hz" : 0.041451000000000002
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.058755800000000004
+                    },
+                    "tls_policy_failures" : {
+                        "hz" : 0
+                    }
+                },
+                "roles" : [
+                    {
+                        "data_version" : 74805090,
+                        "durable_bytes" : {
+                            "counter" : 3264,
+                            "hz" : 20.3995,
+                            "roughness" : 100.999
+                        },
+                        "id" : "56cf105980ec2b07",
+                        "input_bytes" : {
+                            "counter" : 14551,
+                            "hz" : 51.198700000000002,
+                            "roughness" : 255
+                        },
+                        "kvstore_available_bytes" : 84178214912,
+                        "kvstore_free_bytes" : 84178214912,
+                        "kvstore_total_bytes" : 135012552704,
+                        "kvstore_used_bytes" : 104861752,
+                        "queue_disk_available_bytes" : 84178214912,
+                        "queue_disk_free_bytes" : 84178214912,
+                        "queue_disk_total_bytes" : 135012552704,
+                        "queue_disk_used_bytes" : 258048,
+                        "role" : "log"
+                    }
+                ],
+                "run_loop_busy" : 0.0088742700000000001,
+                "uptime_seconds" : 85.003,
+                "version" : "7.1.0-rc1"
+            },
+            "83084479b50c9c3a09b0286297be3796" : {
+                "address" : "10.1.19.3:4501",
+                "class_source" : "command_line",
+                "class_type" : "log",
+                "command_line" : "/usr/bin/fdbserver --class=log --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.19.3:4501 --locality_instance_id=log-4 --locality_machineid=test-cluster-log-4 --locality_zoneid=test-cluster-log-4 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.19.3:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+                "cpu" : {
+                    "usage_cores" : 0.0140474
+                },
+                "disk" : {
+                    "busy" : 0.0101996,
+                    "free_bytes" : 84178165760,
+                    "reads" : {
+                        "counter" : 6598328,
+                        "hz" : 0,
+                        "sectors" : 0
+                    },
+                    "total_bytes" : 135012552704,
+                    "writes" : {
+                        "counter" : 2167046,
+                        "hz" : 19.5991,
+                        "sectors" : 1600
+                    }
+                },
+                "excluded" : false,
+                "fault_domain" : "test-cluster-log-4",
+                "locality" : {
+                    "instance_id" : "log-4",
+                    "machineid" : "test-cluster-log-4",
+                    "processid" : "83084479b50c9c3a09b0286297be3796",
+                    "zoneid" : "test-cluster-log-4"
+                },
+                "machine_id" : "test-cluster-log-4",
+                "memory" : {
+                    "available_bytes" : 8589934592,
+                    "limit_bytes" : 8589934592,
+                    "unused_allocated_memory" : 0,
+                    "used_bytes" : 210481152
+                },
+                "messages" : [
+                ],
+                "network" : {
+                    "connection_errors" : {
+                        "hz" : 0
+                    },
+                    "connections_closed" : {
+                        "hz" : 0
+                    },
+                    "connections_established" : {
+                        "hz" : 0
+                    },
+                    "current_connections" : 6,
+                    "megabits_received" : {
+                        "hz" : 0.041207800000000003
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.058487000000000004
+                    },
+                    "tls_policy_failures" : {
+                        "hz" : 0
+                    }
+                },
+                "roles" : [
+                    {
+                        "data_version" : 74805090,
+                        "durable_bytes" : {
+                            "counter" : 3315,
+                            "hz" : 20.399100000000001,
+                            "roughness" : 101
+                        },
+                        "id" : "31754d1d7d8d6f05",
+                        "input_bytes" : {
+                            "counter" : 15459,
+                            "hz" : 51.197800000000001,
+                            "roughness" : 255
+                        },
+                        "kvstore_available_bytes" : 84178202624,
+                        "kvstore_free_bytes" : 84178202624,
+                        "kvstore_total_bytes" : 135012552704,
+                        "kvstore_used_bytes" : 104861752,
+                        "queue_disk_available_bytes" : 84178202624,
+                        "queue_disk_free_bytes" : 84178202624,
+                        "queue_disk_total_bytes" : 135012552704,
+                        "queue_disk_used_bytes" : 258048,
+                        "role" : "log"
+                    }
+                ],
+                "run_loop_busy" : 0.0089497099999999979,
+                "uptime_seconds" : 85.002700000000004,
+                "version" : "7.1.0-rc1"
+            },
+            "eab0db1aa7aae81a50ca97e9814a1b7d" : {
+                "address" : "10.1.18.253:4501",
+                "class_source" : "command_line",
+                "class_type" : "storage",
+                "command_line" : "/usr/bin/fdbserver --class=storage --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.18.253:4501 --locality_instance_id=storage-3 --locality_machineid=test-cluster-storage-3 --locality_zoneid=test-cluster-storage-3 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.18.253:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+                "cpu" : {
+                    "usage_cores" : 0.0126458
+                },
+                "disk" : {
+                    "busy" : 0.0097997299999999996,
+                    "free_bytes" : 84178145280,
+                    "reads" : {
+                        "counter" : 6598328,
+                        "hz" : 0,
+                        "sectors" : 0
+                    },
+                    "total_bytes" : 135012552704,
+                    "writes" : {
+                        "counter" : 2167046,
+                        "hz" : 18.599499999999999,
+                        "sectors" : 1456
+                    }
+                },
+                "excluded" : false,
+                "fault_domain" : "test-cluster-storage-3",
+                "locality" : {
+                    "instance_id" : "storage-3",
+                    "machineid" : "test-cluster-storage-3",
+                    "processid" : "eab0db1aa7aae81a50ca97e9814a1b7d",
+                    "zoneid" : "test-cluster-storage-3"
+                },
+                "machine_id" : "test-cluster-storage-3",
+                "memory" : {
+                    "available_bytes" : 8589934592,
+                    "limit_bytes" : 8589934592,
+                    "unused_allocated_memory" : 0,
+                    "used_bytes" : 196194304
+                },
+                "messages" : [
+                ],
+                "network" : {
+                    "connection_errors" : {
+                        "hz" : 0
+                    },
+                    "connections_closed" : {
+                        "hz" : 0
+                    },
+                    "connections_established" : {
+                        "hz" : 0.19999400000000001
+                    },
+                    "current_connections" : 9,
+                    "megabits_received" : {
+                        "hz" : 0.070506800000000008
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.094602100000000008
+                    },
+                    "tls_policy_failures" : {
+                        "hz" : 0
+                    }
+                },
+                "roles" : [
+                    {
+                        "role" : "coordinator"
+                    },
+                    {
+                        "bytes_queried" : {
+                            "counter" : 226531,
+                            "hz" : 3388.3899999999999,
+                            "roughness" : 4366.4099999999999
+                        },
+                        "data_lag" : {
+                            "seconds" : 2.1227,
+                            "versions" : 2122697
+                        },
+                        "data_version" : 74608832,
+                        "durability_lag" : {
+                            "seconds" : 5,
+                            "versions" : 5000000
+                        },
+                        "durable_bytes" : {
+                            "counter" : 75858,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "durable_version" : 69608832,
+                        "fetched_versions" : {
+                            "counter" : 74608832,
+                            "hz" : 1647130.0000000002,
+                            "roughness" : 2866620
+                        },
+                        "fetches_from_logs" : {
+                            "counter" : 53,
+                            "hz" : 0.79995000000000005,
+                            "roughness" : 0.39221300000000003
+                        },
+                        "finished_queries" : {
+                            "counter" : 446,
+                            "hz" : 3.9997500000000001,
+                            "roughness" : 4.1554200000000003
+                        },
+                        "id" : "389c23d59a646e52",
+                        "input_bytes" : {
+                            "counter" : 77854,
+                            "hz" : 399.17500000000001,
+                            "roughness" : 1995
+                        },
+                        "keys_queried" : {
+                            "counter" : 1049,
+                            "hz" : 13.5992,
+                            "roughness" : 16.528400000000001
+                        },
+                        "kvstore_available_bytes" : 84178239488,
+                        "kvstore_free_bytes" : 84178239488,
+                        "kvstore_inline_keys" : 0,
+                        "kvstore_total_bytes" : 135012552704,
+                        "kvstore_total_nodes" : 0,
+                        "kvstore_total_size" : 0,
+                        "kvstore_used_bytes" : 104878232,
+                        "local_rate" : 100,
+                        "low_priority_queries" : {
+                            "counter" : 0,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "mutation_bytes" : {
+                            "counter" : 5972,
+                            "hz" : 18.7988,
+                            "roughness" : 93
+                        },
+                        "mutations" : {
+                            "counter" : 78,
+                            "hz" : 0.39997500000000002,
+                            "roughness" : 1
+                        },
+                        "query_queue_max" : 3,
+                        "read_latency_statistics" : {
+                            "count" : 334,
+                            "max" : 0.00058245699999999998,
+                            "mean" : 0.000121679,
+                            "median" : 0.00010228199999999999,
+                            "min" : 1.16825e-05,
+                            "p25" : 6.8187699999999998e-05,
+                            "p90" : 0.00023627299999999998,
+                            "p95" : 0.00031471299999999999,
+                            "p99" : 0.00038647699999999998,
+                            "p99.9" : 0.00048828099999999997
+                        },
+                        "role" : "storage",
+                        "storage_metadata" : {
+                            "created_time_datetime" : "Thu Mar 10 17:26:07 2022 +0000",
+                            "created_time_timestamp" : 1646933167898430464
+                        },
+                        "stored_bytes" : 0,
+                        "total_queries" : {
+                            "counter" : 446,
+                            "hz" : 3.9997500000000001,
+                            "roughness" : 4.1554200000000003
+                        }
+                    },
+                    {
+                        "id" : "dfd679875a386d06",
+                        "role" : "resolver"
+                    }
+                ],
+                "run_loop_busy" : 0.0080509299999999978,
+                "uptime_seconds" : 85.003100000000003,
+                "version" : "7.1.0-rc1"
+            },
+            "eb48ada3a682e86363f06aa89e1041fa" : {
+                "address" : "10.1.18.254:4501",
+                "class_source" : "command_line",
+                "class_type" : "storage",
+                "command_line" : "/usr/bin/fdbserver --class=storage --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.18.254:4501 --locality_instance_id=storage-1 --locality_machineid=test-cluster-storage-1 --locality_zoneid=test-cluster-storage-1 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.18.254:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+                "cpu" : {
+                    "usage_cores" : 0.036252700000000006
+                },
+                "disk" : {
+                    "busy" : 0.0097997599999999994,
+                    "free_bytes" : 84178145280,
+                    "reads" : {
+                        "counter" : 6598328,
+                        "hz" : 0,
+                        "sectors" : 0
+                    },
+                    "total_bytes" : 135012552704,
+                    "writes" : {
+                        "counter" : 2167046,
+                        "hz" : 18.599599999999999,
+                        "sectors" : 1456
+                    }
+                },
+                "excluded" : false,
+                "fault_domain" : "test-cluster-storage-1",
+                "locality" : {
+                    "instance_id" : "storage-1",
+                    "machineid" : "test-cluster-storage-1",
+                    "processid" : "eb48ada3a682e86363f06aa89e1041fa",
+                    "zoneid" : "test-cluster-storage-1"
+                },
+                "machine_id" : "test-cluster-storage-1",
+                "memory" : {
+                    "available_bytes" : 8589934592,
+                    "limit_bytes" : 8589934592,
+                    "unused_allocated_memory" : 0,
+                    "used_bytes" : 189898752
+                },
+                "messages" : [
+                ],
+                "network" : {
+                    "connection_errors" : {
+                        "hz" : 0
+                    },
+                    "connections_closed" : {
+                        "hz" : 0
+                    },
+                    "connections_established" : {
+                        "hz" : 0.19999500000000001
+                    },
+                    "current_connections" : 9,
+                    "megabits_received" : {
+                        "hz" : 0.17158000000000001
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.224244
+                    },
+                    "tls_policy_failures" : {
+                        "hz" : 0
+                    }
+                },
+                "roles" : [
+                    {
+                        "role" : "coordinator"
+                    },
+                    {
+                        "grv_latency_statistics" : {
+                            "batch" : {
+                                "count" : 6,
+                                "max" : 0.0047154399999999996,
+                                "mean" : 0.00133355,
+                                "median" : 0.00053143500000000002,
+                                "min" : 0.00045347199999999996,
+                                "p25" : 0.00045371099999999998,
+                                "p90" : 0.0013067700000000001,
+                                "p95" : 0.0013067700000000001,
+                                "p99" : 0.0013067700000000001,
+                                "p99.9" : 0.0013067700000000001
+                            },
+                            "default" : {
+                                "count" : 225,
+                                "max" : 0.089440599999999995,
+                                "mean" : 0.0020094599999999998,
+                                "median" : 0.00062560999999999997,
+                                "min" : 0.00034069999999999999,
+                                "p25" : 0.00053405800000000002,
+                                "p90" : 0.0020797300000000001,
+                                "p95" : 0.00669289,
+                                "p99" : 0.010715200000000001,
+                                "p99.9" : 0.088694600000000012
+                            }
+                        },
+                        "id" : "0de7f5c5e549cad1",
+                        "role" : "grv_proxy"
+                    },
+                    {
+                        "bytes_queried" : {
+                            "counter" : 223948,
+                            "hz" : 2430.7199999999998,
+                            "roughness" : 3618.48
+                        },
+                        "data_lag" : {
+                            "seconds" : 0.19625800000000002,
+                            "versions" : 196258
+                        },
+                        "data_version" : 74805090,
+                        "durability_lag" : {
+                            "seconds" : 5.1962599999999997,
+                            "versions" : 5196258
+                        },
+                        "durable_bytes" : {
+                            "counter" : 75858,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "durable_version" : 69608832,
+                        "fetched_versions" : {
+                            "counter" : 74805090,
+                            "hz" : 1686420,
+                            "roughness" : 2759180
+                        },
+                        "fetches_from_logs" : {
+                            "counter" : 54,
+                            "hz" : 0.99996600000000002,
+                            "roughness" : 0.63605800000000001
+                        },
+                        "finished_queries" : {
+                            "counter" : 426,
+                            "hz" : 4.5998400000000004,
+                            "roughness" : 4.3348199999999997
+                        },
+                        "id" : "9941616400759d37",
+                        "input_bytes" : {
+                            "counter" : 77854,
+                            "hz" : 399.18700000000001,
+                            "roughness" : 1995
+                        },
+                        "keys_queried" : {
+                            "counter" : 1051,
+                            "hz" : 12.1996,
+                            "roughness" : 17.165900000000001
+                        },
+                        "kvstore_available_bytes" : 84178223104,
+                        "kvstore_free_bytes" : 84178223104,
+                        "kvstore_inline_keys" : 0,
+                        "kvstore_total_bytes" : 135012552704,
+                        "kvstore_total_nodes" : 0,
+                        "kvstore_total_size" : 0,
+                        "kvstore_used_bytes" : 104878232,
+                        "local_rate" : 100,
+                        "low_priority_queries" : {
+                            "counter" : 0,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "mutation_bytes" : {
+                            "counter" : 5972,
+                            "hz" : 18.799399999999999,
+                            "roughness" : 93
+                        },
+                        "mutations" : {
+                            "counter" : 78,
+                            "hz" : 0.39998600000000001,
+                            "roughness" : 1
+                        },
+                        "query_queue_max" : 3,
+                        "read_latency_statistics" : {
+                            "count" : 297,
+                            "max" : 0.078662399999999993,
+                            "mean" : 0.00067559199999999992,
+                            "median" : 0.00011682499999999999,
+                            "min" : 1.3828300000000003e-05,
+                            "p25" : 7.9631800000000012e-05,
+                            "p90" : 0.00028634099999999996,
+                            "p95" : 0.000379324,
+                            "p99" : 0.00071144100000000005,
+                            "p99.9" : 0.078609699999999991
+                        },
+                        "role" : "storage",
+                        "storage_metadata" : {
+                            "created_time_datetime" : "Thu Mar 10 17:26:07 2022 +0000",
+                            "created_time_timestamp" : 1646933167898430464
+                        },
+                        "stored_bytes" : 0,
+                        "total_queries" : {
+                            "counter" : 426,
+                            "hz" : 4.5998400000000004,
+                            "roughness" : 4.3348199999999997
+                        }
+                    }
+                ],
+                "run_loop_busy" : 0.024864200000000003,
+                "uptime_seconds" : 85.002600000000001,
+                "version" : "7.1.0-rc1"
+            },
+            "f483247d4d5f279ef02c549680cbde64" : {
+                "address" : "10.1.19.0:4501",
+                "class_source" : "command_line",
+                "class_type" : "storage",
+                "command_line" : "/usr/bin/fdbserver --class=storage --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.19.0:4501 --locality_instance_id=storage-2 --locality_machineid=test-cluster-storage-2 --locality_zoneid=test-cluster-storage-2 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.19.0:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+                "cpu" : {
+                    "usage_cores" : 0.016351300000000003
+                },
+                "disk" : {
+                    "busy" : 0.0097997999999999991,
+                    "free_bytes" : 84178145280,
+                    "reads" : {
+                        "counter" : 6598328,
+                        "hz" : 0,
+                        "sectors" : 0
+                    },
+                    "total_bytes" : 135012552704,
+                    "writes" : {
+                        "counter" : 2167046,
+                        "hz" : 18.599599999999999,
+                        "sectors" : 1456
+                    }
+                },
+                "excluded" : false,
+                "fault_domain" : "test-cluster-storage-2",
+                "locality" : {
+                    "instance_id" : "storage-2",
+                    "machineid" : "test-cluster-storage-2",
+                    "processid" : "f483247d4d5f279ef02c549680cbde64",
+                    "zoneid" : "test-cluster-storage-2"
+                },
+                "machine_id" : "test-cluster-storage-2",
+                "memory" : {
+                    "available_bytes" : 8589934592,
+                    "limit_bytes" : 8589934592,
+                    "unused_allocated_memory" : 0,
+                    "used_bytes" : 196325376
+                },
+                "messages" : [
+                ],
+                "network" : {
+                    "connection_errors" : {
+                        "hz" : 0
+                    },
+                    "connections_closed" : {
+                        "hz" : 0
+                    },
+                    "connections_established" : {
+                        "hz" : 0.19999600000000001
+                    },
+                    "current_connections" : 9,
+                    "megabits_received" : {
+                        "hz" : 0.072945799999999991
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.094430200000000006
+                    },
+                    "tls_policy_failures" : {
+                        "hz" : 0
+                    }
+                },
+                "roles" : [
+                    {
+                        "role" : "coordinator"
+                    },
+                    {
+                        "commit_batching_window_size" : {
+                            "count" : 39,
+                            "max" : 0.0044400400000000001,
+                            "mean" : 0.0020606499999999998,
+                            "median" : 0.00163269,
+                            "min" : 0.001,
+                            "p25" : 0.001,
+                            "p90" : 0.0038877899999999999,
+                            "p95" : 0.0040663499999999998,
+                            "p99" : 0.0042465699999999999,
+                            "p99.9" : 0.0042465699999999999
+                        },
+                        "commit_latency_statistics" : {
+                            "count" : 31,
+                            "max" : 0.0941882,
+                            "mean" : 0.039759500000000003,
+                            "median" : 0.0079488800000000002,
+                            "min" : 0.0040233099999999996,
+                            "p25" : 0.0054645499999999994,
+                            "p90" : 0.088441099999999995,
+                            "p95" : 0.09051300000000001,
+                            "p99" : 0.090540900000000007,
+                            "p99.9" : 0.090540900000000007
+                        },
+                        "id" : "0eb90e4a0ece85b3",
+                        "role" : "commit_proxy"
+                    },
+                    {
+                        "bytes_queried" : {
+                            "counter" : 0,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "data_lag" : {
+                            "seconds" : 0.19625800000000002,
+                            "versions" : 196258
+                        },
+                        "data_version" : 74805090,
+                        "durability_lag" : {
+                            "seconds" : 5,
+                            "versions" : 5000000
+                        },
+                        "durable_bytes" : {
+                            "counter" : 1106,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "durable_version" : 69805090,
+                        "fetched_versions" : {
+                            "counter" : 74322901,
+                            "hz" : 892381,
+                            "roughness" : 2047820.0000000002
+                        },
+                        "fetches_from_logs" : {
+                            "counter" : 44,
+                            "hz" : 0.59999000000000002,
+                            "roughness" : 0.37684500000000004
+                        },
+                        "finished_queries" : {
+                            "counter" : 0,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "id" : "b5e42e100018bf11",
+                        "input_bytes" : {
+                            "counter" : 1106,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "keys_queried" : {
+                            "counter" : 0,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "kvstore_available_bytes" : 84178112512,
+                        "kvstore_free_bytes" : 84178112512,
+                        "kvstore_inline_keys" : 0,
+                        "kvstore_total_bytes" : 135012552704,
+                        "kvstore_total_nodes" : 0,
+                        "kvstore_total_size" : 0,
+                        "kvstore_used_bytes" : 104861752,
+                        "local_rate" : 100,
+                        "low_priority_queries" : {
+                            "counter" : 0,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "mutation_bytes" : {
+                            "counter" : 52,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "mutations" : {
+                            "counter" : 1,
+                            "hz" : 0,
+                            "roughness" : -1
+                        },
+                        "query_queue_max" : 0,
+                        "read_latency_statistics" : {
+                            "count" : 0,
+                            "max" : 0,
+                            "mean" : 0,
+                            "median" : 0,
+                            "min" : 0,
+                            "p25" : 0,
+                            "p90" : 0,
+                            "p95" : 0,
+                            "p99" : 0,
+                            "p99.9" : 0
+                        },
+                        "role" : "storage",
+                        "storage_metadata" : {
+                            "created_time_datetime" : "Thu Mar 10 17:26:08 2022 +0000",
+                            "created_time_timestamp" : 1646933168295447808
+                        },
+                        "stored_bytes" : 0,
+                        "total_queries" : {
+                            "counter" : 0,
+                            "hz" : 0,
+                            "roughness" : -1
+                        }
+                    }
+                ],
+                "run_loop_busy" : 0.0101502,
+                "uptime_seconds" : 85.002899999999997,
+                "version" : "7.1.0-rc1"
+            },
+            "f6e0f7fd80da429d20329ad95d793ca3" : {
+                "address" : "10.1.19.1:4501",
+                "class_source" : "command_line",
+                "class_type" : "log",
+                "command_line" : "/usr/bin/fdbserver --class=log --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.19.1:4501 --locality_instance_id=log-1 --locality_machineid=test-cluster-log-1 --locality_zoneid=test-cluster-log-1 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.19.1:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+                "cpu" : {
+                    "usage_cores" : 0.041810800000000002
+                },
+                "disk" : {
+                    "busy" : 0.010199700000000001,
+                    "free_bytes" : 84178165760,
+                    "reads" : {
+                        "counter" : 6598328,
+                        "hz" : 0,
+                        "sectors" : 0
+                    },
+                    "total_bytes" : 135012552704,
+                    "writes" : {
+                        "counter" : 2167046,
+                        "hz" : 19.599499999999999,
+                        "sectors" : 1600
+                    }
+                },
+                "excluded" : false,
+                "fault_domain" : "test-cluster-log-1",
+                "locality" : {
+                    "instance_id" : "log-1",
+                    "machineid" : "test-cluster-log-1",
+                    "processid" : "f6e0f7fd80da429d20329ad95d793ca3",
+                    "zoneid" : "test-cluster-log-1"
+                },
+                "machine_id" : "test-cluster-log-1",
+                "memory" : {
+                    "available_bytes" : 8589934592,
+                    "limit_bytes" : 8589934592,
+                    "unused_allocated_memory" : 0,
+                    "used_bytes" : 141787136
+                },
+                "messages" : [
+                ],
+                "network" : {
+                    "connection_errors" : {
+                        "hz" : 0
+                    },
+                    "connections_closed" : {
+                        "hz" : 0
+                    },
+                    "connections_established" : {
+                        "hz" : 0
+                    },
+                    "current_connections" : 7,
+                    "megabits_received" : {
+                        "hz" : 0.24052400000000002
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.156584
+                    },
+                    "tls_policy_failures" : {
+                        "hz" : 0
+                    }
+                },
+                "roles" : [
+                    {
+                        "id" : "b05dfb13cf568dfd",
+                        "role" : "master"
+                    },
+                    {
+                        "id" : "cfdd8010b58eda01",
+                        "role" : "data_distributor"
+                    },
+                    {
+                        "id" : "cbeb915c6cceb4a9",
+                        "role" : "ratekeeper"
+                    }
+                ],
+                "run_loop_busy" : 0.027578200000000001,
+                "uptime_seconds" : 85.002700000000004,
+                "version" : "7.1.0-rc1"
+            },
+            "f75644abdf1b06c803b5c3c124fdd0a0" : {
+                "address" : "10.1.18.255:4501",
+                "class_source" : "command_line",
+                "class_type" : "cluster_controller",
+                "command_line" : "/usr/bin/fdbserver --class=cluster_controller --cluster_file=/var/fdb/data/fdb.cluster --datadir=/var/fdb/data --knob_disable_posix_kernel_aio=1 --listen_address=10.1.18.255:4501 --locality_instance_id=cluster_controller-1 --locality_machineid=test-cluster-cluster-controller-1 --locality_zoneid=test-cluster-cluster-controller-1 --logdir=/var/log/fdb-trace-logs --loggroup=test-cluster --public_address=10.1.18.255:4501 --seed_cluster_file=/var/dynamic-conf/fdb.cluster",
+                "cpu" : {
+                    "usage_cores" : 0.011798900000000001
+                },
+                "disk" : {
+                    "busy" : 0.010199400000000001,
+                    "free_bytes" : 84178165760,
+                    "reads" : {
+                        "counter" : 6598328,
+                        "hz" : 0,
+                        "sectors" : 0
+                    },
+                    "total_bytes" : 135012552704,
+                    "writes" : {
+                        "counter" : 2167046,
+                        "hz" : 19.598800000000001,
+                        "sectors" : 1600
+                    }
+                },
+                "excluded" : false,
+                "fault_domain" : "test-cluster-cluster-controller-1",
+                "locality" : {
+                    "instance_id" : "cluster_controller-1",
+                    "machineid" : "test-cluster-cluster-controller-1",
+                    "processid" : "f75644abdf1b06c803b5c3c124fdd0a0",
+                    "zoneid" : "test-cluster-cluster-controller-1"
+                },
+                "machine_id" : "test-cluster-cluster-controller-1",
+                "memory" : {
+                    "available_bytes" : 8589934592,
+                    "limit_bytes" : 8589934592,
+                    "unused_allocated_memory" : 0,
+                    "used_bytes" : 142704640
+                },
+                "messages" : [
+                ],
+                "network" : {
+                    "connection_errors" : {
+                        "hz" : 0
+                    },
+                    "connections_closed" : {
+                        "hz" : 0
+                    },
+                    "connections_established" : {
+                        "hz" : 0.199988
+                    },
+                    "current_connections" : 9,
+                    "megabits_received" : {
+                        "hz" : 0.18640100000000001
+                    },
+                    "megabits_sent" : {
+                        "hz" : 0.18823100000000001
+                    },
+                    "tls_policy_failures" : {
+                        "hz" : 0
+                    }
+                },
+                "roles" : [
+                    {
+                        "id" : "1f953018ad2e746f",
+                        "role" : "cluster_controller"
+                    }
+                ],
+                "run_loop_busy" : 0.0075524900000000002,
+                "uptime_seconds" : 85.002899999999997,
+                "version" : "7.1.0-rc1"
+            }
+        },
+        "protocol_version" : "fdb00b071010000",
+        "qos" : {
+            "batch_performance_limited_by" : {
+                "description" : "The database is not being saturated by the workload.",
+                "name" : "workload",
+                "reason_id" : 2
+            },
+            "batch_released_transactions_per_second" : 0.21091300000000002,
+            "batch_transactions_per_second_limit" : 97585900,
+            "limiting_data_lag_storage_server" : {
+                "seconds" : 0,
+                "versions" : 0
+            },
+            "limiting_durability_lag_storage_server" : {
+                "seconds" : 5.0145299999999997,
+                "versions" : 5014530
+            },
+            "limiting_queue_bytes_storage_server" : 1996,
+            "performance_limited_by" : {
+                "description" : "The database is not being saturated by the workload.",
+                "name" : "workload",
+                "reason_id" : 2
+            },
+            "released_transactions_per_second" : 6.0522499999999999,
+            "throttled_tags" : {
+                "auto" : {
+                    "busy_read" : 0,
+                    "busy_write" : 0,
+                    "count" : 0,
+                    "recommended_only" : 0
+                },
+                "manual" : {
+                    "count" : 0
+                }
+            },
+            "transactions_per_second_limit" : 159461000,
+            "worst_data_lag_storage_server" : {
+                "seconds" : 0,
+                "versions" : 0
+            },
+            "worst_durability_lag_storage_server" : {
+                "seconds" : 5.0150199999999998,
+                "versions" : 5015017
+            },
+            "worst_queue_bytes_log_server" : 12144,
+            "worst_queue_bytes_storage_server" : 1996
+        },
+        "recovery_state" : {
+            "active_generations" : 1,
+            "description" : "Recovery complete.",
+            "name" : "fully_recovered",
+            "seconds_since_last_recovered" : 76.8155
+        },
+        "workload" : {
+            "bytes" : {
+                "read" : {
+                    "counter" : 450479,
+                    "hz" : 5819.1099999999997,
+                    "roughness" : 4053.9899999999998
+                },
+                "written" : {
+                    "counter" : 4904,
+                    "hz" : 13.999600000000001,
+                    "roughness" : 69
+                }
+            },
+            "keys" : {
+                "read" : {
+                    "counter" : 2100,
+                    "hz" : 25.7988,
+                    "roughness" : 16.829899999999999
+                }
+            },
+            "operations" : {
+                "location_requests" : {
+                    "counter" : 56,
+                    "hz" : 0.39998900000000004,
+                    "roughness" : 0.97065500000000005
+                },
+                "low_priority_reads" : {
+                    "counter" : 0,
+                    "hz" : 0,
+                    "roughness" : 0
+                },
+                "memory_errors" : {
+                    "counter" : 0,
+                    "hz" : 0,
+                    "roughness" : 0
+                },
+                "read_requests" : {
+                    "counter" : 872,
+                    "hz" : 8.5995899999999992,
+                    "roughness" : 4.2513800000000002
+                },
+                "reads" : {
+                    "counter" : 872,
+                    "hz" : 8.5995899999999992,
+                    "roughness" : 4.2513800000000002
+                },
+                "writes" : {
+                    "counter" : 74,
+                    "hz" : 0.39998900000000004,
+                    "roughness" : 1
+                }
+            },
+            "transactions" : {
+                "committed" : {
+                    "counter" : 32,
+                    "hz" : 0.39998900000000004,
+                    "roughness" : 0.08752349999999999
+                },
+                "conflicted" : {
+                    "counter" : 9,
+                    "hz" : 0,
+                    "roughness" : 0
+                },
+                "rejected_for_queued_too_long" : {
+                    "counter" : 0,
+                    "hz" : 0,
+                    "roughness" : 0
+                },
+                "started" : {
+                    "counter" : 429,
+                    "hz" : 5.99993,
+                    "roughness" : 2.8608099999999999
+                },
+                "started_batch_priority" : {
+                    "counter" : 14,
+                    "hz" : 0.19999800000000001,
+                    "roughness" : 0
+                },
+                "started_default_priority" : {
+                    "counter" : 192,
+                    "hz" : 2.7999700000000001,
+                    "roughness" : 1.2489600000000001
+                },
+                "started_immediate_priority" : {
+                    "counter" : 223,
+                    "hz" : 2.9999700000000002,
+                    "roughness" : 1.4561900000000001
+                }
+            }
+        }
+    }
+}
diff --git a/config/tests/dns/client.yaml b/config/tests/dns/client.yaml
index 63eb5ae4..b1e9e690 100644
--- a/config/tests/dns/client.yaml
+++ b/config/tests/dns/client.yaml
@@ -17,7 +17,7 @@ spec:
       automountServiceAccountToken: false
       initContainers:
         - name: foundationdb-kubernetes-init-7-1
-          image: foundationdb/foundationdb-kubernetes-sidecar:7.1.0-1
+          image: foundationdb/foundationdb-kubernetes-sidecar:7.1.0-rc2-1
           imagePullPolicy: IfNotPresent
           args:
             - "--copy-file"
@@ -34,10 +34,8 @@ spec:
               mountPath: /var/output-files
       containers:
         - name: client
-          image: foundationdb/foundationdb-sample-go-app:latest
+          image: foundationdb/foundationdb-sample-python-app:latest
           imagePullPolicy: IfNotPresent
-          command:
-            - /go/bin/fdb-demo-golang
           env:
             - name: FDB_CLUSTER_FILE
               value: /var/dynamic-conf/fdb.cluster
@@ -87,4 +85,4 @@ spec:
   type: LoadBalancer
   ports:
     - port: 9562
-      targetPort: 8080
+      targetPort: 5000
diff --git a/config/tests/dns/dns.yaml b/config/tests/dns/dns.yaml
index 2666741b..922eb01e 100644
--- a/config/tests/dns/dns.yaml
+++ b/config/tests/dns/dns.yaml
@@ -1,6 +1,6 @@
 - op: add
   path: "/spec/version"
-  value: 7.1.0
+  value: 7.1.0-rc2
 - op: add
   path: "/spec/routing/useDNSInClusterFile"
   value: true
diff --git a/config/tests/dns/kustomization.yaml b/config/tests/dns/kustomization.yaml
index 11e01e88..3cc60515 100644
--- a/config/tests/dns/kustomization.yaml
+++ b/config/tests/dns/kustomization.yaml
@@ -10,7 +10,7 @@ patchesJson6902:
 - path: dns.yaml
   target:
     group: apps.foundationdb.org
-    version: v1beta1
+    version: v1beta2
     kind: FoundationDBCluster
     name: test-cluster
 resources:
diff --git a/config/tests/tls/kustomization.yaml b/config/tests/tls/kustomization.yaml
index 059ec60d..00f828e1 100644
--- a/config/tests/tls/kustomization.yaml
+++ b/config/tests/tls/kustomization.yaml
@@ -5,6 +5,6 @@ patchesJson6902:
 - path: tls.yaml
   target:
     group: apps.foundationdb.org
-    version: v1beta1
+    version: v1beta2
     kind: FoundationDBCluster
     name: test-cluster
diff --git a/config/tests/unified_image/kustomization.yaml b/config/tests/unified_image/kustomization.yaml
index 6ccfc843..f144c0a3 100644
--- a/config/tests/unified_image/kustomization.yaml
+++ b/config/tests/unified_image/kustomization.yaml
@@ -8,6 +8,6 @@ patchesJson6902:
 - path: images.yaml
   target:
     group: apps.foundationdb.org
-    version: v1beta1
+    version: v1beta2
     kind: FoundationDBCluster
     name: test-cluster
diff --git a/controllers/admin_client_test.go b/controllers/admin_client_test.go
index 2a969afb..d9ab6ded 100644
--- a/controllers/admin_client_test.go
+++ b/controllers/admin_client_test.go
@@ -71,11 +71,13 @@ var _ = Describe("admin_client_test", func() {
 					StorageEngine:  "ssd-2",
 					UsableRegions:  1,
 					RoleCounts: fdbv1beta2.RoleCounts{
-						Logs:       3,
-						Proxies:    3,
-						Resolvers:  1,
-						LogRouters: -1,
-						RemoteLogs: -1,
+						Logs:          3,
+						Proxies:       3,
+						CommitProxies: 2,
+						GrvProxies:    1,
+						Resolvers:     1,
+						LogRouters:    -1,
+						RemoteLogs:    -1,
 					},
 					VersionFlags: fdbv1beta2.VersionFlags{
 						LogSpill: 2,
diff --git a/controllers/cluster_controller_test.go b/controllers/cluster_controller_test.go
index 33607515..9f00d655 100644
--- a/controllers/cluster_controller_test.go
+++ b/controllers/cluster_controller_test.go
@@ -226,11 +226,13 @@ var _ = Describe("cluster_controller", func() {
 				Expect(adminClient.DatabaseConfiguration.RedundancyMode).To(Equal(fdbv1beta2.RedundancyModeDouble))
 				Expect(adminClient.DatabaseConfiguration.StorageEngine).To(Equal(fdbv1beta2.StorageEngineSSD2))
 				Expect(adminClient.DatabaseConfiguration.RoleCounts).To(Equal(fdbv1beta2.RoleCounts{
-					Logs:       3,
-					Proxies:    3,
-					Resolvers:  1,
-					RemoteLogs: -1,
-					LogRouters: -1,
+					Logs:          3,
+					Proxies:       3,
+					CommitProxies: 2,
+					GrvProxies:    1,
+					Resolvers:     1,
+					RemoteLogs:    -1,
+					LogRouters:    -1,
 				}))
 			})
 
@@ -1164,7 +1166,7 @@ var _ = Describe("cluster_controller", func() {
 
 					configuration := cluster.DesiredDatabaseConfiguration()
 					configuration.LogVersion = 3
-					err = adminClient.ConfigureDatabase(configuration, false)
+					err = adminClient.ConfigureDatabase(configuration, false, cluster.Spec.Version)
 					Expect(err).NotTo(HaveOccurred())
 
 					generationGap = 1
@@ -3698,7 +3700,7 @@ var _ = Describe("cluster_controller", func() {
 			})
 
 			It("should be the public IP from the pod", func() {
-				result := podmanager.GetPublicIPs(pod)
+				result := podmanager.GetPublicIPs(pod, log)
 				Expect(result).To(Equal([]string{"1.1.1.1"}))
 			})
 		})
@@ -3717,7 +3719,7 @@ var _ = Describe("cluster_controller", func() {
 			})
 
 			It("should select the address based on the spec", func() {
-				result := podmanager.GetPublicIPs(pod)
+				result := podmanager.GetPublicIPs(pod, log)
 				Expect(result).To(Equal([]string{"2001:db8::ff00:42:8329"}))
 			})
 
@@ -3732,7 +3734,7 @@ var _ = Describe("cluster_controller", func() {
 				})
 
 				It("should be empty", func() {
-					result := podmanager.GetPublicIPs(pod)
+					result := podmanager.GetPublicIPs(pod, log)
 					Expect(result).To(BeEmpty())
 				})
 			})
@@ -3752,14 +3754,14 @@ var _ = Describe("cluster_controller", func() {
 			})
 
 			It("should select the address based on the spec", func() {
-				result := podmanager.GetPublicIPs(pod)
+				result := podmanager.GetPublicIPs(pod, log)
 				Expect(result).To(Equal([]string{"1.1.1.2"}))
 			})
 		})
 
 		Context("with no pod", func() {
 			It("should be empty", func() {
-				result := podmanager.GetPublicIPs(nil)
+				result := podmanager.GetPublicIPs(nil, log)
 				Expect(result).To(BeEmpty())
 			})
 		})
diff --git a/kubectl-fdb/cmd/configuration_test.go b/kubectl-fdb/cmd/configuration_test.go
index b886ddde..1b4fd1ad 100644
--- a/kubectl-fdb/cmd/configuration_test.go
+++ b/kubectl-fdb/cmd/configuration_test.go
@@ -54,6 +54,7 @@ var _ = Describe("[plugin] configuration command", func() {
 								},
 							},
 						},
+						Version: fdbv1beta2.Versions.Default.String(),
 					},
 				}
 			})
@@ -66,7 +67,7 @@ var _ = Describe("[plugin] configuration command", func() {
 
 				configuration, err := getConfigurationString(kubeClient, "test", "test", false)
 				Expect(err).NotTo(HaveOccurred())
-				Expect(configuration).To(Equal("  usable_regions=0 logs=0 proxies=0 resolvers=0 log_routers=0 remote_logs=0 regions=[{\\\"datacenters\\\":[{\\\"id\\\":\\\"test\\\",\\\"priority\\\":1}]}]"))
+				Expect(configuration).To(Equal("  usable_regions=0 logs=0 resolvers=0 log_routers=0 remote_logs=0 proxies=3 regions=[{\\\"datacenters\\\":[{\\\"id\\\":\\\"test\\\",\\\"priority\\\":1}]}]"))
 			})
 
 			It("should return the same configuration string with failover", func() {
@@ -77,7 +78,7 @@ var _ = Describe("[plugin] configuration command", func() {
 
 				configuration, err := getConfigurationString(kubeClient, "test", "test", true)
 				Expect(err).NotTo(HaveOccurred())
-				Expect(configuration).To(Equal("  usable_regions=0 logs=0 proxies=0 resolvers=0 log_routers=0 remote_logs=0 regions=[{\\\"datacenters\\\":[{\\\"id\\\":\\\"test\\\",\\\"priority\\\":1}]}]"))
+				Expect(configuration).To(Equal("  usable_regions=0 logs=0 resolvers=0 log_routers=0 remote_logs=0 proxies=3 regions=[{\\\"datacenters\\\":[{\\\"id\\\":\\\"test\\\",\\\"priority\\\":1}]}]"))
 			})
 		})
 
@@ -131,6 +132,7 @@ var _ = Describe("[plugin] configuration command", func() {
 								},
 							},
 						},
+						Version: fdbv1beta2.Versions.Default.String(),
 					},
 				}
 
@@ -144,7 +146,7 @@ var _ = Describe("[plugin] configuration command", func() {
 
 				configuration, err := getConfigurationString(kubeClient, "test", "test", false)
 				Expect(err).NotTo(HaveOccurred())
-				Expect(configuration).To(Equal("  usable_regions=0 logs=0 proxies=0 resolvers=0 log_routers=0 remote_logs=0 regions=[{\\\"datacenters\\\":[{\\\"id\\\":\\\"primary\\\",\\\"priority\\\":1},{\\\"id\\\":\\\"primary-sat\\\",\\\"priority\\\":1,\\\"satellite\\\":1},{\\\"id\\\":\\\"remote-sat\\\",\\\"satellite\\\":1}]},{\\\"datacenters\\\":[{\\\"id\\\":\\\"remote\\\"},{\\\"id\\\":\\\"remote-sat\\\",\\\"priority\\\":1,\\\"satellite\\\":1},{\\\"id\\\":\\\"primary-sat\\\",\\\"satellite\\\":1}]}]"))
+				Expect(configuration).To(Equal("  usable_regions=0 logs=0 resolvers=0 log_routers=0 remote_logs=0 proxies=3 regions=[{\\\"datacenters\\\":[{\\\"id\\\":\\\"primary\\\",\\\"priority\\\":1},{\\\"id\\\":\\\"primary-sat\\\",\\\"priority\\\":1,\\\"satellite\\\":1},{\\\"id\\\":\\\"remote-sat\\\",\\\"satellite\\\":1}]},{\\\"datacenters\\\":[{\\\"id\\\":\\\"remote\\\"},{\\\"id\\\":\\\"remote-sat\\\",\\\"priority\\\":1,\\\"satellite\\\":1},{\\\"id\\\":\\\"primary-sat\\\",\\\"satellite\\\":1}]}]"))
 			})
 
 			It("should return the configuration string with the modified priority", func() {
@@ -155,7 +157,7 @@ var _ = Describe("[plugin] configuration command", func() {
 
 				configuration, err := getConfigurationString(kubeClient, "test", "test", true)
 				Expect(err).NotTo(HaveOccurred())
-				Expect(configuration).To(Equal("  usable_regions=0 logs=0 proxies=0 resolvers=0 log_routers=0 remote_logs=0 regions=[{\\\"datacenters\\\":[{\\\"id\\\":\\\"primary\\\"},{\\\"id\\\":\\\"primary-sat\\\",\\\"priority\\\":1,\\\"satellite\\\":1},{\\\"id\\\":\\\"remote-sat\\\",\\\"satellite\\\":1}]},{\\\"datacenters\\\":[{\\\"id\\\":\\\"remote\\\",\\\"priority\\\":1},{\\\"id\\\":\\\"remote-sat\\\",\\\"priority\\\":1,\\\"satellite\\\":1},{\\\"id\\\":\\\"primary-sat\\\",\\\"satellite\\\":1}]}]"))
+				Expect(configuration).To(Equal("  usable_regions=0 logs=0 resolvers=0 log_routers=0 remote_logs=0 proxies=3 regions=[{\\\"datacenters\\\":[{\\\"id\\\":\\\"primary\\\"},{\\\"id\\\":\\\"primary-sat\\\",\\\"priority\\\":1,\\\"satellite\\\":1},{\\\"id\\\":\\\"remote-sat\\\",\\\"satellite\\\":1}]},{\\\"datacenters\\\":[{\\\"id\\\":\\\"remote\\\",\\\"priority\\\":1},{\\\"id\\\":\\\"remote-sat\\\",\\\"priority\\\":1,\\\"satellite\\\":1},{\\\"id\\\":\\\"primary-sat\\\",\\\"satellite\\\":1}]}]"))
 			})
 		})
 	})
diff --git a/kubectl-fdb/cmd/remove_process_group_test.go b/kubectl-fdb/cmd/remove_process_group_test.go
index c37db095..df46e946 100644
--- a/kubectl-fdb/cmd/remove_process_group_test.go
+++ b/kubectl-fdb/cmd/remove_process_group_test.go
@@ -247,6 +247,57 @@ var _ = Describe("[plugin] remove instances command", func() {
 						RemoveAllFailed: true,
 					}),
 			)
+
+			When("a procress group was already marked for removal", func() {
+				var kubeClient client.Client
+
+				BeforeEach(func() {
+					cluster.Spec.ProcessGroupsToRemove = []string{"instance-1"}
+					scheme := runtime.NewScheme()
+					_ = clientgoscheme.AddToScheme(scheme)
+					_ = fdbv1beta2.AddToScheme(scheme)
+					kubeClient = fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(cluster, &podList).Build()
+				})
+
+				When("adding the same process group to the removal list without exclusion", func() {
+					It("should add the process group to the removal without exclusion list", func() {
+						removals := []string{"instance-1"}
+						err := replaceProcessGroups(kubeClient, clusterName, removals, namespace, false, false, true, false)
+						Expect(err).NotTo(HaveOccurred())
+
+						var resCluster fdbv1beta2.FoundationDBCluster
+						err = kubeClient.Get(ctx.Background(), client.ObjectKey{
+							Namespace: namespace,
+							Name:      clusterName,
+						}, &resCluster)
+
+						Expect(err).NotTo(HaveOccurred())
+						Expect(resCluster.Spec.ProcessGroupsToRemove).To(ContainElements(removals))
+						Expect(len(resCluster.Spec.ProcessGroupsToRemove)).To(BeNumerically("==", len(removals)))
+						Expect(resCluster.Spec.ProcessGroupsToRemoveWithoutExclusion).To(ContainElements(removals))
+						Expect(len(resCluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(BeNumerically("==", len(removals)))
+					})
+				})
+
+				When("adding the same process group to the removal list", func() {
+					It("should add the process group to the removal without exclusion list", func() {
+						removals := []string{"instance-1"}
+						err := replaceProcessGroups(kubeClient, clusterName, removals, namespace, true, false, true, false)
+						Expect(err).NotTo(HaveOccurred())
+
+						var resCluster fdbv1beta2.FoundationDBCluster
+						err = kubeClient.Get(ctx.Background(), client.ObjectKey{
+							Namespace: namespace,
+							Name:      clusterName,
+						}, &resCluster)
+
+						Expect(err).NotTo(HaveOccurred())
+						Expect(resCluster.Spec.ProcessGroupsToRemove).To(ContainElements(removals))
+						Expect(len(resCluster.Spec.ProcessGroupsToRemove)).To(BeNumerically("==", len(removals)))
+						Expect(len(resCluster.Spec.ProcessGroupsToRemoveWithoutExclusion)).To(BeNumerically("==", 0))
+					})
+				})
+			})
 		})
 	})
 })