Repository: metal3-io/baremetal-operator
Pull request #1615 (2024-03-09 13:52:12)

🌱Added unit tests for baremetalHostTypes and bmceventSubscriptionController
**What this PR does / why we need it**:
This PR adds unit tests to baremetalhost_types_test.go for SetOperationalStatus,HasBMCDetails,SetHardwareProfile function in baremetalhost_types.go and it also adds unit tests for adding finalizers in bmceventsubscription_controller_test.go
**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:  This PR increases unit test coverage in baremetalhost_types and slightly in bmceventsubscription_controller
Part of #1574


metal3-io-bot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please assign [lentzi90](https://github.com/lentzi90) for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=metal3-io%2Fbaremetal-operator).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/metal3-io/baremetal-operator/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["lentzi90"]} -->
metal3-io-bot: Hi @NeerajNagure. Thanks for your PR.

I'm waiting for a [metal3-io](https://github.com/orgs/metal3-io/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://github.com/orgs/metal3-io/people) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=metal3-io%2Fbaremetal-operator).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

NeerajNagure: In bmceventsubscription_controller I could not understand how can we get the Provisioning ID i.e. host.Status.Provisioning.Id where host is metal3api.baremetalhost.Provisioning Id is required to create provisioner which leads to letting me to write tests for createsubscription,deletesubscription,etc.
tuminoid: /ok-to-test
tuminoid: > Can someone review this PR and provide suggestions?

Most of the approvers/reviewers were in KubeCon last week, hence the silence so far. 

/cc @lentzi90 @dtantsur @kashifest @zaneb 
tuminoid: Please fix the linting errors.
NeerajNagure: I have fixed the linting errors
NeerajNagure: @lentzi90 I have now called addFinalizers directly instead of going through reconcile and I have also added a test on getprovisioner.Please review it and provide feedback
NeerajNagure: I would write the tests for create/delete subscription in another PR
lentzi90: Alright that is fine! Could you just update the description so it has "Part of https://github.com/metal3-io/baremetal-operator/issues/1574" instead of "Fixes https://github.com/metal3-io/baremetal-operator/issues/1574"? Otherwise this will automatically close the issue, which is still not completely done.
lentzi90: Please [squash the commits](https://stackoverflow.com/questions/1657017/how-to-squash-all-git-commits-into-one) and remeber to add `-s` to [sign off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) to make the DCO check green

Patch:
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 32d89893..8fbebf20 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -10,7 +10,7 @@ permissions: {}
 
 jobs:
   build:
-    name: tag release
+    name: release
     runs-on: ubuntu-latest
 
     permissions:
@@ -36,4 +36,4 @@ jobs:
       with:
         draft: true
         files: out/*
-        body_path: releasenotes/${{ env.RELEASE_TAG }}.md
+        body_path: releasenotes/releasenotes.md
diff --git a/Dockerfile b/Dockerfile
index cd9aa58d..7447f457 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # Support FROM override
-ARG BUILD_IMAGE=docker.io/golang:1.21.8@sha256:856073656d1a517517792e6cdd2f7a5ef080d3ca2dff33e518c8412f140fdd2d
+ARG BUILD_IMAGE=docker.io/golang:1.21.7@sha256:549dd88a1a53715f177b41ab5fee25f7a376a6bb5322ac7abe263480d9554021
 ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:9ecc53c269509f63c69a266168e4a687c7eb8c0cfd753bd8bfcaa4f58a90876f
 
 # Build the manager binary
diff --git a/Makefile b/Makefile
index 71b600fe..f898bdde 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ RUN_NAMESPACE = metal3
 GO_TEST_FLAGS = $(TEST_FLAGS)
 DEBUG = --debug
 COVER_PROFILE = cover.out
-GO_VERSION ?= 1.21.8
+GO_VERSION ?= 1.21.7
 
 ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
 
@@ -128,7 +128,7 @@ test-e2e: $(GINKGO) ## Run the end-to-end tests
 ## --------------------------------------
 
 .PHONY: linters
-linters: lint generate-check
+linters: lint generate-check fmt-check
 
 $(GOLANGCI_LINT):
 	GOBIN=$(TOOLS_BIN_DIR) go install $(GOLANGCI_LINT_PKG)@$(GOLANGCI_LINT_VER)
@@ -167,7 +167,7 @@ demo: generate lint manifests ## Run in demo mode
 	go run -ldflags $(LDFLAGS) ./main.go -namespace=$(RUN_NAMESPACE) -dev -demo-mode -webhook-port=0 $(RUN_FLAGS)
 
 .PHONY: run-test-mode
-run-test-mode: generate lint manifests ## Run against the configured Kubernetes cluster in ~/.kube/config
+run-test-mode: generate fmt-check lint manifests ## Run against the configured Kubernetes cluster in ~/.kube/config
 	go run -ldflags $(LDFLAGS) ./main.go -namespace=$(RUN_NAMESPACE) -dev -test-mode -webhook-port=0 $(RUN_FLAGS)
 
 .PHONY: install
@@ -260,6 +260,14 @@ generate-check:
 generate-check-local:
 	IS_CONTAINER=local ./hack/generate.sh
 
+.PHONY: fmt-check
+fmt-check: ## Run gofmt and report an error if any changes are made
+	./hack/gofmt.sh
+
+.PHONY: fmt
+fmt: ## Run gofmt and fix files with formatting issues
+	gofmt -s -w .
+
 ## --------------------------------------
 ## Documentation
 ## --------------------------------------
@@ -320,15 +328,15 @@ mod: ## Clean up go module settings
 ## Release
 ## --------------------------------------
 RELEASE_TAG ?= $(shell git describe --abbrev=0 2>/dev/null)
+PREVIOUS_TAG ?= $(shell git tag -l | grep -B 1 "^$(RELEASE_TAG)" | head -n 1)
 RELEASE_NOTES_DIR := releasenotes
-PREVIOUS_TAG ?= $(shell git tag -l | grep -E "^v[0-9]+\.[0-9]+\.[0-9]+" | sort -V | grep -B1 $(RELEASE_TAG) | grep -E "^v[0-9]+\.[0-9]+\.[0-9]+$$" | head -n 1 2>/dev/null)
 
 $(RELEASE_NOTES_DIR):
 	mkdir -p $(RELEASE_NOTES_DIR)/
 
 .PHONY: release-notes
 release-notes: $(RELEASE_NOTES_DIR)
-	go run ./hack/tools/release/notes.go --from=$(PREVIOUS_TAG) > $(RELEASE_NOTES_DIR)/$(RELEASE_TAG).md
+	go run ./hack/tools/release_notes.go --from=$(PREVIOUS_TAG) > $(RELEASE_NOTES_DIR)/releasenotes.md
 
 go-version: ## Print the go version we use to compile our binaries and images
 	@echo $(GO_VERSION)
diff --git a/apis/go.mod b/apis/go.mod
index 385d46ff..67bf9ad0 100644
--- a/apis/go.mod
+++ b/apis/go.mod
@@ -45,7 +45,7 @@ require (
 	golang.org/x/time v0.3.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
-	google.golang.org/protobuf v1.33.0 // indirect
+	google.golang.org/protobuf v1.31.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/apis/go.sum b/apis/go.sum
index c82d5b68..c5fd5f2e 100644
--- a/apis/go.sum
+++ b/apis/go.sum
@@ -151,8 +151,8 @@ google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6
 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/docs/api.md b/docs/api.md
index 9ad98ce4..af8d6c39 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -36,9 +36,11 @@ communicate with them.
 | Generic IPMI    | IPMI     | iPXE          | `ipmi://<host>:<port>` or just `<host>:<port>`    | Port is optional, defaults to 623                                       |
 | Generic Redfish | Redfish  | iPXE          | `redfish://<host>:<port>/<systemID>`              | System ID is a path like `/redfish/v1/Systems/System.Embedded.1`        |
 |                 |          | Virtual media | `redfish-virtualmedia://<host>:<port>/<systemID>` | Virtual media support is vendor-dependent. Should not be used for Dell. |
-| Dell iDRAC      | Redfish  | iPXE          | `idrac-redfish://<host>:<port>/<systemID>`        | See above about system ID.                                              |
+| Dell iDRAC      | WSMAN    | iPXE          | `idrac://<host>:<port>`                           |                                                                         |
+|                 | Redfish  | iPXE          | `idrac-redfish://<host>:<port>/<systemID>`        | See above about system ID.                                              |
 |                 | Redfish  | Virtual media | `idrac-virtualmedia://<host>:<port>/<systemID>`   | See above about system ID.                                              |
 | Fujitsu iRMC    | iRMC     | iPXE          | `irmc://<host>:<port>`                            | Port is optional, the default is 443.                                   |
+| HUAWEI ibmc     | ibmc     | iPXE          | `ibmc://<host>:<port>`                            |                                                                         |
 | HPE iLO 4       | iLO      | iPXE          | `ilo4://<host>:<port>`                            | Port is optional, the default is 443.                                   |
 |                 | iLO      | Virtual media | `ilo4-virtualmedia://<host>:<port>`               |                                                                         |
 | HPE iLO 5       | iLO      | iPXE          | `ilo5://<host>:<port>`                            |                                                                         |
diff --git a/go.mod b/go.mod
index 0aec2e7e..40cfe06e 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	github.com/gophercloud/gophercloud/v2 v2.0.0-beta.2
 	github.com/metal3-io/baremetal-operator/apis v0.5.0
 	github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.5.0
-	github.com/onsi/gomega v1.32.0
+	github.com/onsi/gomega v1.31.1
 	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.19.0
 	github.com/stretchr/testify v1.9.0
@@ -67,7 +67,7 @@ require (
 	golang.org/x/time v0.3.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
-	google.golang.org/protobuf v1.33.0 // indirect
+	google.golang.org/protobuf v1.32.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/go.sum b/go.sum
index b02e5b68..82f8c9ad 100644
--- a/go.sum
+++ b/go.sum
@@ -85,8 +85,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
 github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
-github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
-github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
+github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
+github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -193,8 +193,8 @@ google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6
 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
+google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
diff --git a/hack/gofmt.sh b/hack/gofmt.sh
new file mode 100755
index 00000000..81084cba
--- /dev/null
+++ b/hack/gofmt.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+set -eux
+
+IS_CONTAINER=${IS_CONTAINER:-false}
+CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-podman}"
+
+if [ "${IS_CONTAINER}" != "false" ]; then
+  TOP_DIR="${1:-.}"
+  export XDG_CACHE_HOME="/tmp/.cache"
+
+  cd "${TOP_DIR}"
+
+  # shellcheck disable=SC2086
+  if [ -n "$(gofmt -l .)" ]; then
+      gofmt -s -d -e .
+      exit 1
+  fi
+else
+  "${CONTAINER_RUNTIME}" run --rm \
+    --env IS_CONTAINER=TRUE \
+    --volume "${PWD}:/workdir:rw,z" \
+    --entrypoint sh \
+    --workdir /workdir \
+    docker.io/golang:1.21 \
+    /workdir/hack/gofmt.sh "${@}"
+fi;
diff --git a/hack/gosec.sh b/hack/gosec.sh
new file mode 100755
index 00000000..0b809822
--- /dev/null
+++ b/hack/gosec.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+set -eux
+
+IS_CONTAINER=${IS_CONTAINER:-false}
+CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-podman}"
+
+if [ "${IS_CONTAINER}" != "false" ]; then
+  export XDG_CACHE_HOME="/tmp/.cache"
+
+  # It seems like gosec does not handle submodules well. Therefore we skip them and run separately.
+  gosec -severity medium --confidence medium -quiet -exclude-dir=apis -exclude-dir=hack/tools -exclude-dir=test ./...
+  (cd apis && gosec -severity medium --confidence medium -quiet ./...)
+  (cd hack/tools && gosec -severity medium --confidence medium -quiet ./...)
+  (cd test && gosec -severity medium --confidence medium -quiet ./...)
+else
+  "${CONTAINER_RUNTIME}" run --rm \
+    --env IS_CONTAINER=TRUE \
+    --volume "${PWD}:/go/src/github.com/metal3-io/baremetal-operator:ro,z" \
+    --entrypoint sh \
+    --workdir /go/src/github.com/metal3-io/baremetal-operator \
+    docker.io/securego/gosec:2.18.2@sha256:2f9daee1739765788945b79de7f46229f33fda5ed35127393d8a1e459f3a7577 \
+    /go/src/github.com/metal3-io/baremetal-operator/hack/gosec.sh "$@"
+fi
diff --git a/hack/shellcheck.sh b/hack/shellcheck.sh
index 3e2f353c..c36df789 100755
--- a/hack/shellcheck.sh
+++ b/hack/shellcheck.sh
@@ -7,13 +7,15 @@ CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-podman}"
 
 if [ "${IS_CONTAINER}" != "false" ]; then
   TOP_DIR="${1:-.}"
-  find "${TOP_DIR}" \( -path ./vendor -prune \) -o -name '*.sh' -exec shellcheck -s bash {} \+
+  find "${TOP_DIR}" \
+       \( -path ./vendor -prune \) \
+       -o -name '*.sh' -exec shellcheck -s bash {} \+
 else
   "${CONTAINER_RUNTIME}" run --rm \
     --env IS_CONTAINER=TRUE \
     --volume "${PWD}:/workdir:ro,z" \
     --entrypoint sh \
     --workdir /workdir \
-    docker.io/koalaman/shellcheck-alpine:v0.10.0@sha256:5921d946dac740cbeec2fb1c898747b6105e585130cc7f0602eec9a10f7ddb63 \
-    /workdir/hack/shellcheck.sh "$@"
-fi
+    docker.io/koalaman/shellcheck-alpine:v0.9.0@sha256:e19ed93c22423970d56568e171b4512c9244fc75dd9114045016b4a0073ac4b7 \
+    /workdir/hack/shellcheck.sh "${@}"
+fi;
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index ef04fa56..58c4391a 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -51,7 +51,7 @@ require (
 	golang.org/x/sys v0.13.0 // indirect
 	golang.org/x/text v0.13.0 // indirect
 	golang.org/x/tools v0.14.0 // indirect
-	google.golang.org/protobuf v1.33.0 // indirect
+	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index 76e05dcb..99351b15 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -219,8 +219,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
 google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
+google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
diff --git a/hack/tools/release/notes.go b/hack/tools/release_notes.go
similarity index 79%
rename from hack/tools/release/notes.go
rename to hack/tools/release_notes.go
index 0283d6f4..9446f5d9 100644
--- a/hack/tools/release/notes.go
+++ b/hack/tools/release_notes.go
@@ -45,10 +45,6 @@ const (
 	superseded    = ":recycle: Superseded or Reverted"
 )
 
-const (
-	warningTemplate = ":rotating_light: This is a %s. Use it only for testing purposes. If you find any bugs, file an [issue](https://github.com/metal3-io/baremetal-operator/issues/new/).\n\n"
-)
-
 var (
 	outputOrder = []string{
 		warning,
@@ -89,14 +85,6 @@ func lastTag() string {
 	return string(bytes.TrimSpace(out))
 }
 
-func isBeta(tag string) bool {
-	return strings.Contains(tag, "-beta.")
-}
-
-func isRC(tag string) bool {
-	return strings.Contains(tag, "-rc.")
-}
-
 func firstCommit() string {
 	cmd := exec.Command("git", "rev-list", "--max-parents=0", "HEAD")
 	out, err := cmd.Output()
@@ -109,7 +97,7 @@ func firstCommit() string {
 func run() int {
 	lastTag := lastTag()
 	latestTag := latestTag()
-	cmd := exec.Command("git", "rev-list", lastTag+"..HEAD", "--merges", "--pretty=format:%B") // #nosec G204:gosec
+	cmd := exec.Command("git", "rev-list", lastTag+"..HEAD", "--merges", "--pretty=format:%B")
 
 	merges := map[string][]string{
 		features:      {},
@@ -186,15 +174,12 @@ func run() int {
 		merges[key] = append(merges[key], formatMerge(body, prNumber))
 	}
 
-	// Add empty superseded section, if not beta/rc, we don't cleanup those notes
-	if !isBeta(latestTag) && !isRC(latestTag) {
-		merges[superseded] = append(merges[superseded], "- `<insert superseded bumps and reverts here>`")
-	}
+	// Add empty superseded section
+	merges[superseded] = append(merges[superseded], "- `<insert superseded bumps and reverts here>`")
 
 	// TODO Turn this into a link (requires knowing the project name + organization)
 	fmt.Printf("Changes since %v\n---\n", lastTag)
 
-	// print the changes by category
 	for _, key := range outputOrder {
 		mergeslice := merges[key]
 		if len(mergeslice) > 0 {
@@ -204,29 +189,10 @@ func run() int {
 			}
 			fmt.Println()
 		}
-
-		// if we're doing beta/rc, print breaking changes and hide the rest of the changes
-		if key == warning {
-			if isBeta(latestTag) {
-				fmt.Printf(warningTemplate, "BETA RELEASE")
-			}
-			if isRC(latestTag) {
-				fmt.Printf(warningTemplate, "RELEASE CANDIDATE")
-			}
-			if isBeta(latestTag) || isRC(latestTag) {
-				fmt.Printf("<details>\n")
-				fmt.Printf("<summary>More details about the release</summary>\n\n")
-			}
-		}
-	}
-
-	// then close the details if we had it open
-	if isBeta(latestTag) || isRC(latestTag) {
-		fmt.Printf("</details>\n\n")
 	}
 
-	fmt.Printf("The image for this release is: %v\n", latestTag)
-	fmt.Println("\n_Thanks to all our contributors!_ 😊")
+	fmt.Printf("The container image for this release is: %v\n", latestTag)
+	fmt.Println("\nThanks to all our contributors! 😊")
 
 	return 0
 }
diff --git a/hack/verify-release.sh b/hack/verify-release.sh
index 2c45730c..79f1351e 100755
--- a/hack/verify-release.sh
+++ b/hack/verify-release.sh
@@ -406,37 +406,6 @@ verify_container_images()
     echo -e "Done\n"
 }
 
-verify_container_base_image()
-{
-    # check if the golang used for container image build is latest of its minor
-    local image tag
-
-    echo "Verifying container base images ..."
-
-    image_and_tag="$(grep "^ARG BUILD_IMAGE=" Dockerfile | cut -f2 -d=)"
-    image="${image_and_tag/:*}"
-    image_and_tag_without_sha="${image_and_tag/@sha256:*}"
-    tag="${image_and_tag_without_sha/*:}"
-    tag_minor="${tag%.*}"
-
-    # quay paginates 50 items at a time, so it is simpler to use gcrane
-    # to list all the tags, than DIY parse the pagination logic
-    if ! "${GCRANE_CMD[@]}" ls --platform "linux/amd64" "${image}" 2>/dev/null > "${TAG_LOG}"; then
-        echo "ERROR: cannot list container tags for ${image}"
-        return 1
-    fi
-    latest_minor="$(sort -rV < "${TAG_LOG}" | cut -f2 -d: | grep -E "^v?${tag_minor/./\\.}\.[[:digit:]]+$" | head -1)"
-
-    if [[ -z "${latest_minor}" ]]; then
-        echo "WARNING: could not find any minor releases of ${image_and_tag_without_sha}"
-    elif [[ "${latest_minor}" != "${tag}" ]]; then
-        echo "WARNING: container base image ${image_and_tag_without_sha} is not the latest minor"
-        echo "WARNING: latest minor ${latest_minor} != ${tag}, needs a bump"
-    fi
-
-    echo -e "Done\n"
-}
-
 
 #
 # helper functions for module related checks
@@ -618,7 +587,6 @@ if [[ -n "${TAG_EXISTS}" ]]; then
 fi
 
 # always verified
-verify_container_base_image
 verify_module_versions
 verify_module_group_versions
 verify_module_releases
diff --git a/pkg/hardwareutils/bmc/access.go b/pkg/hardwareutils/bmc/access.go
index d3860286..81830c06 100644
--- a/pkg/hardwareutils/bmc/access.go
+++ b/pkg/hardwareutils/bmc/access.go
@@ -9,13 +9,6 @@ import (
 	"strings"
 )
 
-const (
-	noRaid   = "no-raid"
-	ipxe     = "ipxe"
-	enabled  = "Enabled"
-	disabled = "Disabled"
-)
-
 // AccessDetailsFactory describes a callable that returns a new
 // AccessDetails based on the input parameters.
 type AccessDetailsFactory func(parsedURL *url.URL, disableCertificateVerification bool) (AccessDetails, error)
diff --git a/pkg/hardwareutils/bmc/ibmc.go b/pkg/hardwareutils/bmc/ibmc.go
new file mode 100644
index 00000000..24227b39
--- /dev/null
+++ b/pkg/hardwareutils/bmc/ibmc.go
@@ -0,0 +1,132 @@
+package bmc
+
+import (
+	"fmt"
+	"net/url"
+	"strings"
+)
+
+const (
+	noRaid = "no-raid"
+	ibmc   = "ibmc"
+	ipxe   = "ipxe"
+)
+
+func init() {
+	RegisterFactory(ibmc, newIbmcAccessDetails, []string{"http", "https"})
+}
+
+func newIbmcAccessDetails(parsedURL *url.URL, disableCertificateVerification bool) (AccessDetails, error) {
+	return &ibmcAccessDetails{
+		bmcType:                        parsedURL.Scheme,
+		host:                           parsedURL.Host,
+		path:                           parsedURL.Path,
+		disableCertificateVerification: disableCertificateVerification,
+	}, nil
+}
+
+type ibmcAccessDetails struct {
+	bmcType                        string
+	host                           string
+	path                           string
+	disableCertificateVerification bool
+}
+
+func (a *ibmcAccessDetails) Type() string {
+	return a.bmcType
+}
+
+// NeedsMAC returns true when the host is going to need a separate
+// port created rather than having it discovered.
+func (a *ibmcAccessDetails) NeedsMAC() bool {
+	// For the inspection to work, we need a MAC address
+	// https://github.com/metal3-io/baremetal-operator/pull/284#discussion_r317579040
+	return true
+}
+
+func (a *ibmcAccessDetails) Driver() string {
+	return ibmc
+}
+
+func (a *ibmcAccessDetails) DisableCertificateVerification() bool {
+	return a.disableCertificateVerification
+}
+
+const ibmcDefaultScheme = "https"
+
+// DriverInfo returns a data structure to pass as the DriverInfo
+// parameter when creating a node in Ironic. The structure is
+// pre-populated with the access information, and the caller is
+// expected to add any other information that might be needed (such as
+// the kernel and ramdisk locations).
+func (a *ibmcAccessDetails) DriverInfo(bmcCreds Credentials) map[string]interface{} {
+	ibmcAddress := []string{}
+	schemes := strings.Split(a.bmcType, "+")
+	if len(schemes) > 1 {
+		ibmcAddress = append(ibmcAddress, schemes[1])
+	} else {
+		ibmcAddress = append(ibmcAddress, ibmcDefaultScheme)
+	}
+	ibmcAddress = append(ibmcAddress, "://")
+	ibmcAddress = append(ibmcAddress, a.host)
+	ibmcAddress = append(ibmcAddress, a.path)
+
+	result := map[string]interface{}{
+		"ibmc_username": bmcCreds.Username,
+		"ibmc_password": bmcCreds.Password,
+		"ibmc_address":  strings.Join(ibmcAddress, ""),
+	}
+
+	if a.disableCertificateVerification {
+		result["ibmc_verify_ca"] = false
+	}
+
+	return result
+}
+
+func (a *ibmcAccessDetails) BIOSInterface() string {
+	return ""
+}
+
+func (a *ibmcAccessDetails) BootInterface() string {
+	return ipxe
+}
+
+func (a *ibmcAccessDetails) FirmwareInterface() string {
+	return ""
+}
+
+func (a *ibmcAccessDetails) ManagementInterface() string {
+	return ibmc
+}
+
+func (a *ibmcAccessDetails) PowerInterface() string {
+	return ibmc
+}
+
+func (a *ibmcAccessDetails) RAIDInterface() string {
+	return noRaid
+}
+
+func (a *ibmcAccessDetails) VendorInterface() string {
+	return ""
+}
+
+func (a *ibmcAccessDetails) SupportsSecureBoot() bool {
+	return false
+}
+
+func (a *ibmcAccessDetails) SupportsISOPreprovisioningImage() bool {
+	return false
+}
+
+func (a *ibmcAccessDetails) RequiresProvisioningNetwork() bool {
+	return true
+}
+
+func (a *ibmcAccessDetails) BuildBIOSSettings(firmwareConfig *FirmwareConfig) (settings []map[string]string, err error) {
+	if firmwareConfig != nil {
+		return nil, fmt.Errorf("firmware settings for %s are not supported", a.Driver())
+	}
+	return nil, nil
+}
diff --git a/pkg/hardwareutils/bmc/idrac.go b/pkg/hardwareutils/bmc/idrac.go
new file mode 100644
index 00000000..3744c44a
--- /dev/null
+++ b/pkg/hardwareutils/bmc/idrac.go
@@ -0,0 +1,172 @@
+package bmc
+
+import (
+	"net/url"
+	"strings"
+)
+
+const (
+	enabled  = "Enabled"
+	disabled = "Disabled"
+	idrac    = "idrac"
+)
+
+func init() {
+	RegisterFactory(idrac, newIDRACAccessDetails, []string{"http", "https"})
+}
+
+func newIDRACAccessDetails(parsedURL *url.URL, disableCertificateVerification bool) (AccessDetails, error) {
+	return &iDracAccessDetails{
+		bmcType:                        parsedURL.Scheme,
+		portNum:                        parsedURL.Port(),
+		hostname:                       parsedURL.Hostname(),
+		path:                           parsedURL.Path,
+		disableCertificateVerification: disableCertificateVerification,
+	}, nil
+}
+
+type iDracAccessDetails struct {
+	bmcType                        string
+	portNum                        string
+	hostname                       string
+	path                           string
+	disableCertificateVerification bool
+}
+
+func (a *iDracAccessDetails) Type() string {
+	return a.bmcType
+}
+
+// NeedsMAC returns true when the host is going to need a separate
+// port created rather than having it discovered.
+func (a *iDracAccessDetails) NeedsMAC() bool {
+	return false
+}
+
+func (a *iDracAccessDetails) Driver() string {
+	return idrac
+}
+
+func (a *iDracAccessDetails) DisableCertificateVerification() bool {
+	return a.disableCertificateVerification
+}
+
+// DriverInfo returns a data structure to pass as the DriverInfo
+// parameter when creating a node in Ironic. The structure is
+// pre-populated with the access information, and the caller is
+// expected to add any other information that might be needed (such as
+// the kernel and ramdisk locations).
+func (a *iDracAccessDetails) DriverInfo(bmcCreds Credentials) map[string]interface{} {
+	result := map[string]interface{}{
+		"drac_username": bmcCreds.Username,
+		"drac_password": bmcCreds.Password,
+		"drac_address":  a.hostname,
+	}
+	if a.disableCertificateVerification {
+		result["drac_verify_ca"] = false
+	}
+
+	schemes := strings.Split(a.bmcType, "+")
+	if len(schemes) > 1 {
+		result["drac_protocol"] = schemes[1]
+	}
+	if a.portNum != "" {
+		result["drac_port"] = a.portNum
+	}
+	if a.path != "" {
+		result["drac_path"] = a.path
+	}
+
+	return result
+}
+
+func (a *iDracAccessDetails) BIOSInterface() string {
+	return ""
+}
+
+func (a *iDracAccessDetails) BootInterface() string {
+	return ipxe
+}
+
+func (a *iDracAccessDetails) FirmwareInterface() string {
+	return ""
+}
+
+func (a *iDracAccessDetails) ManagementInterface() string {
+	return ""
+}
+
+func (a *iDracAccessDetails) PowerInterface() string {
+	return ""
+}
+
+func (a *iDracAccessDetails) RAIDInterface() string {
+	return "idrac-wsman"
+}
+
+func (a *iDracAccessDetails) VendorInterface() string {
+	return ""
+}
+
+// NOTE(dtantsur): change to true if we switch to redfish-based implementations
+// by default.
+func (a *iDracAccessDetails) SupportsSecureBoot() bool {
+	return false
+}
+
+func (a *iDracAccessDetails) SupportsISOPreprovisioningImage() bool {
+	return false
+}
+
+func (a *iDracAccessDetails) RequiresProvisioningNetwork() bool {
+	return true
+}
+
+func (a *iDracAccessDetails) BuildBIOSSettings(firmwareConfig *FirmwareConfig) (settings []map[string]string, err error) {
+	if firmwareConfig == nil {
+		return nil, nil
+	}
+
+	var value string
+
+	if firmwareConfig.VirtualizationEnabled != nil {
+		value = disabled
+		if *firmwareConfig.VirtualizationEnabled {
+			value = enabled
+		}
+		settings = append(settings,
+			map[string]string{
+				"name":  "ProcVirtualization",
+				"value": value,
+			},
+		)
+	}
+
+	if firmwareConfig.SimultaneousMultithreadingEnabled != nil {
+		value = disabled
+		if *firmwareConfig.SimultaneousMultithreadingEnabled {
+			value = enabled
+		}
+		settings = append(settings,
+			map[string]string{
+				"name":  "LogicalProc",
+				"value": value,
+			},
+		)
+	}
+
+	if firmwareConfig.SriovEnabled != nil {
+		value = disabled
+		if *firmwareConfig.SriovEnabled {
+			value = enabled
+		}
+		settings = append(settings,
+			map[string]string{
+				"name":  "SriovGlobalEnable",
+				"value": value,
+			},
+		)
+	}
+
+	return
+}
diff --git a/pkg/hardwareutils/bmc/idrac_virtualmedia.go b/pkg/hardwareutils/bmc/idrac_virtualmedia.go
index 1ad56eda..8f6f4887 100644
--- a/pkg/hardwareutils/bmc/idrac_virtualmedia.go
+++ b/pkg/hardwareutils/bmc/idrac_virtualmedia.go
@@ -6,7 +6,6 @@ import (
 )
 
 const (
-	idrac        = "idrac"
 	idracRedfish = "idrac-redfish"
 	redfish      = "redfish"
 )


Test patch:
diff --git a/apis/metal3.io/v1alpha1/baremetalhost_types_test.go b/apis/metal3.io/v1alpha1/baremetalhost_types_test.go
index 7ee7f7bb..50c9474f 100644
--- a/apis/metal3.io/v1alpha1/baremetalhost_types_test.go
+++ b/apis/metal3.io/v1alpha1/baremetalhost_types_test.go
@@ -510,3 +510,159 @@ func TestBootMode(t *testing.T) {
 		})
 	}
 }
+
+func TestSetOperationalStatus(t *testing.T) {
+	for _, tc := range []struct {
+		Scenario  string
+		Host      BareMetalHost
+		StatusArg OperationalStatus
+		Expected  bool
+	}{
+		{
+			Scenario: "Host is partially configured and operationalStatus is ok",
+			Host: BareMetalHost{
+				Status: BareMetalHostStatus{
+					OperationalStatus: "OK",
+				},
+			},
+			StatusArg: "discovered",
+			Expected:  true,
+		},
+		{
+			Scenario: "Host is detached and operationalStatus is also detached",
+			Host: BareMetalHost{
+				Status: BareMetalHostStatus{
+					OperationalStatus: "detached",
+				},
+			},
+			StatusArg: "detached",
+			Expected:  false,
+		},
+		{
+			Scenario: "Host has an error and operationalStatus also has an error",
+			Host: BareMetalHost{
+				Status: BareMetalHostStatus{
+					OperationalStatus: "error",
+				},
+			},
+			StatusArg: "error",
+			Expected:  false,
+		},
+		{
+			Scenario: "Host is delayed and operationalStatus is not defined",
+			Host: BareMetalHost{
+				Status: BareMetalHostStatus{},
+			},
+			StatusArg: "delayed",
+			Expected:  true,
+		},
+	} {
+		t.Run(tc.Scenario, func(t *testing.T) {
+			actual := tc.Host.SetOperationalStatus(tc.StatusArg)
+			if tc.Expected && !actual {
+				t.Error("Expected a change in operational status")
+			}
+			if !tc.Expected && actual {
+				t.Error("Did not expect a change in operational status")
+			}
+		})
+	}
+}
+
+func TestSetHardwareProfile(t *testing.T) {
+	for _, tc := range []struct {
+		Scenario string
+		Host     BareMetalHost
+		name     string
+		Expected bool
+	}{
+		{
+			Scenario: "Host hardware profile and name is same",
+			Host: BareMetalHost{
+				Status: BareMetalHostStatus{
+					HardwareProfile: "test",
+				},
+			},
+			name:     "test",
+			Expected: false,
+		},
+		{
+			Scenario: "Host hardware profile and name are different",
+			Host: BareMetalHost{
+				Status: BareMetalHostStatus{
+					HardwareProfile: "test",
+				},
+			},
+			name:     "newtest",
+			Expected: true,
+		},
+		{
+			Scenario: "Host hardware profile is not defined",
+			Host: BareMetalHost{
+				Status: BareMetalHostStatus{},
+			},
+			name:     "test",
+			Expected: true,
+		},
+	} {
+		t.Run(tc.Scenario, func(t *testing.T) {
+			actual := tc.Host.SetHardwareProfile(tc.name)
+			if tc.Expected && !actual {
+				t.Error("Expected a change in hardware profile")
+			}
+			if !tc.Expected && actual {
+				t.Error("Did not expect a change in hardware profile")
+			}
+		})
+	}
+}
+
+func TestHasBMCDetails(t *testing.T) {
+	for _, tc := range []struct {
+		Scenario string
+		Host     BareMetalHost
+		Expected bool
+	}{
+		{
+			Scenario: "Only host bmc address is set",
+			Host: BareMetalHost{
+				Spec: BareMetalHostSpec{
+					BMC: BMCDetails{
+						Address: "http://127.0.0.1/",
+					},
+				},
+			},
+			Expected: true,
+		},
+		{
+			Scenario: "Only host credentialsName is set",
+			Host: BareMetalHost{
+				Spec: BareMetalHostSpec{
+					BMC: BMCDetails{
+						CredentialsName: "test",
+					},
+				},
+			},
+			Expected: true,
+		},
+		{
+			Scenario: "Neither host credentialsName nor address is set",
+			Host: BareMetalHost{
+				Spec: BareMetalHostSpec{
+					BMC: BMCDetails{},
+				},
+			},
+			Expected: false,
+		},
+	} {
+		t.Run(tc.Scenario, func(t *testing.T) {
+			actual := tc.Host.HasBMCDetails()
+			if tc.Expected && !actual {
+				t.Error("Expected BMC details exist")
+			}
+			if !tc.Expected && actual {
+				t.Error("Did not expect that BMC details exist")
+			}
+		})
+	}
+}
diff --git a/apis/metal3.io/v1alpha1/baremetalhost_validation_test.go b/apis/metal3.io/v1alpha1/baremetalhost_validation_test.go
index 0222249c..521a6a7e 100644
--- a/apis/metal3.io/v1alpha1/baremetalhost_validation_test.go
+++ b/apis/metal3.io/v1alpha1/baremetalhost_validation_test.go
@@ -303,7 +303,7 @@ func TestValidateCreate(t *testing.T) {
 				ObjectMeta: om,
 				Spec: BareMetalHostSpec{
 					BMC: BMCDetails{
-						Address:         "redfish://127.0.0.1",
+						Address:         "idrac://127.0.0.1",
 						CredentialsName: "test1",
 					},
 					RAID: &RAIDConfig{
@@ -329,7 +329,7 @@ func TestValidateCreate(t *testing.T) {
 				ObjectMeta: om,
 				Spec: BareMetalHostSpec{
 					BMC: BMCDetails{
-						Address:         "redfish://127.0.0.1",
+						Address:         "idrac://127.0.0.1",
 						CredentialsName: "test1",
 					},
 					RAID: &RAIDConfig{
@@ -501,7 +501,7 @@ func TestValidateCreate(t *testing.T) {
 				ObjectMeta: om,
 				Spec: BareMetalHostSpec{
 					BMC: BMCDetails{
-						Address:         "redfish://127.0.0.1",
+						Address:         "idrac://127.0.0.1",
 						CredentialsName: "test1",
 					},
 					Image: &Image{
diff --git a/controllers/metal3.io/bmceventsubscription_controller_test.go b/controllers/metal3.io/bmceventsubscription_controller_test.go
new file mode 100644
index 00000000..899f7824
--- /dev/null
+++ b/controllers/metal3.io/bmceventsubscription_controller_test.go
@@ -0,0 +1,142 @@
+package controllers
+
+import (
+	"context"
+	"testing"
+
+	corev1 "k8s.io/api/core/v1"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
+
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+
+	metal3api "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
+	"github.com/metal3-io/baremetal-operator/pkg/provisioner/fixture"
+	"github.com/metal3-io/baremetal-operator/pkg/utils"
+)
+
+func newBMCTestReconcilerWithFixture(fix *fixture.Fixture, initObjs ...runtime.Object) *BMCEventSubscriptionReconciler {
+	clientBuilder := fakeclient.NewClientBuilder().WithRuntimeObjects(initObjs...)
+	for _, v := range initObjs {
+		clientBuilder = clientBuilder.WithStatusSubresource(v.(client.Object))
+	}
+	c := clientBuilder.Build()
+	// Add a default secret that can be used by most subscriptions.
+	bmcSecret := newBMCCredsSecret(defaultSecretName, "User", "Pass")
+	c.Create(context.TODO(), bmcSecret)
+
+	return &BMCEventSubscriptionReconciler{
+		Client:             c,
+		ProvisionerFactory: fix,
+		Log:                ctrl.Log.WithName("controllers").WithName("BMCEventSubscription"),
+		APIReader:          c,
+	}
+}
+
+type BMCDoneFunc func(subscription *metal3api.BMCEventSubscription, result reconcile.Result) bool
+
+func newBMCTestReconciler(initObjs ...runtime.Object) *BMCEventSubscriptionReconciler {
+	fix := fixture.Fixture{}
+	return newBMCTestReconcilerWithFixture(&fix, initObjs...)
+}
+
+func newBMCRequest(subscription *metal3api.BMCEventSubscription) ctrl.Request {
+	namespacedName := types.NamespacedName{
+		Namespace: subscription.ObjectMeta.Namespace,
+		Name:      subscription.ObjectMeta.Name,
+	}
+	return ctrl.Request{NamespacedName: namespacedName}
+}
+
+func newSubscription(name string, spec *metal3api.BMCEventSubscriptionSpec) *metal3api.BMCEventSubscription {
+	return &metal3api.BMCEventSubscription{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "BareMetalHost",
+			APIVersion: "metal3.io/v1alpha1",
+		}, ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: *spec,
+	}
+}
+
+func newDefaultNamedSubscription(t *testing.T, name string) *metal3api.BMCEventSubscription {
+	t.Helper()
+	spec := &metal3api.BMCEventSubscriptionSpec{
+		HostName:    t.Name(),
+		Destination: "user destination",
+		Context:     "user context",
+		HTTPHeadersRef: &corev1.SecretReference{
+			Name:      defaultSecretName,
+			Namespace: namespace,
+		},
+	}
+	t.Logf("newNamedSubscription(%s)", name)
+	subscription := newSubscription(name, spec)
+	return subscription
+}
+
+func newDefaultSubscription(t *testing.T) *metal3api.BMCEventSubscription {
+	t.Helper()
+	return newDefaultNamedSubscription(t, t.Name())
+}
+
+func HostWithProvisioningID(t *testing.T, host *metal3api.BareMetalHost) *metal3api.BareMetalHost {
+	t.Helper()
+	host.Status.Provisioning.ID = "made-up-id"
+	return host
+}
+
+func TestBMCAddFinalizers(t *testing.T) {
+	host := newDefaultHost(t)
+	subscription := newDefaultSubscription(t)
+	r := newBMCTestReconciler(subscription, host)
+	err := r.addFinalizer(context.Background(), subscription)
+	if err != nil {
+		t.Error(err)
+	}
+	t.Logf("subscription finalizers: %v", subscription.Finalizers)
+	if !utils.StringInList(subscription.Finalizers, metal3api.BMCEventSubscriptionFinalizer) {
+		t.Error("Expected finalizers to be added")
+	}
+}
+
+func TestBMCGetProvisioner(t *testing.T) {
+	host := newDefaultHost(t)
+	subscription := newDefaultSubscription(t)
+	request := newBMCRequest(subscription)
+	r := newBMCTestReconciler(subscription, host)
+	for _, tc := range []struct {
+		Scenario string
+		Host     *metal3api.BareMetalHost
+		Expected bool
+	}{
+		{
+			Scenario: "No provisioning id is provided",
+			Host:     host,
+			Expected: true,
+		},
+		{
+			Scenario: "Provisioning id is provided",
+			Host:     HostWithProvisioningID(t, host),
+			Expected: true,
+		},
+	} {
+		t.Run(tc.Scenario, func(t *testing.T) {
+			prov, actual, err := r.getProvisioner(context.Background(), request, tc.Host)
+			if err != nil {
+				t.Error(err)
+			}
+			t.Log("Provisioner Details:", prov)
+			if tc.Expected && !actual {
+				t.Error("Expected a ready provisioner")
+			}
+		})
+	}
+}
diff --git a/pkg/hardwareutils/bmc/access_test.go b/pkg/hardwareutils/bmc/access_test.go
index 952c77f0..420b57c6 100644
--- a/pkg/hardwareutils/bmc/access_test.go
+++ b/pkg/hardwareutils/bmc/access_test.go
@@ -138,6 +138,46 @@ func TestParse(t *testing.T) {
 			Path:     "",
 		},
 
+		{
+			Scenario: "idrac url",
+			Address:  "idrac://192.168.122.1",
+			Type:     "idrac",
+			Port:     "",
+			Host:     "192.168.122.1",
+			Hostname: "192.168.122.1",
+			Path:     "",
+		},
+
+		{
+			Scenario: "idrac url with path",
+			Address:  "idrac://192.168.122.1:6233/foo",
+			Type:     "idrac",
+			Port:     "6233",
+			Host:     "192.168.122.1",
+			Hostname: "192.168.122.1:6233",
+			Path:     "/foo",
+		},
+
+		{
+			Scenario: "idrac url ipv6",
+			Address:  "idrac://[fe80::fc33:62ff:fe83:8a76]",
+			Type:     "idrac",
+			Port:     "",
+			Host:     "fe80::fc33:62ff:fe83:8a76",
+			Hostname: "[fe80::fc33:62ff:fe83:8a76]",
+			Path:     "",
+		},
+
+		{
+			Scenario: "idrac url, no sep",
+			Address:  "idrac:192.168.122.1",
+			Type:     "idrac",
+			Port:     "",
+			Host:     "192.168.122.1",
+			Hostname: "192.168.122.1",
+			Path:     "",
+		},
+
 		{
 			Scenario: "irmc url",
 			Address:  "irmc://192.168.122.1",
@@ -218,6 +258,87 @@ func TestParse(t *testing.T) {
 			Path:     "",
 		},
 
+		// ibmc
+		{
+			Scenario: "ibmc url",
+			Address:  "ibmc://192.168.122.1",
+			Type:     "ibmc",
+			Port:     "",
+			Host:     "192.168.122.1",
+			Hostname: "192.168.122.1",
+			Path:     "",
+		},
+
+		{
+			Scenario: "ibmc url path",
+			Address:  "ibmc://192.168.122.1:6233/foo",
+			Type:     "ibmc",
+			Port:     "6233",
+			Host:     "192.168.122.1",
+			Hostname: "192.168.122.1:6233",
+			Path:     "/foo",
+		},
+
+		{
+			Scenario: "ibmc url with http scheme",
+			Address:  "ibmc+http://192.168.122.1",
+			Type:     "ibmc+http",
+			Port:     "",
+			Host:     "192.168.122.1",
+			Hostname: "192.168.122.1",
+			Path:     "",
+		},
+
+		{
+			Scenario: "ibmc url with http scheme",
+			Address:  "ibmc+https://192.168.122.1",
+			Type:     "ibmc+https",
+			Port:     "",
+			Host:     "192.168.122.1",
+			Hostname: "192.168.122.1",
+			Path:     "",
+		},
+
+		{
+			Scenario: "ibmc url path",
+			Address:  "ibmc://192.168.122.1:6233/foo",
+			Type:     "ibmc",
+			Port:     "6233",
+			Host:     "192.168.122.1",
+			Hostname: "192.168.122.1:6233",
+			Path:     "/foo",
+		},
+
+		{
+			Scenario: "ibmc url ipv6",
+			Address:  "ibmc://[fe80::fc33:62ff:fe83:8a76]",
+			Type:     "ibmc",
+			Port:     "",
+			Host:     "fe80::fc33:62ff:fe83:8a76",
+			Hostname: "[fe80::fc33:62ff:fe83:8a76]",
+			Path:     "",
+		},
+
+		{
+			Scenario: "ibmc url path ipv6",
+			Address:  "ibmc://[fe80::fc33:62ff:fe83:8a76]:6233/foo",
+			Type:     "ibmc",
+			Port:     "6233",
+			Host:     "fe80::fc33:62ff:fe83:8a76",
+			Hostname: "[fe80::fc33:62ff:fe83:8a76]:6233",
+			Path:     "/foo",
+		},
+
+		{
+			Scenario: "ibmc url no sep",
+			Address:  "ibmc:192.168.122.1",
+			Type:     "ibmc",
+			Port:     "",
+			Host:     "192.168.122.1",
+			Hostname: "192.168.122.1",
+			Path:     "",
+		},
+
 		{
 			Scenario: "ilo4 url",
 			Address:  "ilo4://192.168.122.1",
@@ -372,6 +493,18 @@ func TestStaticDriverInfo(t *testing.T) {
 			power:      "",
 		},
 
+		{
+			Scenario:   "idrac",
+			input:      "idrac://192.168.122.1",
+			needsMac:   false,
+			driver:     "idrac",
+			bios:       "",
+			boot:       "ipxe",
+			firmware:   "",
+			management: "",
+			power:      "",
+		},
+
 		{
 			Scenario:   "irmc",
 			input:      "irmc://192.168.122.1",
@@ -514,6 +647,18 @@ func TestStaticDriverInfo(t *testing.T) {
 			vendor:     "idrac-redfish",
 		},
 
+		{
+			Scenario:   "ibmc",
+			input:      "ibmc://192.168.122.1:6233",
+			needsMac:   true,
+			driver:     "ibmc",
+			bios:       "",
+			boot:       "ipxe",
+			firmware:   "",
+			management: "ibmc",
+			power:      "ibmc",
+		},
+
 		{
 			Scenario:   "ilo4",
 			input:      "ilo4://192.168.122.1",
@@ -613,6 +758,79 @@ func TestDriverInfo(t *testing.T) {
 			},
 		},
 
+		{
+			Scenario: "idrac",
+			input:    "idrac://192.168.122.1",
+			expects: map[string]interface{}{
+				"drac_address":   "192.168.122.1",
+				"drac_password":  "",
+				"drac_username":  "",
+				"drac_verify_ca": false,
+			},
+		},
+
+		{
+			Scenario: "idrac http",
+			input:    "idrac+http://192.168.122.1",
+			expects: map[string]interface{}{
+				"drac_address":   "192.168.122.1",
+				"drac_protocol":  "http",
+				"drac_password":  "",
+				"drac_username":  "",
+				"drac_verify_ca": false,
+			},
+		},
+
+		{
+			Scenario: "idrac https",
+			input:    "idrac+https://192.168.122.1",
+			expects: map[string]interface{}{
+				"drac_address":   "192.168.122.1",
+				"drac_protocol":  "https",
+				"drac_password":  "",
+				"drac_username":  "",
+				"drac_verify_ca": false,
+			},
+		},
+
+		{
+			Scenario: "idrac port and path http",
+			input:    "idrac://192.168.122.1:8080/foo",
+			expects: map[string]interface{}{
+				"drac_address":   "192.168.122.1",
+				"drac_port":      "8080",
+				"drac_path":      "/foo",
+				"drac_password":  "",
+				"drac_username":  "",
+				"drac_verify_ca": false,
+			},
+		},
+
+		{
+			Scenario: "idrac ipv6",
+			input:    "idrac://[fe80::fc33:62ff:fe83:8a76]/foo",
+			expects: map[string]interface{}{
+				"drac_address":   "fe80::fc33:62ff:fe83:8a76",
+				"drac_path":      "/foo",
+				"drac_password":  "",
+				"drac_username":  "",
+				"drac_verify_ca": false,
+			},
+		},
+
+		{
+			Scenario: "idrac ipv6 port and path",
+			input:    "idrac://[fe80::fc33:62ff:fe83:8a76]:8080/foo",
+			expects: map[string]interface{}{
+				"drac_address":   "fe80::fc33:62ff:fe83:8a76",
+				"drac_port":      "8080",
+				"drac_path":      "/foo",
+				"drac_password":  "",
+				"drac_username":  "",
+				"drac_verify_ca": false,
+			},
+		},
+
 		{
 			Scenario: "irmc",
 			input:    "irmc://192.168.122.1",
@@ -791,6 +1009,73 @@ func TestDriverInfo(t *testing.T) {
 			},
 		},
 
+		// ibmc driver testcases
+		{
+			Scenario: "ibmc",
+			input:    "ibmc://192.168.122.1/foo/bar",
+			expects: map[string]interface{}{
+				"ibmc_address":   "https://192.168.122.1/foo/bar",
+				"ibmc_password":  "",
+				"ibmc_username":  "",
+				"ibmc_verify_ca": false,
+			},
+		},
+
+		{
+			Scenario: "ibmc http",
+			input:    "ibmc+http://192.168.122.1/foo/bar",
+			expects: map[string]interface{}{
+				"ibmc_address":   "http://192.168.122.1/foo/bar",
+				"ibmc_password":  "",
+				"ibmc_username":  "",
+				"ibmc_verify_ca": false,
+			},
+		},
+
+		{
+			Scenario: "ibmc https",
+			input:    "ibmc+https://192.168.122.1/foo/bar",
+			expects: map[string]interface{}{
+				"ibmc_address":   "https://192.168.122.1/foo/bar",
+				"ibmc_password":  "",
+				"ibmc_username":  "",
+				"ibmc_verify_ca": false,
+			},
+		},
+
+		{
+			Scenario: "ibmc port",
+			input:    "ibmc://192.168.122.1:8080/foo/bar",
+			expects: map[string]interface{}{
+				"ibmc_address":   "https://192.168.122.1:8080/foo/bar",
+				"ibmc_password":  "",
+				"ibmc_username":  "",
+				"ibmc_verify_ca": false,
+			},
+		},
+
+		{
+			Scenario: "ibmc ipv6",
+			input:    "ibmc://[fe80::fc33:62ff:fe83:8a76]/foo/bar",
+			expects: map[string]interface{}{
+				"ibmc_address":   "https://[fe80::fc33:62ff:fe83:8a76]/foo/bar",
+				"ibmc_password":  "",
+				"ibmc_username":  "",
+				"ibmc_verify_ca": false,
+			},
+		},
+
+		{
+			Scenario: "ibmc ipv6 port",
+			input:    "ibmc://[fe80::fc33:62ff:fe83:8a76]:8080/foo",
+			expects: map[string]interface{}{
+				"ibmc_address":   "https://[fe80::fc33:62ff:fe83:8a76]:8080/foo",
+				"ibmc_password":  "",
+				"ibmc_username":  "",
+				"ibmc_verify_ca": false,
+			},
+		},
+
 		{
 			Scenario: "ilo4",
 			input:    "ilo4://192.168.122.1",
diff --git a/test/go.mod b/test/go.mod
index 5a177520..af4702ea 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -4,8 +4,8 @@ go 1.21
 
 require (
 	github.com/cert-manager/cert-manager v1.10.0
-	github.com/metal3-io/baremetal-operator/apis v0.5.1
-	github.com/metal3-io/cluster-api-provider-metal3/test v1.6.1
+	github.com/metal3-io/baremetal-operator/apis v0.5.0
+	github.com/metal3-io/cluster-api-provider-metal3/test v1.6.0
 	github.com/onsi/ginkgo/v2 v2.13.2
 	github.com/onsi/gomega v1.30.0
 	github.com/pkg/errors v0.9.1
@@ -15,9 +15,9 @@ require (
 	k8s.io/apimachinery v0.28.5
 	k8s.io/klog/v2 v2.100.1
 	k8s.io/utils v0.0.0-20230406110748-d93618cff8a2
-	sigs.k8s.io/cluster-api v1.6.3
-	sigs.k8s.io/cluster-api/test v1.6.3
-	sigs.k8s.io/controller-runtime v0.16.5
+	sigs.k8s.io/cluster-api v1.6.0
+	sigs.k8s.io/cluster-api/test v1.6.0
+	sigs.k8s.io/controller-runtime v0.16.3
 	sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3
 	sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3
 )
@@ -41,7 +41,7 @@ require (
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/distribution/reference v0.5.0 // indirect
 	github.com/docker/distribution v2.8.3+incompatible // indirect
-	github.com/docker/docker v24.0.9+incompatible // indirect
+	github.com/docker/docker v24.0.7+incompatible // indirect
 	github.com/docker/go-connections v0.4.0 // indirect
 	github.com/docker/go-units v0.5.0 // indirect
 	github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 // indirect
@@ -79,9 +79,9 @@ require (
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
-	github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.5.1 // indirect
+	github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.5.0 // indirect
 	github.com/metal3-io/cluster-api-provider-metal3/api v1.5.1 // indirect
-	github.com/metal3-io/ip-address-manager/api v1.6.2 // indirect
+	github.com/metal3-io/ip-address-manager/api v1.6.0 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
@@ -126,7 +126,7 @@ require (
 	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
-	google.golang.org/protobuf v1.33.0 // indirect
+	google.golang.org/protobuf v1.31.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/test/go.sum b/test/go.sum
index cf24bc56..8eed3590 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -98,8 +98,8 @@ github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK
 github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
 github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
 github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0=
-github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
+github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -274,10 +274,10 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
 github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 github.com/metal3-io/cluster-api-provider-metal3/api v1.5.1 h1:IS7g5VUG7truCFUtdTlJ9eG+yPYoiZ0fOFDVfcz160U=
 github.com/metal3-io/cluster-api-provider-metal3/api v1.5.1/go.mod h1:r1CAUr9WA0Ar26YszPLpLtivER6z/8Yh6gQvv5UD8rw=
-github.com/metal3-io/cluster-api-provider-metal3/test v1.6.1 h1:UaTb1Wd+AXulNvyP/7peQ19mOt4U+/Vw09eOH445Eug=
-github.com/metal3-io/cluster-api-provider-metal3/test v1.6.1/go.mod h1:cmWUg8raOTOypLz9iA49Q1DOkytBfOM6HKl966ycbXc=
-github.com/metal3-io/ip-address-manager/api v1.6.2 h1:YPndsZoqYBFlMYQWjbB5DkrRSr0Jr1gZthx0H0xCoEA=
-github.com/metal3-io/ip-address-manager/api v1.6.2/go.mod h1:vf+6KPKpmzOIdR62WJ9u6ck7PT6oUUIPyjcGRvmjpBU=
+github.com/metal3-io/cluster-api-provider-metal3/test v1.6.0 h1:WAZosRvBw4wB2X3idV9ptToAfNYGtBpwHrQfGX9/59E=
+github.com/metal3-io/cluster-api-provider-metal3/test v1.6.0/go.mod h1:fQ3GtLRCr9pwtdff2/EtHRhhO67pEUqlltJq2M0KdzA=
+github.com/metal3-io/ip-address-manager/api v1.6.0 h1:9AZhHHnFv/P0iC2UHvjzRwc34yPjRUmZkdWhF65sjZE=
+github.com/metal3-io/ip-address-manager/api v1.6.0/go.mod h1:qDeEwi/ebU/SS5r2Ek0vmC7tdIPgDsO3eFIpylZAM04=
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
 github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
@@ -342,8 +342,6 @@ github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX
 github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
 github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
-github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
 github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
 github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
@@ -731,8 +729,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -784,12 +782,12 @@ k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/cluster-api v1.6.3 h1:VOlPNg92PQLlhBVLc5pg+cbAuPvGOOBujeFLk9zgnoo=
-sigs.k8s.io/cluster-api v1.6.3/go.mod h1:4FzfgPPiYaFq8X9F9j2SvmggH/4OOLEDgVJuWDqKLig=
-sigs.k8s.io/cluster-api/test v1.6.3 h1:ZCboLCTpKWzSbf+f7MpQT7EN8aeH9DNhJC1T9/vAuAM=
-sigs.k8s.io/cluster-api/test v1.6.3/go.mod h1:AKs25dgW6AnyGaQBoWuXfWnBs+FT7vJmAI/aox64DEI=
-sigs.k8s.io/controller-runtime v0.16.5 h1:yr1cEJbX08xsTW6XEIzT13KHHmIyX8Umvme2cULvFZw=
-sigs.k8s.io/controller-runtime v0.16.5/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0=
+sigs.k8s.io/cluster-api v1.6.0 h1:2bhVSnUbtWI8taCjd9lGiHExsRUpKf7Z1fXqi/IwYx4=
+sigs.k8s.io/cluster-api v1.6.0/go.mod h1:LB7u/WxiWj4/bbpHNOa1oQ8nq0MQ5iYlD0pGfRSBGLI=
+sigs.k8s.io/cluster-api/test v1.6.0 h1:hvqUpSYxXCvs4FiEfsDpFZAfZ7i4kkP/59mVdFHlzSI=
+sigs.k8s.io/cluster-api/test v1.6.0/go.mod h1:DJtbkrnrH77cd3PnXeKCQDMtCGVCrHZHPOjMvEsLB2U=
+sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4=
+sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0=
 sigs.k8s.io/gateway-api v0.5.0 h1:ze+k9fJqvmL8s1t3e4q1ST8RnN+f09dEv+gfacahlAE=
 sigs.k8s.io/gateway-api v0.5.0/go.mod h1:x0AP6gugkFV8fC/oTlnOMU0pnmuzIR8LfIPRVUjxSqA=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=