Repository: metal3-io/baremetal-operator
Pull request #2415 (2025-04-23 19:11:54)

:bug: Make firmware update handle only a subset of `Spec.Updates`
<!-- please add a icon to the title of this PR and delete this line and similar ones -->
<!-- the icon will be either ⚠️ (:warning:, major or breaking changes), ✨ (:sparkles:, feature additions), 🐛 (:bug:, patch and bugfixes), 📖 (:book:, documentation or proposals), or 🌱 (:seedling:, minor or other) -->

**What this PR does / why we need it**:
When an operator wants to update bare metal firmware, it must pass to `HostFirmwareComponents.Spec` a list of components and URIs for the update. In the old behavior, the controller will try to update the whole list, but it is desired to update only the firmware that was not updated. This PR fixes this passing to `TargetComponents` only the subset of unupdated components.


metal3-io-bot: Hi @winiciusallan. 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-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>

iurygregory: /ok-to-test
iurygregory: /lgtm 
metal3-io-bot: New changes are detected. LGTM label has been removed.

Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index e5851722..47c8a882 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -54,13 +54,13 @@ updates:
   - "ok-to-test"
 ## Main branch config ends here
 
-## release-0.10 branch config starts here
+## release-0.9 branch config starts here
 - package-ecosystem: "github-actions"
   directory: "/" # Location of package manifests
   schedule:
     interval: "monthly"
     day: "saturday"
-  target-branch: release-0.10
+  target-branch: release-0.9
   ## group all action bumps into single PR
   groups:
     github-actions:
@@ -84,7 +84,7 @@ updates:
   schedule:
     interval: "weekly"
     day: "sunday"
-  target-branch: release-0.10
+  target-branch: release-0.9
   groups:
     kubernetes:
       patterns: ["k8s.io/*"]
@@ -100,16 +100,15 @@ updates:
     prefix: ":seedling:"
   labels:
   - "ok-to-test"
+## release-0.9 branch config ends here
 
-## release-0.10 branch config ends here
-
-## release-0.9 branch config starts here
+## release-0.8 branch config starts here
 - package-ecosystem: "github-actions"
   directory: "/" # Location of package manifests
   schedule:
     interval: "monthly"
     day: "saturday"
-  target-branch: release-0.9
+  target-branch: release-0.8
   ## group all action bumps into single PR
   groups:
     github-actions:
@@ -133,7 +132,7 @@ updates:
   schedule:
     interval: "weekly"
     day: "sunday"
-  target-branch: release-0.9
+  target-branch: release-0.8
   groups:
     kubernetes:
       patterns: ["k8s.io/*"]
@@ -150,4 +149,4 @@ updates:
   labels:
   - "ok-to-test"
 
-## release-0.9 branch config ends here
+## release-0.8 branch config ends here
diff --git a/apis/go.mod b/apis/go.mod
index 029cf92f..f15e75fb 100644
--- a/apis/go.mod
+++ b/apis/go.mod
@@ -6,8 +6,8 @@ require (
 	github.com/google/uuid v1.6.0
 	github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.5.1
 	github.com/stretchr/testify v1.10.0
-	k8s.io/api v0.32.4
-	k8s.io/apimachinery v0.32.4
+	k8s.io/api v0.32.3
+	k8s.io/apimachinery v0.32.3
 	sigs.k8s.io/controller-runtime v0.20.4
 )
 
@@ -55,8 +55,8 @@ require (
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/apiextensions-apiserver v0.32.4 // indirect
-	k8s.io/client-go v0.32.4 // indirect
+	k8s.io/apiextensions-apiserver v0.32.3 // indirect
+	k8s.io/client-go v0.32.3 // indirect
 	k8s.io/klog/v2 v2.130.1 // indirect
 	k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
 	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
diff --git a/apis/go.sum b/apis/go.sum
index 3d37e8b7..908bc06e 100644
--- a/apis/go.sum
+++ b/apis/go.sum
@@ -166,14 +166,14 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.32.4 h1:kw8Y/G8E7EpNy7gjB8gJZl3KJkNz8HM2YHrZPtAZsF4=
-k8s.io/api v0.32.4/go.mod h1:5MYFvLvweRhyKylM3Es/6uh/5hGp0dg82vP34KifX4g=
-k8s.io/apiextensions-apiserver v0.32.4 h1:IA+CoR63UDOijR/vEpow6wQnX4V6iVpzazJBskHrpHE=
-k8s.io/apiextensions-apiserver v0.32.4/go.mod h1:Y06XO/b92H8ymOdG1HlA1submf7gIhbEDc3RjriqZOs=
-k8s.io/apimachinery v0.32.4 h1:8EEksaxA7nd7xWJkkwLDN4SvWS5ot9g6Z/VZb3ju25I=
-k8s.io/apimachinery v0.32.4/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
-k8s.io/client-go v0.32.4 h1:zaGJS7xoYOYumoWIFXlcVrsiYioRPrXGO7dBfVC5R6M=
-k8s.io/client-go v0.32.4/go.mod h1:k0jftcyYnEtwlFW92xC7MTtFv5BNcZBr+zn9jPlT9Ic=
+k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
+k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
+k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY=
+k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss=
+k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
+k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
+k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
 k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
diff --git a/go.mod b/go.mod
index 56a227ad..30ee0b70 100644
--- a/go.mod
+++ b/go.mod
@@ -15,10 +15,10 @@ require (
 	github.com/stretchr/testify v1.10.0
 	go.etcd.io/etcd/client/pkg/v3 v3.5.21
 	go.uber.org/zap v1.27.0
-	k8s.io/api v0.32.4
-	k8s.io/apimachinery v0.32.4
-	k8s.io/client-go v0.32.4
-	k8s.io/component-base v0.32.4
+	k8s.io/api v0.32.3
+	k8s.io/apimachinery v0.32.3
+	k8s.io/client-go v0.32.3
+	k8s.io/component-base v0.32.3
 	k8s.io/klog/v2 v2.130.1
 	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
 	sigs.k8s.io/controller-runtime v0.20.4
@@ -94,8 +94,8 @@ require (
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/apiextensions-apiserver v0.32.4 // indirect
-	k8s.io/apiserver v0.32.4 // indirect
+	k8s.io/apiextensions-apiserver v0.32.3 // indirect
+	k8s.io/apiserver v0.32.3 // indirect
 	k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
 	sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
 	sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
diff --git a/go.sum b/go.sum
index 6f3a99da..2983d410 100644
--- a/go.sum
+++ b/go.sum
@@ -227,18 +227,18 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.32.4 h1:kw8Y/G8E7EpNy7gjB8gJZl3KJkNz8HM2YHrZPtAZsF4=
-k8s.io/api v0.32.4/go.mod h1:5MYFvLvweRhyKylM3Es/6uh/5hGp0dg82vP34KifX4g=
-k8s.io/apiextensions-apiserver v0.32.4 h1:IA+CoR63UDOijR/vEpow6wQnX4V6iVpzazJBskHrpHE=
-k8s.io/apiextensions-apiserver v0.32.4/go.mod h1:Y06XO/b92H8ymOdG1HlA1submf7gIhbEDc3RjriqZOs=
-k8s.io/apimachinery v0.32.4 h1:8EEksaxA7nd7xWJkkwLDN4SvWS5ot9g6Z/VZb3ju25I=
-k8s.io/apimachinery v0.32.4/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
-k8s.io/apiserver v0.32.4 h1:Yf7sd/y+GOQKH1Qf6wUeayZrYXe2SKZ17Bcq7VQM5HQ=
-k8s.io/apiserver v0.32.4/go.mod h1:JFUMNtE2M5yqLZpIsgCb06SkVSW1YcxW1oyLSTfjXR8=
-k8s.io/client-go v0.32.4 h1:zaGJS7xoYOYumoWIFXlcVrsiYioRPrXGO7dBfVC5R6M=
-k8s.io/client-go v0.32.4/go.mod h1:k0jftcyYnEtwlFW92xC7MTtFv5BNcZBr+zn9jPlT9Ic=
-k8s.io/component-base v0.32.4 h1:HuF+2JVLbFS5GODLIfPCb1Td6b+G2HszJoArcWOSr5I=
-k8s.io/component-base v0.32.4/go.mod h1:10KloJEYw1keU/Xmjfy9TKJqUq7J2mYdiD1VDXoco4o=
+k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
+k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
+k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY=
+k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss=
+k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
+k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/apiserver v0.32.3 h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8=
+k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc=
+k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
+k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
+k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k=
+k8s.io/component-base v0.32.3/go.mod h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
 k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index ceb9edc4..72cca4f3 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -8,7 +8,7 @@ require (
 	github.com/pkg/errors v0.9.1
 	golang.org/x/crypto v0.37.0
 	golang.org/x/oauth2 v0.29.0
-	sigs.k8s.io/cluster-api/test v1.10.0
+	sigs.k8s.io/cluster-api/test v1.10.0-rc.0
 	sigs.k8s.io/controller-tools v0.16.5
 	sigs.k8s.io/kustomize/api v0.19.0
 	sigs.k8s.io/kustomize/kustomize/v5 v5.6.0
@@ -56,9 +56,9 @@ require (
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/api v0.32.4 // indirect
-	k8s.io/apiextensions-apiserver v0.32.4 // indirect
-	k8s.io/apimachinery v0.32.4 // indirect
+	k8s.io/api v0.32.3 // indirect
+	k8s.io/apiextensions-apiserver v0.32.3 // indirect
+	k8s.io/apimachinery v0.32.3 // indirect
 	k8s.io/klog/v2 v2.130.1 // indirect
 	k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
 	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index b73e3d3b..16352d7d 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -178,20 +178,20 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.32.4 h1:kw8Y/G8E7EpNy7gjB8gJZl3KJkNz8HM2YHrZPtAZsF4=
-k8s.io/api v0.32.4/go.mod h1:5MYFvLvweRhyKylM3Es/6uh/5hGp0dg82vP34KifX4g=
-k8s.io/apiextensions-apiserver v0.32.4 h1:IA+CoR63UDOijR/vEpow6wQnX4V6iVpzazJBskHrpHE=
-k8s.io/apiextensions-apiserver v0.32.4/go.mod h1:Y06XO/b92H8ymOdG1HlA1submf7gIhbEDc3RjriqZOs=
-k8s.io/apimachinery v0.32.4 h1:8EEksaxA7nd7xWJkkwLDN4SvWS5ot9g6Z/VZb3ju25I=
-k8s.io/apimachinery v0.32.4/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
+k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
+k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY=
+k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss=
+k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
+k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
 k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
 k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-sigs.k8s.io/cluster-api/test v1.10.0 h1:UAip3TgR/J+KV7ykJCAVP4C3UHMVoakl4hXd84kwVOI=
-sigs.k8s.io/cluster-api/test v1.10.0/go.mod h1:CYFajf4HhV5zhoiPl+m5Omk0a3WtnyXHtFVPqWIcUy4=
+sigs.k8s.io/cluster-api/test v1.10.0-rc.0 h1:OgpsT8KZMom0zsypVQ6nPQhlHXnhyMwUE5Bl7kgAkow=
+sigs.k8s.io/cluster-api/test v1.10.0-rc.0/go.mod h1:CYFajf4HhV5zhoiPl+m5Omk0a3WtnyXHtFVPqWIcUy4=
 sigs.k8s.io/controller-tools v0.16.5 h1:5k9FNRqziBPwqr17AMEPPV/En39ZBplLAdOwwQHruP4=
 sigs.k8s.io/controller-tools v0.16.5/go.mod h1:8vztuRVzs8IuuJqKqbXCSlXcw+lkAv/M2sTpg55qjMY=
 sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
diff --git a/internal/controller/metal3.io/baremetalhost_controller.go b/internal/controller/metal3.io/baremetalhost_controller.go
index 33310bf7..3ad6b7b5 100644
--- a/internal/controller/metal3.io/baremetalhost_controller.go
+++ b/internal/controller/metal3.io/baremetalhost_controller.go
@@ -1133,6 +1133,22 @@ func (r *BareMetalHostReconciler) matchProfile(info *reconcileInfo) (dirty bool,
 	return
 }
 
+func getUpdatesDifference(specUpdates []metal3api.FirmwareUpdate, statusUpdates []metal3api.FirmwareUpdate) []metal3api.FirmwareUpdate {
+	diff := []metal3api.FirmwareUpdate{}
+	// Mapping already updated components
+	updated := make(map[string]string, len(statusUpdates))
+	for _, s := range statusUpdates {
+		updated[s.Component] = s.URL
+	}
+
+	for _, firmware := range specUpdates {
+		if _, ok := updated[firmware.Component]; !ok || firmware.URL != updated[firmware.Component] {
+			diff = append(diff, firmware)
+		}
+	}
+	return diff
+}
+
 func (r *BareMetalHostReconciler) actionPreparing(prov provisioner.Provisioner, info *reconcileInfo) actionResult {
 	info.log.Info("preparing")
 
@@ -1178,7 +1194,12 @@ func (r *BareMetalHostReconciler) actionPreparing(prov provisioner.Provisioner,
 		return actionContinue{subResourceNotReadyRetryDelay}
 	}
 	if hfcDirty {
-		prepareData.TargetFirmwareComponents = hfc.Spec.Updates
+		// Handle only Firmware Component that it is in hfc.Spec.Updates but not in hfc.Status.Updates.
+		if hfc.Status.Updates != nil {
+			prepareData.TargetFirmwareComponents = getUpdatesDifference(hfc.Spec.Updates, hfc.Status.Updates)
+		} else {
+			prepareData.TargetFirmwareComponents = hfc.Spec.Updates
+		}
 	}
 
 	provResult, started, err := prov.Prepare(prepareData, bmhDirty || hfsDirty || hfcDirty,
@@ -1426,7 +1447,12 @@ func (r *BareMetalHostReconciler) doServiceIfNeeded(prov provisioner.Provisioner
 			return actionError{fmt.Errorf("could not determine firmware components: %w", err)}
 		}
 		if hfcDirty {
-			servicingData.TargetFirmwareComponents = hfc.Spec.Updates
+			// Handle only Firmware Component that it is in hfc.Spec.Updates but not in hfc.Status.Updates.
+			if hfc.Status.Updates != nil {
+				servicingData.TargetFirmwareComponents = getUpdatesDifference(hfc.Spec.Updates, hfc.Status.Updates)
+			} else {
+				servicingData.TargetFirmwareComponents = hfc.Spec.Updates
+			}
 		}
 	}
 
diff --git a/releasenotes/v0.10.0.md b/releasenotes/v0.10.0.md
deleted file mode 100644
index 9d9aad4f..00000000
--- a/releasenotes/v0.10.0.md
+++ /dev/null
@@ -1,114 +0,0 @@
-<!-- markdownlint-disable no-inline-html line-length -->
-# Changes since v0.9.0
-
-## :warning: Breaking Changes
-
-- Bump Kubernetes api, controller-runtime and decouple webhooks from api (#2390)
-- Only accept HTTPHeadersRef in same namespace [GHSA-c98h-7hp9-v9hq](https://github.com/metal3-io/baremetal-operator/security/advisories/GHSA-c98h-7hp9-v9hq)
-- Remove keepalived from BMO repository (#2228)
-
-## :sparkles: New Features
-
-- Use standard GetVirtualMedia API for checking dataImage status (#2336)
-- Add support for Disablepoweroff (#2229)
-- add support for locally built ironic in e2e test (#2167)
-
-## :bug: Bug Fixes
-
-- Fix few errors when making beta release notes (#2381)
-- Handle dataImage before reboot annotation to prevent annotation removal before all actions are complete (#2333)
-- fix broken release process (#2325)
-- Define some IPA (downloader) related variables (#2264)
-- bump x/net, x/crypto and x/oauth2 (#2316)
-- update rescue iso used for e2e tests (#2315)
-- Add BMH OwnerReference for DataImage right after its creation (#2249)
-- Set ownerReference for dataImage during BMH Reconcile (#2238)
-- Do not create HFC for non-redfish BMH (#2227)
-- Add validation against cross-namespace secret references (#2190)
-- Adding empty checksumType checks on BMH  to automatically detect hashing algorithm (#2174)
-- Fix BMO optional test by not checking metrics service (#2170)
-- Fix metrics service validation (#2165)
-
-## :seedling: Others
-
-- Bump CAPI to v1.10.0 (#2416)
-- bump golang to 1.23.6 (#2243)
-- Set e2e timeout to 1.5h (#2191)
-- Bump github.com/prometheus/client_golang from 1.21.1 to 1.22.0 (#2411)
-- Bump golang.org/x/crypto from 0.36.0 to 0.37.0 in /hack/tools (#2412)
-- Bump golang.org/x/crypto from 0.36.0 to 0.37.0 in /test (#2414)
-- Bump github.com/onsi/ginkgo/v2 from 2.23.3 to 2.23.4 in /test (#2413)
-- Refactoring: move ./controllers to ./internal/controller (#2405)
-- Makefile: do not explicit include ./internal/ (#2404)
-- Add Adil and Peppi-lotta as reviewers (#2407)
-- Bump the kubernetes group across 2 directories with 1 update (#2400)
-- Bump go.etcd.io/etcd/client/pkg/v3 from 3.5.19 to 3.5.21 (#2367)
-- Minor fix release workflow (#2403)
-- Enable intrange linter in golangci-lint (#2393)
-- Bump github.com/onsi/gomega from 1.36.3 to 1.37.0 in /test (#2388)
-- make osv-scanner scan run daily (#2389)
-- add labels to Dockerfile (#2384)
-- Enable testifylint linter in golangci-lint (#2383)
-- Bump golang.org/x/oauth2 from 0.27.0 to 0.29.0 in /hack/tools (#2387)
-- Bump github.com/onsi/gomega from 1.36.3 to 1.37.0 (#2386)
-- bump golang to v1.23.8 and x/net to v0.38.0 (#2374)
-- Enable usetesting linter in golangci-lint (#2380)
-- cleanup build/ and obsolete publishing-images.md (#2379)
-- Bump the github-actions group with 5 updates (#2378)
-- do not bump major version of github actions (#2373)
-- Minor logging improvements (#2370)
-- Bump sigs.k8s.io/kind from 0.26.0 to 0.27.0 in /test (#2327)
-- E2E: Avoid net-booting VMs unintentionally (#2266)
-- update the used linters list (#2350)
-- Bump golangci-lint to v1.64.7 (#2335)
-- update osv-scanner to v2.0.0 (#2339)
-- bump tj-actions/changed-files to v46.0.1 (#2331)
-- Reduce logging noise from status reconstruction (#2330)
-- Move @mquhuy to emeritus reviewers (#2329)
-- group all github action bumps into single PR (#2326)
-- Remove periodic upgrade tests from release branches (#2306)
-- Bump the kubernetes group to v0.31.7 (#2308)
-- Replace deprecated pkg/errors with stdlib in provisioner/ironic (#2303)
-- Bump golang.org/x/net from 0.34.0 to 0.36.0 in /hack/tools (#2305)
-- fix dependabot testing issues by re-adding "edited" (#2304)
-- Bump github.com/gophercloud/gophercloud/v2 from 2.5.0 to 2.6.0 (#2289)
-- bump local hack scripts to basic-checks:golang-1.23 (#2296)
-- E2E: Fix ironic overlays for 25.0 and 26.0 (#2295)
-- Bump sigs.k8s.io/controller-runtime from 0.19.6 to 0.19.7 (#2293)
-- E2E: Remove ensure_kind.sh script (#2277)
-- e2e: Revert temporary pinning of Ironic (#2113)
-- Bump actions/cache from 4.2.0 to 4.2.2 (#2272)
-- Bump actions/upload-artifact from 4.6.0 to 4.6.1 (#2271)
-- Bump github/codeql-action from 3.28.8 to 3.28.10 (#2270)
-- E2E: Bump cert-manager to v1.17.1 (#2268)
-- E2E vbmctl and ssh boot check refactor (#2223)
-- pin osv-scanner image in verify-release.sh (#2244)
-- workflows: sync pull_request trigger types (#2242)
-- Bump libvirt.org/go/libvirt from 1.10009.1 to 1.11001.0 in /test (#2237)
-- Bump actions/setup-go from 5.2.0 to 5.3.0 (#2231)
-- Bump softprops/action-gh-release from 2.2.0 to 2.2.1 (#2230)
-- Bump github.com/cert-manager/cert-manager from 1.16.2 to 1.16.3 in /test (#2236)
-- Fix ensure_kind.sh filename (#2221)
-- Refactor and rename ValidateManagementAccess (#2154)
-- Bump the k8s.io/* deps to v0.31.5 (#2203)
-- Switch e2e to kind (#2209)
-- Bump sigs.k8s.io/kustomize/api from 0.18.0 to 0.19.0 in /test (#2197)
-- Bump sigs.k8s.io/kustomize/kustomize/v5 from 5.5.0 to 5.6.0 in /hack/tools (#2196)
-- Take new release workflow into use (#2171)
-- bump x/net to v0.34.0 (#2185)
-- bump kubeconform to v0.6.7 (#2176)
-- increase 1 timeout in external-inspection test (#2166)
-- Add intervals while checking metrics service (#2161)
-- Bump google/osv-scanner from 1.9.1 to 1.9.2 (#2163)
-- Bump the capi group across 2 directories with 2 updates (#2156)
-- Fix BMO fixture-release-0.8 tag names (#2144)
-- update dependabot config for release-0.9 (#2135)
-- Add release 0.9 e2e tests and branch release-0.9 to README (#2134)
-
-## :recycle: Superseded or Reverted
-
-- #2274, #2211, #2397, #2391, #2239, #2240, #2182, #2184, #2232. #2138, #2140, #2143, #2142. #2139, #2150, #2164, #2342, #2343, #2157, #2155, #2259, #2148, #2310, #2169, #2149, #2181, #2214, #2261, #2193, #2212, #2292, #2269, #2233, #2235, #2234, #2275, #2290, #2282, #2258, #2406, #2224
-
-The image for this release is: v0.10.0
-
-_Thanks to all our contributors!_ 😊
diff --git a/releasenotes/v0.10.1.md b/releasenotes/v0.10.1.md
deleted file mode 100644
index 6914ac66..00000000
--- a/releasenotes/v0.10.1.md
+++ /dev/null
@@ -1,10 +0,0 @@
-<!-- markdownlint-disable no-inline-html line-length -->
-# Changes since v0.10.0
-
-## :bug: Bug Fixes
-
-- Pin ironic to version 29.0 (#2419)
-
-The image for this release is: v0.10.1
-
-_Thanks to all our contributors!_ 😊


Test patch:
diff --git a/internal/controller/metal3.io/baremetalhost_controller_test.go b/internal/controller/metal3.io/baremetalhost_controller_test.go
index 54885be8..2f07aece 100644
--- a/internal/controller/metal3.io/baremetalhost_controller_test.go
+++ b/internal/controller/metal3.io/baremetalhost_controller_test.go
@@ -2831,6 +2831,30 @@ func TestPreprovImageAvailable(t *testing.T) {
 	}
 }
 
+func TestGetUpdatesDifference(t *testing.T) {
+	hfc := metal3api.HostFirmwareComponents{
+		Status: metal3api.HostFirmwareComponentsStatus{
+			Updates: []metal3api.FirmwareUpdate{
+				{Component: "bmc", URL: "http://example1.com/bmc.exe"},
+				{Component: "bios", URL: "http://example1.com/bios.exe"},
+			},
+		},
+		Spec: metal3api.HostFirmwareComponentsSpec{
+			Updates: []metal3api.FirmwareUpdate{
+				{Component: "bmc", URL: "http://example1.com/bmc.exe"},
+				{Component: "bios", URL: "http://example1.com/bios.exe"},
+				{Component: "bios", URL: "http://example2.com/bios.exe"}},
+		},
+	}
+
+	expected := []metal3api.FirmwareUpdate{
+		{Component: "bios", URL: "http://example2.com/bios.exe"},
+	}
+	diff := getUpdatesDifference(hfc.Spec.Updates, hfc.Status.Updates)
+
+	assert.Equal(t, expected, diff)
+}
+
 // TestHostFirwmareSettings verifies that a change to the HFS
 // can be detected as it will be used to set state to Preparing.
 func TestHostFirmwareSettings(t *testing.T) {
diff --git a/test/go.mod b/test/go.mod
index 9b045674..f725b67b 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -12,14 +12,14 @@ require (
 	github.com/pkg/errors v0.9.1
 	golang.org/x/crypto v0.37.0
 	gopkg.in/yaml.v2 v2.4.0
-	k8s.io/api v0.32.4
-	k8s.io/apiextensions-apiserver v0.32.4
-	k8s.io/apimachinery v0.32.4
+	k8s.io/api v0.32.3
+	k8s.io/apiextensions-apiserver v0.32.3
+	k8s.io/apimachinery v0.32.3
 	k8s.io/klog/v2 v2.130.1
 	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
 	libvirt.org/go/libvirt v1.11001.0
-	sigs.k8s.io/cluster-api v1.10.0
-	sigs.k8s.io/cluster-api/test v1.10.0
+	sigs.k8s.io/cluster-api v1.10.0-rc.0
+	sigs.k8s.io/cluster-api/test v1.10.0-rc.0
 	sigs.k8s.io/controller-runtime v0.20.4
 	sigs.k8s.io/kind v0.27.0
 	sigs.k8s.io/kustomize/api v0.19.0
@@ -148,10 +148,10 @@ require (
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gotest.tools/v3 v3.5.0 // indirect
-	k8s.io/apiserver v0.32.4 // indirect
-	k8s.io/client-go v0.32.4 // indirect
+	k8s.io/apiserver v0.32.3 // indirect
+	k8s.io/client-go v0.32.3 // indirect
 	k8s.io/cluster-bootstrap v0.32.3 // indirect
-	k8s.io/component-base v0.32.4 // indirect
+	k8s.io/component-base v0.32.3 // indirect
 	k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
 	sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
 	sigs.k8s.io/gateway-api v1.1.0 // indirect
diff --git a/test/go.sum b/test/go.sum
index 1c2c823a..b8336344 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -378,20 +378,20 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
 gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
-k8s.io/api v0.32.4 h1:kw8Y/G8E7EpNy7gjB8gJZl3KJkNz8HM2YHrZPtAZsF4=
-k8s.io/api v0.32.4/go.mod h1:5MYFvLvweRhyKylM3Es/6uh/5hGp0dg82vP34KifX4g=
-k8s.io/apiextensions-apiserver v0.32.4 h1:IA+CoR63UDOijR/vEpow6wQnX4V6iVpzazJBskHrpHE=
-k8s.io/apiextensions-apiserver v0.32.4/go.mod h1:Y06XO/b92H8ymOdG1HlA1submf7gIhbEDc3RjriqZOs=
-k8s.io/apimachinery v0.32.4 h1:8EEksaxA7nd7xWJkkwLDN4SvWS5ot9g6Z/VZb3ju25I=
-k8s.io/apimachinery v0.32.4/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
-k8s.io/apiserver v0.32.4 h1:Yf7sd/y+GOQKH1Qf6wUeayZrYXe2SKZ17Bcq7VQM5HQ=
-k8s.io/apiserver v0.32.4/go.mod h1:JFUMNtE2M5yqLZpIsgCb06SkVSW1YcxW1oyLSTfjXR8=
-k8s.io/client-go v0.32.4 h1:zaGJS7xoYOYumoWIFXlcVrsiYioRPrXGO7dBfVC5R6M=
-k8s.io/client-go v0.32.4/go.mod h1:k0jftcyYnEtwlFW92xC7MTtFv5BNcZBr+zn9jPlT9Ic=
+k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
+k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
+k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY=
+k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss=
+k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
+k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/apiserver v0.32.3 h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8=
+k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc=
+k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
+k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
 k8s.io/cluster-bootstrap v0.32.3 h1:AqIpsUhB6MUeaAsl1WvaUw54AHRd2hfZrESlKChtd8s=
 k8s.io/cluster-bootstrap v0.32.3/go.mod h1:CHbBwgOb6liDV6JFUTkx5t85T2xidy0sChBDoyYw344=
-k8s.io/component-base v0.32.4 h1:HuF+2JVLbFS5GODLIfPCb1Td6b+G2HszJoArcWOSr5I=
-k8s.io/component-base v0.32.4/go.mod h1:10KloJEYw1keU/Xmjfy9TKJqUq7J2mYdiD1VDXoco4o=
+k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k=
+k8s.io/component-base v0.32.3/go.mod h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
 k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
@@ -402,10 +402,10 @@ libvirt.org/go/libvirt v1.11001.0 h1:QJgpslxY7qkpXZIDxdMHpkDl7FfhgQJwqRTGBbg/S8E
 libvirt.org/go/libvirt v1.11001.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
-sigs.k8s.io/cluster-api v1.10.0 h1:Jcmr8aEDI4/MCqNmvjj9YQlW7r4qTUpTnL6T9UUrVuQ=
-sigs.k8s.io/cluster-api v1.10.0/go.mod h1:aiPMrNPoaJc/GuJ4TCpWX8bVe11+iCJ4HI0f3c9QiJg=
-sigs.k8s.io/cluster-api/test v1.10.0 h1:UAip3TgR/J+KV7ykJCAVP4C3UHMVoakl4hXd84kwVOI=
-sigs.k8s.io/cluster-api/test v1.10.0/go.mod h1:CYFajf4HhV5zhoiPl+m5Omk0a3WtnyXHtFVPqWIcUy4=
+sigs.k8s.io/cluster-api v1.10.0-rc.0 h1:D+89GG2aqx9Jdor+8p5d5AdosA4iMcBbtZaETdHLmH4=
+sigs.k8s.io/cluster-api v1.10.0-rc.0/go.mod h1:aiPMrNPoaJc/GuJ4TCpWX8bVe11+iCJ4HI0f3c9QiJg=
+sigs.k8s.io/cluster-api/test v1.10.0-rc.0 h1:OgpsT8KZMom0zsypVQ6nPQhlHXnhyMwUE5Bl7kgAkow=
+sigs.k8s.io/cluster-api/test v1.10.0-rc.0/go.mod h1:CYFajf4HhV5zhoiPl+m5Omk0a3WtnyXHtFVPqWIcUy4=
 sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU=
 sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY=
 sigs.k8s.io/gateway-api v1.1.0 h1:DsLDXCi6jR+Xz8/xd0Z1PYl2Pn0TyaFMOPPZIj4inDM=