From a81f0c22f38e1058ccad185e1a6d68eabe1b812a Mon Sep 17 00:00:00 2001 From: Honza Pokorny Date: Thu, 12 Mar 2026 10:09:13 -0300 Subject: [PATCH 1/9] baremetal: fix unreachable code in skipIfUnsupportedPlatformOrConfig The empty string check for provisioningNetwork was unreachable because an empty string already satisfies the `!= "Disabled"` condition in the preceding branch. This caused missing Provisioning CRs to skip with the misleading "Unsupported config" message instead of reporting the actual problem. Check for empty string first. Co-Authored-By: Claude Opus 4.6 --- test/extended/baremetal/common.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/extended/baremetal/common.go b/test/extended/baremetal/common.go index 94d98d5ab4b3..647957923d7e 100644 --- a/test/extended/baremetal/common.go +++ b/test/extended/baremetal/common.go @@ -53,12 +53,10 @@ func skipIfUnsupportedPlatformOrConfig(oc *exutil.CLI, dc dynamic.Interface) { fallthrough case configv1.NonePlatformType: provisioningNetwork := getProvisioningNetwork(dc) - if provisioningNetwork != "Disabled" { - e2eskipper.Skipf("Unsupported config in supported platform detected") - } else if provisioningNetwork == "" { + if provisioningNetwork == "" { e2eskipper.Skipf("Unable to read ProvisioningNetwork from Provisioning CR") - } else { - return + } else if provisioningNetwork != "Disabled" { + e2eskipper.Skipf("Unsupported config in supported platform detected") } default: e2eskipper.Skipf("No supported platform detected") From 8b4d47dc414ea4d2251ac956a1134562f8b5a24c Mon Sep 17 00:00:00 2001 From: Honza Pokorny Date: Thu, 12 Mar 2026 10:09:33 -0300 Subject: [PATCH 2/9] baremetal: reenable HostFirmwareSettings status.settings check This assertion was disabled over 3 years ago waiting for a BMO fix to prevent HostFirmwareSettings with 0 entries. That fix has long since shipped. Reenable the check. Co-Authored-By: Claude Opus 4.6 --- test/extended/baremetal/hosts.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/extended/baremetal/hosts.go b/test/extended/baremetal/hosts.go index 92c908dae54d..5f8764059869 100644 --- a/test/extended/baremetal/hosts.go +++ b/test/extended/baremetal/hosts.go @@ -106,9 +106,8 @@ var _ = g.Describe("[sig-installer][Feature:baremetal] Baremetal platform should o.Expect(err).NotTo(o.HaveOccurred()) o.Expect(hfs).NotTo(o.Equal(nil)) - // Reenable this when fix to prevent settings with 0 entries is in BMO - // g.By("check that hostfirmwaresettings settings have been populated") - // expectStringMapField(*hfs, "hostfirmwaresettings", "status.settings").ToNot(o.BeEmpty()) + g.By("check that hostfirmwaresettings settings have been populated") + expectStringMapField(*hfs, "hostfirmwaresettings", "status.settings").ToNot(o.BeEmpty()) g.By("check that hostfirmwaresettings conditions show resource is valid") checkConditionStatus(*hfs, "Valid", "True") From 7a60ca686f64ae1084cdca25d1b9c8617623ec08 Mon Sep 17 00:00:00 2001 From: Honza Pokorny Date: Thu, 12 Mar 2026 10:09:52 -0300 Subject: [PATCH 3/9] baremetal: add Provisioning CR validation test The Provisioning CR (provisioning-configuration) is the key resource managed by cluster-baremetal-operator, yet it had zero test coverage. Add a test that verifies it exists and has a provisioningNetwork spec field. Co-Authored-By: Claude Opus 4.6 fix --- test/extended/baremetal/hosts.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/extended/baremetal/hosts.go b/test/extended/baremetal/hosts.go index 5f8764059869..7d40135b0baa 100644 --- a/test/extended/baremetal/hosts.go +++ b/test/extended/baremetal/hosts.go @@ -150,6 +150,20 @@ var _ = g.Describe("[sig-installer][Feature:baremetal] Baremetal platform should check, _, _ := unstructured.NestedString(h.Object, "spec", "bootMACAddress") o.Expect(check).To(o.Equal(bootMACAddress)) }) + + g.It("have a valid provisioning configuration", func() { + dc := oc.AdminDynamicClient() + provisioningClient := dc.Resource(schema.GroupVersionResource{Group: "metal3.io", Resource: "provisionings", Version: "v1alpha1"}) + + provisioning, err := provisioningClient.Get(context.Background(), "provisioning-configuration", metav1.GetOptions{}) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("checking provisioning network is set") + provisioningNetwork, found, err := unstructured.NestedString(provisioning.Object, "spec", "provisioningNetwork") + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(found).To(o.BeTrue(), "spec.provisioningNetwork not found") + o.Expect(provisioningNetwork).ToNot(o.BeEmpty()) + }) }) // This block must be used for the serial tests. Any eventual extra worker deployed will be From e832b9868d409b6631504762d8c5ae8a2d09ec03 Mon Sep 17 00:00:00 2001 From: Honza Pokorny Date: Thu, 12 Mar 2026 10:10:22 -0300 Subject: [PATCH 4/9] baremetal: add metal3 pod container health test The existing test only checks that the metal3 deployment has 1 available replica. Add a test that verifies every container in the metal3 pod is Ready and hasn't been crash-looping (< 5 restarts). This catches issues with individual containers like ironic that the deployment-level check misses. Co-Authored-By: Claude Opus 4.6 --- test/extended/baremetal/hosts.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/extended/baremetal/hosts.go b/test/extended/baremetal/hosts.go index 7d40135b0baa..155415dcaa32 100644 --- a/test/extended/baremetal/hosts.go +++ b/test/extended/baremetal/hosts.go @@ -164,6 +164,25 @@ var _ = g.Describe("[sig-installer][Feature:baremetal] Baremetal platform should o.Expect(found).To(o.BeTrue(), "spec.provisioningNetwork not found") o.Expect(provisioningNetwork).ToNot(o.BeEmpty()) }) + + g.It("have all metal3 pod containers running", func() { + c, err := e2e.LoadClientset() + o.Expect(err).ToNot(o.HaveOccurred()) + + pods, err := c.CoreV1().Pods("openshift-machine-api").List(context.Background(), metav1.ListOptions{ + LabelSelector: "baremetal.openshift.io/cluster-baremetal-operator=metal3-state", + }) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(pods.Items).ToNot(o.BeEmpty()) + + for _, pod := range pods.Items { + g.By(fmt.Sprintf("checking containers in pod %s", pod.Name)) + for _, cs := range pod.Status.ContainerStatuses { + o.Expect(cs.Ready).To(o.BeTrue(), fmt.Sprintf("container %s in pod %s is not ready", cs.Name, pod.Name)) + o.Expect(cs.RestartCount).To(o.BeNumerically("<", 5), fmt.Sprintf("container %s in pod %s has restarted %d times", cs.Name, pod.Name, cs.RestartCount)) + } + } + }) }) // This block must be used for the serial tests. Any eventual extra worker deployed will be From 654b07e69cdf241c0151b3498d5f201177fb1e56 Mon Sep 17 00:00:00 2001 From: Honza Pokorny Date: Fri, 13 Mar 2026 10:44:46 -0300 Subject: [PATCH 5/9] baremetal: add metal3-image-customization deployment test The image-customization-controller is deployed by CBO on baremetal platform as the metal3-image-customization deployment but had zero test coverage. Add a test that verifies the deployment exists, has 1 available replica, and carries the expected CBO ownership annotation and label. Co-Authored-By: Claude Opus 4.6 --- test/extended/baremetal/hosts.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/extended/baremetal/hosts.go b/test/extended/baremetal/hosts.go index 155415dcaa32..e91a489e9f86 100644 --- a/test/extended/baremetal/hosts.go +++ b/test/extended/baremetal/hosts.go @@ -183,6 +183,18 @@ var _ = g.Describe("[sig-installer][Feature:baremetal] Baremetal platform should } } }) + + g.It("have a metal3-image-customization deployment", func() { + c, err := e2e.LoadClientset() + o.Expect(err).ToNot(o.HaveOccurred()) + + icc, err := c.AppsV1().Deployments("openshift-machine-api").Get(context.Background(), "metal3-image-customization", metav1.GetOptions{}) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(icc.Status.AvailableReplicas).To(o.BeEquivalentTo(1)) + + o.Expect(icc.Annotations).Should(o.HaveKey("baremetal.openshift.io/owned")) + o.Expect(icc.Labels).Should(o.HaveKeyWithValue("baremetal.openshift.io/cluster-baremetal-operator", "metal3-image-customization-service")) + }) }) // This block must be used for the serial tests. Any eventual extra worker deployed will be From cbdad1e70931cd17789d37c4635cba4cd5c7624e Mon Sep 17 00:00:00 2001 From: Honza Pokorny Date: Thu, 12 Mar 2026 11:37:26 -0300 Subject: [PATCH 6/9] baremetal: update OWNERS --- test/extended/baremetal/OWNERS | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/extended/baremetal/OWNERS b/test/extended/baremetal/OWNERS index bda393e5c2e5..c9cdfda9cd62 100644 --- a/test/extended/baremetal/OWNERS +++ b/test/extended/baremetal/OWNERS @@ -1,5 +1,10 @@ approvers: - andfasano - - ardaguclu - - bfournie + - dtantsur - elfosardo + - honza + - hroyrh + - iurygregory + - jacob-anders + - MahnoorAsghar + - tdomnesc From b409e4f4774d58f933b5dac0d0198c4253f6b7f3 Mon Sep 17 00:00:00 2001 From: Honza Pokorny Date: Thu, 12 Mar 2026 11:42:07 -0300 Subject: [PATCH 7/9] baremetal: add [apigroup:metal3.io] annotations to test suites All baremetal test suites use metal3.io API resources but were missing the [apigroup:metal3.io] annotation needed for proper CI filtering. Also add [apigroup:config.openshift.io] to the baremetal-only suite which queries the Infrastructure resource. Co-Authored-By: Claude Opus 4.6 --- test/extended/baremetal/high_availability.go | 2 +- test/extended/baremetal/hosts.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/extended/baremetal/high_availability.go b/test/extended/baremetal/high_availability.go index c7a47126f5b0..df1ea37b7f87 100644 --- a/test/extended/baremetal/high_availability.go +++ b/test/extended/baremetal/high_availability.go @@ -29,7 +29,7 @@ const ( metal3Deployment = "metal3" ) -var _ = g.Describe("[sig-installer][Feature:baremetal][Serial] Baremetal platform should ensure [apigroup:config.openshift.io]", func() { +var _ = g.Describe("[sig-installer][Feature:baremetal][Serial][apigroup:metal3.io][apigroup:config.openshift.io] Baremetal platform should ensure", func() { defer g.GinkgoRecover() var ( diff --git a/test/extended/baremetal/hosts.go b/test/extended/baremetal/hosts.go index e91a489e9f86..85529017af8c 100644 --- a/test/extended/baremetal/hosts.go +++ b/test/extended/baremetal/hosts.go @@ -14,7 +14,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -var _ = g.Describe("[sig-installer][Feature:baremetal] Baremetal/OpenStack/vSphere/None/AWS/Azure/GCP platforms", func() { +var _ = g.Describe("[sig-installer][Feature:baremetal][apigroup:metal3.io] Baremetal/OpenStack/vSphere/None/AWS/Azure/GCP platforms", func() { defer g.GinkgoRecover() var ( @@ -37,7 +37,7 @@ var _ = g.Describe("[sig-installer][Feature:baremetal] Baremetal/OpenStack/vSphe }) }) -var _ = g.Describe("[sig-installer][Feature:baremetal] Baremetal platform should", func() { +var _ = g.Describe("[sig-installer][Feature:baremetal][apigroup:metal3.io][apigroup:config.openshift.io] Baremetal platform should", func() { defer g.GinkgoRecover() oc := exutil.NewCLI("baremetal") @@ -199,7 +199,7 @@ var _ = g.Describe("[sig-installer][Feature:baremetal] Baremetal platform should // This block must be used for the serial tests. Any eventual extra worker deployed will be // automatically deleted during the AfterEach -var _ = g.Describe("[sig-installer][Feature:baremetal][Serial] Baremetal platform should", func() { +var _ = g.Describe("[sig-installer][Feature:baremetal][Serial][apigroup:metal3.io] Baremetal platform should", func() { defer g.GinkgoRecover() var ( From b19154114012fdedc99a5d24c89da8e64f6ba965 Mon Sep 17 00:00:00 2001 From: Honza Pokorny Date: Thu, 12 Mar 2026 11:47:42 -0300 Subject: [PATCH 8/9] baremetal: use typed metal3 structs instead of unstructured field access Replace manual unstructured field access (expectStringField, getStringField, expectBoolField, etc.) with typed metal3.io/v1alpha1 structs using runtime.DefaultUnstructuredConverter. This catches API drift at compile time and makes the tests much more readable. The dynamic client is still used for API calls since there is no generated clientset for metal3.io, but results are now converted to BareMetalHost and HostFirmwareSettings typed structs for assertions. The Provisioning CR and BootMacAddress update tests retain unstructured access where needed (Provisioning has no vendored type, and the update test requires manipulating raw unstructured objects). Co-Authored-By: Claude Opus 4.6 --- test/extended/baremetal/common.go | 78 ++++---------------- test/extended/baremetal/high_availability.go | 11 ++- test/extended/baremetal/hosts.go | 72 +++++++++--------- 3 files changed, 62 insertions(+), 99 deletions(-) diff --git a/test/extended/baremetal/common.go b/test/extended/baremetal/common.go index 647957923d7e..fff91fe9439d 100644 --- a/test/extended/baremetal/common.go +++ b/test/extended/baremetal/common.go @@ -2,15 +2,15 @@ package baremetal import ( "context" - "fmt" - "strings" + metal3v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" g "github.com/onsi/ginkgo/v2" o "github.com/onsi/gomega" configv1 "github.com/openshift/api/config/v1" exutil "github.com/openshift/origin/test/extended/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" @@ -96,73 +96,25 @@ func preprovisioningImagesClient(dc dynamic.Interface) dynamic.ResourceInterface return ppiClient.Namespace("openshift-machine-api") } -type FieldGetterFunc func(obj map[string]interface{}, fields ...string) (interface{}, bool, error) - -func expectField(object unstructured.Unstructured, resource string, nestedField string, fieldGetter FieldGetterFunc) o.Assertion { - fields := strings.Split(nestedField, ".") - - value, found, err := fieldGetter(object.Object, fields...) - o.Expect(err).NotTo(o.HaveOccurred()) - o.Expect(found).To(o.BeTrue(), fmt.Sprintf("`%s` field `%s` not found", resource, nestedField)) - return o.Expect(value) -} - -func expectStringField(object unstructured.Unstructured, resource string, nestedField string) o.Assertion { - return expectField(object, resource, nestedField, func(obj map[string]interface{}, fields ...string) (interface{}, bool, error) { - return unstructured.NestedString(obj, fields...) - }) -} - -func expectBoolField(object unstructured.Unstructured, resource string, nestedField string) o.Assertion { - return expectField(object, resource, nestedField, func(obj map[string]interface{}, fields ...string) (interface{}, bool, error) { - return unstructured.NestedBool(obj, fields...) - }) +func firmwareSchemaClient(dc dynamic.Interface, namespace string) dynamic.ResourceInterface { + fsClient := dc.Resource(schema.GroupVersionResource{Group: "metal3.io", Resource: "firmwareschemas", Version: "v1alpha1"}) + return fsClient.Namespace(namespace) } -func expectStringMapField(object unstructured.Unstructured, resource string, nestedField string) o.Assertion { - return expectField(object, resource, nestedField, func(obj map[string]interface{}, fields ...string) (interface{}, bool, error) { - return unstructured.NestedStringMap(obj, fields...) - }) +func provisioningGVR() schema.GroupVersionResource { + return schema.GroupVersionResource{Group: "metal3.io", Resource: "provisionings", Version: "v1alpha1"} } -func expectSliceField(object unstructured.Unstructured, resource string, nestedField string) o.Assertion { - return expectField(object, resource, nestedField, func(obj map[string]interface{}, fields ...string) (interface{}, bool, error) { - return unstructured.NestedSlice(obj, fields...) - }) -} - -// Conditions are stored as a slice of maps, check that the type has the correct status -func checkConditionStatus(hfs unstructured.Unstructured, condType string, condStatus string) { - - conditions, _, err := unstructured.NestedSlice(hfs.Object, "status", "conditions") +func toBMH(obj unstructured.Unstructured) metal3v1alpha1.BareMetalHost { + var bmh metal3v1alpha1.BareMetalHost + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &bmh) o.Expect(err).NotTo(o.HaveOccurred()) - o.Expect(conditions).ToNot(o.BeEmpty()) - - for _, c := range conditions { - condition, ok := c.(map[string]interface{}) - o.Expect(ok).To(o.BeTrue()) - - t, ok := condition["type"] - o.Expect(ok).To(o.BeTrue()) - if t == condType { - s, ok := condition["status"] - o.Expect(ok).To(o.BeTrue()) - o.Expect(s).To(o.Equal(condStatus)) - } - } + return bmh } -func getField(object unstructured.Unstructured, resource string, nestedField string, fieldGetter FieldGetterFunc) string { - fields := strings.Split(nestedField, ".") - - value, found, err := fieldGetter(object.Object, fields...) +func toHFS(obj unstructured.Unstructured) metal3v1alpha1.HostFirmwareSettings { + var hfs metal3v1alpha1.HostFirmwareSettings + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &hfs) o.Expect(err).NotTo(o.HaveOccurred()) - o.Expect(found).To(o.BeTrue(), fmt.Sprintf("`%s` field `%s` not found", resource, nestedField)) - return value.(string) -} - -func getStringField(object unstructured.Unstructured, resource string, nestedField string) string { - return getField(object, resource, nestedField, func(obj map[string]interface{}, fields ...string) (interface{}, bool, error) { - return unstructured.NestedFieldNoCopy(obj, fields...) - }) + return hfs } diff --git a/test/extended/baremetal/high_availability.go b/test/extended/baremetal/high_availability.go index df1ea37b7f87..87266fe45951 100644 --- a/test/extended/baremetal/high_availability.go +++ b/test/extended/baremetal/high_availability.go @@ -4,6 +4,7 @@ import ( "context" "time" + metal3v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" configv1 "github.com/openshift/api/config/v1" clusteroperatorhelpers "github.com/openshift/library-go/pkg/config/clusteroperator/v1helpers" @@ -155,8 +156,12 @@ func checkMetal3DeploymentHealthy(oc *exutil.CLI) { o.Expect(hosts.Items).ToNot(o.BeEmpty()) for _, h := range hosts.Items { - expectStringField(h, "baremetalhost", "status.operationalStatus").To(o.BeEquivalentTo("OK")) - expectStringField(h, "baremetalhost", "status.provisioning.state").To(o.Or(o.BeEquivalentTo("provisioned"), o.BeEquivalentTo("externally provisioned"))) - expectBoolField(h, "baremetalhost", "spec.online").To(o.BeTrue()) + bmh := toBMH(h) + o.Expect(bmh.Status.OperationalStatus).To(o.Equal(metal3v1alpha1.OperationalStatusOK), "host %s", bmh.Name) + o.Expect(bmh.Status.Provisioning.State).To(o.Or( + o.Equal(metal3v1alpha1.StateProvisioned), + o.Equal(metal3v1alpha1.StateExternallyProvisioned), + ), "host %s", bmh.Name) + o.Expect(bmh.Spec.Online).To(o.BeTrue(), "host %s", bmh.Name) } } diff --git a/test/extended/baremetal/hosts.go b/test/extended/baremetal/hosts.go index 85529017af8c..1df545a3da00 100644 --- a/test/extended/baremetal/hosts.go +++ b/test/extended/baremetal/hosts.go @@ -4,10 +4,10 @@ import ( "context" "fmt" + metal3v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" g "github.com/onsi/ginkgo/v2" o "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" e2e "k8s.io/kubernetes/test/e2e/framework" exutil "github.com/openshift/origin/test/extended/util" @@ -52,10 +52,9 @@ var _ = g.Describe("[sig-installer][Feature:baremetal][apigroup:metal3.io][apigr dc := oc.AdminDynamicClient() bmc := baremetalClient(dc) - // In TNF Deployments we expect baremetal installs to be detached. - expectedOperationalStatus := "OK" + expectedOperationalStatus := metal3v1alpha1.OperationalStatusOK if isTNFDeployment { - expectedOperationalStatus = "detached" + expectedOperationalStatus = metal3v1alpha1.OperationalStatusDetached } hosts, err := bmc.List(context.Background(), metav1.ListOptions{}) @@ -63,10 +62,13 @@ var _ = g.Describe("[sig-installer][Feature:baremetal][apigroup:metal3.io][apigr o.Expect(hosts.Items).ToNot(o.BeEmpty()) for _, h := range hosts.Items { - expectStringField(h, "baremetalhost", "status.operationalStatus").To(o.BeEquivalentTo(expectedOperationalStatus)) - expectStringField(h, "baremetalhost", "status.provisioning.state").To(o.Or(o.BeEquivalentTo("provisioned"), o.BeEquivalentTo("externally provisioned"))) - expectBoolField(h, "baremetalhost", "spec.online").To(o.BeTrue()) - + bmh := toBMH(h) + o.Expect(bmh.Status.OperationalStatus).To(o.Equal(expectedOperationalStatus), "host %s", bmh.Name) + o.Expect(bmh.Status.Provisioning.State).To(o.Or( + o.Equal(metal3v1alpha1.StateProvisioned), + o.Equal(metal3v1alpha1.StateExternallyProvisioned), + ), "host %s", bmh.Name) + o.Expect(bmh.Spec.Online).To(o.BeTrue(), "host %s", bmh.Name) } }) @@ -79,11 +81,10 @@ var _ = g.Describe("[sig-installer][Feature:baremetal][apigroup:metal3.io][apigr o.Expect(err).NotTo(o.HaveOccurred()) for _, h := range hosts.Items { - state := getStringField(h, "baremetalhost", "status.provisioning.state") - if state != "externally provisioned" { - hostName := getStringField(h, "baremetalhost", "metadata.name") - _, err := ppiClient.Get(context.Background(), hostName, metav1.GetOptions{}) - o.Expect(err).NotTo(o.HaveOccurred()) + bmh := toBMH(h) + if bmh.Status.Provisioning.State != metal3v1alpha1.StateExternallyProvisioned { + _, err := ppiClient.Get(context.Background(), bmh.Name, metav1.GetOptions{}) + o.Expect(err).NotTo(o.HaveOccurred(), "missing PreprovisioningImage for host %s", bmh.Name) } } }) @@ -99,27 +100,33 @@ var _ = g.Describe("[sig-installer][Feature:baremetal][apigroup:metal3.io][apigr hfsClient := hostfirmwaresettingsClient(dc) for _, h := range hosts.Items { - hostName := getStringField(h, "baremetalhost", "metadata.name") + bmh := toBMH(h) - g.By(fmt.Sprintf("check that baremetalhost %s has a corresponding hostfirmwaresettings", hostName)) - hfs, err := hfsClient.Get(context.Background(), hostName, metav1.GetOptions{}) + g.By(fmt.Sprintf("check that baremetalhost %s has a corresponding hostfirmwaresettings", bmh.Name)) + hfsUnstructured, err := hfsClient.Get(context.Background(), bmh.Name, metav1.GetOptions{}) o.Expect(err).NotTo(o.HaveOccurred()) - o.Expect(hfs).NotTo(o.Equal(nil)) + + hfs := toHFS(*hfsUnstructured) g.By("check that hostfirmwaresettings settings have been populated") - expectStringMapField(*hfs, "hostfirmwaresettings", "status.settings").ToNot(o.BeEmpty()) + o.Expect(hfs.Status.Settings).ToNot(o.BeEmpty()) g.By("check that hostfirmwaresettings conditions show resource is valid") - checkConditionStatus(*hfs, "Valid", "True") + o.Expect(hfs.Status.Conditions).ToNot(o.BeEmpty()) + for _, cond := range hfs.Status.Conditions { + if cond.Type == string(metal3v1alpha1.FirmwareSettingsValid) { + o.Expect(cond.Status).To(o.Equal(metav1.ConditionTrue)) + } + } g.By("check that hostfirmwaresettings reference a schema") - refName := getStringField(*hfs, "hostfirmwaresettings", "status.schema.name") - refNS := getStringField(*hfs, "hostfirmwaresettings", "status.schema.namespace") + o.Expect(hfs.Status.FirmwareSchema).ToNot(o.BeNil()) + o.Expect(hfs.Status.FirmwareSchema.Name).ToNot(o.BeEmpty()) - schemaClient := dc.Resource(schema.GroupVersionResource{Group: "metal3.io", Resource: "firmwareschemas", Version: "v1alpha1"}).Namespace(refNS) - schema, err := schemaClient.Get(context.Background(), refName, metav1.GetOptions{}) + fsClient := firmwareSchemaClient(dc, hfs.Status.FirmwareSchema.Namespace) + fs, err := fsClient.Get(context.Background(), hfs.Status.FirmwareSchema.Name, metav1.GetOptions{}) o.Expect(err).NotTo(o.HaveOccurred()) - o.Expect(schema).NotTo(o.Equal(nil)) + o.Expect(fs).NotTo(o.BeNil()) } }) @@ -132,9 +139,9 @@ var _ = g.Describe("[sig-installer][Feature:baremetal][apigroup:metal3.io][apigr o.Expect(hosts.Items).ToNot(o.BeEmpty()) host := hosts.Items[0] - expectStringField(host, "baremetalhost", "spec.bootMACAddress").ShouldNot(o.BeNil()) - // Already verified that bootMACAddress exists - bootMACAddress, _, _ := unstructured.NestedString(host.Object, "spec", "bootMACAddress") + bmh := toBMH(host) + o.Expect(bmh.Spec.BootMACAddress).ToNot(o.BeEmpty()) + testMACAddress := "11:11:11:11:11:11" g.By("updating bootMACAddress which is not allowed") @@ -147,13 +154,13 @@ var _ = g.Describe("[sig-installer][Feature:baremetal][apigroup:metal3.io][apigr g.By("verify bootMACAddress is not updated") h, err := bmc.Get(context.Background(), host.GetName(), metav1.GetOptions{}) o.Expect(err).NotTo(o.HaveOccurred()) - check, _, _ := unstructured.NestedString(h.Object, "spec", "bootMACAddress") - o.Expect(check).To(o.Equal(bootMACAddress)) + updated := toBMH(*h) + o.Expect(updated.Spec.BootMACAddress).To(o.Equal(bmh.Spec.BootMACAddress)) }) g.It("have a valid provisioning configuration", func() { dc := oc.AdminDynamicClient() - provisioningClient := dc.Resource(schema.GroupVersionResource{Group: "metal3.io", Resource: "provisionings", Version: "v1alpha1"}) + provisioningClient := dc.Resource(provisioningGVR()) provisioning, err := provisioningClient.Get(context.Background(), "provisioning-configuration", metav1.GetOptions{}) o.Expect(err).NotTo(o.HaveOccurred()) @@ -230,8 +237,7 @@ var _ = g.Describe("[sig-installer][Feature:baremetal][Serial][apigroup:metal3.i host = helper.WaitForProvisioningState(host, "available") g.By("Check that hardware field in status is empty") - _, found, err := unstructured.NestedString(host.Object, "status", "hardware") - o.Expect(err).NotTo(o.HaveOccurred()) - o.Expect(found).To(o.BeFalse()) + bmh := toBMH(*host) + o.Expect(bmh.Status.HardwareDetails).To(o.BeNil()) }) }) From 72e7cd431e3cd2da980e7c182ae64d40bad4be53 Mon Sep 17 00:00:00 2001 From: Honza Pokorny Date: Thu, 12 Mar 2026 12:23:39 -0300 Subject: [PATCH 9/9] baremetal: bump metal3-io/baremetal-operator/apis to v0.11.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update from v0.11.0-alpha.0 to the v0.11.0 release. No struct field changes affect our tests — the only API change is GetChecksum() returning error instead of bool. No transitive dependency changes. Co-Authored-By: Claude Opus 4.6 --- go.mod | 2 +- go.sum | 4 ++-- .../metal3.io/v1alpha1/baremetalhost_types.go | 20 +++++++++++-------- .../v1alpha1/hostfirmwarecomponents_types.go | 11 ++++++++-- vendor/modules.txt | 2 +- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 421f67e72471..c087c66b9f2b 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( github.com/lestrrat/go-jsschema v0.0.0-20181205002244-5c81c58ffcc3 github.com/lithammer/dedent v1.1.0 github.com/mattn/go-sqlite3 v1.14.30 - github.com/metal3-io/baremetal-operator/apis v0.11.0-alpha.0 + github.com/metal3-io/baremetal-operator/apis v0.11.0 github.com/metallb/frr-k8s v0.0.15 github.com/microsoftgraph/msgraph-sdk-go v1.81.0 github.com/onsi/ginkgo/v2 v2.23.3 diff --git a/go.sum b/go.sum index a094396ccacb..b65bbbe30d35 100644 --- a/go.sum +++ b/go.sum @@ -727,8 +727,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.30 h1:bVreufq3EAIG1Quvws73du3/QgdeZ3myglJlrzSYYCY= github.com/mattn/go-sqlite3 v1.14.30/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/metal3-io/baremetal-operator/apis v0.11.0-alpha.0 h1:DMdk7yuxgLXGTL+HmUNHzm8WyZaZGkSFxxdXJjWq6IU= -github.com/metal3-io/baremetal-operator/apis v0.11.0-alpha.0/go.mod h1:TB7T7qAumrfrY/TCSuHvKtpEOLGw9QjnG43ez3vX14g= +github.com/metal3-io/baremetal-operator/apis v0.11.0 h1:sYxjnObegWnDyz028m5Rc6gVYxbSLlvjOAqo6Iq1vOE= +github.com/metal3-io/baremetal-operator/apis v0.11.0/go.mod h1:T0v/wKeJeUfVFeTq1sihv76IZEE2Bn+hqKkh5kGMxA4= github.com/metallb/frr-k8s v0.0.15 h1:6M3UGhovX1EFoaSGjrRD7djUAx3w2I+g81FH8OVtHkM= github.com/metallb/frr-k8s v0.0.15/go.mod h1:TjrGoAf+v00hYGlI8jUdyDxY5udMAOs2GWwrvLWnA4E= github.com/microsoft/kiota-abstractions-go v1.9.3 h1:cqhbqro+VynJ7kObmo7850h3WN2SbvoyhypPn8uJ1SE= diff --git a/vendor/github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1/baremetalhost_types.go b/vendor/github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1/baremetalhost_types.go index e7fbb7a14b97..266ca5e16e13 100644 --- a/vendor/github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1/baremetalhost_types.go +++ b/vendor/github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1/baremetalhost_types.go @@ -16,6 +16,9 @@ limitations under the License. package v1alpha1 import ( + "errors" + "fmt" + "strings" "time" corev1 "k8s.io/api/core/v1" @@ -1118,21 +1121,23 @@ func (host *BareMetalHost) OperationMetricForState(operation ProvisioningState) return } +var supportedChecksums = strings.Join([]string{string(AutoChecksum), string(MD5), string(SHA256), string(SHA512)}, ", ") + // GetChecksum method returns the checksum of an image. -func (image *Image) GetChecksum() (checksum, checksumType string, ok bool) { +func (image *Image) GetChecksum() (checksum, checksumType string, err error) { if image == nil { - return "", "", false + return "", "", errors.New("image is not provided") } if image.DiskFormat != nil && *image.DiskFormat == "live-iso" { // Checksum is not required for live-iso - ok = true - return "", "", ok + return "", "", nil } + // FIXME(dtantsur): Ironic supports oci:// images with an embedded checksum if image.Checksum == "" { // Return empty if checksum is not provided - return "", "", false + return "", "", errors.New("checksum is required for normal images") } switch image.ChecksumType { @@ -1141,12 +1146,11 @@ func (image *Image) GetChecksum() (checksum, checksumType string, ok bool) { case "", AutoChecksum: // No type, let Ironic detect default: - return "", "", false + return "", "", fmt.Errorf("unknown checksumType %s, supported are %s", image.ChecksumType, supportedChecksums) } checksum = image.Checksum - ok = true - return checksum, checksumType, ok + return checksum, checksumType, nil } // +kubebuilder:object:root=true diff --git a/vendor/github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1/hostfirmwarecomponents_types.go b/vendor/github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1/hostfirmwarecomponents_types.go index 885ba717c355..51b406e7ee37 100644 --- a/vendor/github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1/hostfirmwarecomponents_types.go +++ b/vendor/github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1/hostfirmwarecomponents_types.go @@ -18,6 +18,7 @@ package v1alpha1 import ( "fmt" + "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -47,6 +48,12 @@ const ( HostFirmwareComponentsValid UpdatesConditionType = "Valid" ) +// Firmware component constants. +const ( + // NICComponentPrefix is the prefix for NIC firmware components. + NICComponentPrefix = "nic:" +) + // HostFirmwareComponentsSpec defines the desired state of HostFirmwareComponents. type HostFirmwareComponentsSpec struct { Updates []FirmwareUpdate `json:"updates"` @@ -101,8 +108,8 @@ func (host *HostFirmwareComponents) ValidateHostFirmwareComponents() error { allowedNames := map[string]struct{}{"bmc": {}, "bios": {}} for _, update := range host.Spec.Updates { componentName := update.Component - if _, ok := allowedNames[componentName]; !ok { - return fmt.Errorf("component %s is invalid, only 'bmc' or 'bios' are allowed as update names", update.Component) + if _, ok := allowedNames[componentName]; !ok && !strings.HasPrefix(componentName, NICComponentPrefix) { + return fmt.Errorf("component %s is invalid, only 'bmc', 'bios', or names starting with '%s' are allowed as update names", update.Component, NICComponentPrefix) } } diff --git a/vendor/modules.txt b/vendor/modules.txt index cfb7d1d4ef67..2c76e4cca62f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1171,7 +1171,7 @@ github.com/mattn/go-isatty # github.com/mattn/go-sqlite3 v1.14.30 ## explicit; go 1.19 github.com/mattn/go-sqlite3 -# github.com/metal3-io/baremetal-operator/apis v0.11.0-alpha.0 +# github.com/metal3-io/baremetal-operator/apis v0.11.0 ## explicit; go 1.24.0 github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1 # github.com/metallb/frr-k8s v0.0.15