diff --git a/internal/controller/serverbootconfiguration_pxe_controller.go b/internal/controller/serverbootconfiguration_pxe_controller.go index 27aa3182..c31b0cf8 100644 --- a/internal/controller/serverbootconfiguration_pxe_controller.go +++ b/internal/controller/serverbootconfiguration_pxe_controller.go @@ -219,42 +219,49 @@ func (r *ServerBootConfigurationPXEReconciler) getLayerDigestsFromNestedManifest return "", "", "", fmt.Errorf("failed to resolve image reference: %w", err) } - indexData, err := fetchContent(ctx, resolver, name, desc) + targetManifestDesc := desc + manifestData, err := fetchContent(ctx, resolver, name, desc) if err != nil { - return "", "", "", fmt.Errorf("failed to fetch index manifest: %w", err) + return "", "", "", fmt.Errorf("failed to fetch manifest data: %w", err) } - var indexManifest ocispec.Index - if err := json.Unmarshal(indexData, &indexManifest); err != nil { + var manifest ocispec.Manifest + if err := json.Unmarshal(manifestData, &manifest); err != nil { return "", "", "", fmt.Errorf("failed to unmarshal index manifest: %w", err) } - var targetManifestDesc ocispec.Descriptor - for _, manifest := range indexManifest.Manifests { - if strings.HasPrefix(manifest.Annotations["cname"], CNAMEPrefixMetalPXE) { - if manifest.Annotations["architecture"] == r.Architecture { - targetManifestDesc = manifest - break - } + if desc.MediaType == ocispec.MediaTypeImageIndex { + var indexManifest ocispec.Index + if err := json.Unmarshal(manifestData, &indexManifest); err != nil { + return "", "", "", fmt.Errorf("failed to unmarshal index manifest: %w", err) } - } - if targetManifestDesc.Digest == "" { - return "", "", "", fmt.Errorf("failed to find target manifest with cname annotation") - } + for _, manifest := range indexManifest.Manifests { + if strings.HasPrefix(manifest.Annotations["cname"], CNAMEPrefixMetalPXE) { + if manifest.Annotations["architecture"] == r.Architecture { + targetManifestDesc = manifest + break + } + } + } + if targetManifestDesc.Digest == "" { + return "", "", "", fmt.Errorf("failed to find target manifest with cname annotation") + } - nestedData, err := fetchContent(ctx, resolver, name, targetManifestDesc) - if err != nil { - return "", "", "", fmt.Errorf("failed to fetch nested manifest: %w", err) - } + nestedData, err := fetchContent(ctx, resolver, name, targetManifestDesc) + if err != nil { + return "", "", "", fmt.Errorf("failed to fetch nested manifest: %w", err) + } - var nestedManifest ocispec.Manifest - if err := json.Unmarshal(nestedData, &nestedManifest); err != nil { - return "", "", "", fmt.Errorf("failed to unmarshal nested manifest: %w", err) + var nestedManifest ocispec.Manifest + if err := json.Unmarshal(nestedData, &nestedManifest); err != nil { + return "", "", "", fmt.Errorf("failed to unmarshal nested manifest: %w", err) + } + manifest = nestedManifest } var kernelDigest, initrdDigest, squashFSDigest string - for _, layer := range nestedManifest.Layers { + for _, layer := range manifest.Layers { if layer.Annotations[AnnotationArchitecture] == r.Architecture { switch layer.MediaType { case MediaTypeKernel: diff --git a/internal/controller/serverbootconfiguration_pxe_controller_test.go b/internal/controller/serverbootconfiguration_pxe_controller_test.go index 628ee24f..bcad78eb 100644 --- a/internal/controller/serverbootconfiguration_pxe_controller_test.go +++ b/internal/controller/serverbootconfiguration_pxe_controller_test.go @@ -91,4 +91,65 @@ var _ = Describe("ServerBootConfiguration Controller", func() { HaveField("Spec.IgnitionSecretRef.Name", "foo"), )) }) + + It("should map a new ServerBootConfiguration", func(ctx SpecContext) { + By("creating a new Server object") + server := &metalv1alpha1.Server{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "server-", + }, + Spec: metalv1alpha1.ServerSpec{ + UUID: "12345", + }, + } + Expect(k8sClient.Create(ctx, server)).To(Succeed()) + + By("patching the Server NICs in Server status") + Eventually(UpdateStatus(server, func() { + server.Status.NetworkInterfaces = []metalv1alpha1.NetworkInterface{ + { + Name: "foo", + IP: metalv1alpha1.MustParseIP("1.1.1.1"), + MACAddress: "abcd", + }, + } + })).Should(Succeed()) + + By("creating a new ServerBootConfiguration") + config := &metalv1alpha1.ServerBootConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns.Name, + GenerateName: "test-", + }, + Spec: metalv1alpha1.ServerBootConfigurationSpec{ + ServerRef: corev1.LocalObjectReference{ + Name: server.Name, + }, + Image: "ghcr.io/gardenlinux/gardenlinux:1770.0-metal_pxe-arm64-1770.0-60d819dd-arm64", + IgnitionSecretRef: &corev1.LocalObjectReference{Name: "foo"}, + }, + } + Expect(k8sClient.Create(ctx, config)).To(Succeed()) + + By("ensuring that the ipxe boot configuration is correct") + bootConfig := &v1alpha1.IPXEBootConfig{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns.Name, + Name: config.Name, + }, + } + Eventually(Object(bootConfig)).Should(SatisfyAll( + HaveField("OwnerReferences", ContainElement(metav1.OwnerReference{ + APIVersion: "metal.ironcore.dev/v1alpha1", + Kind: "ServerBootConfiguration", + Name: config.Name, + UID: config.UID, + Controller: ptr.To(true), + BlockOwnerDeletion: ptr.To(true), + })), + HaveField("Spec.SystemUUID", server.Spec.UUID), + HaveField("Spec.SystemIPs", ContainElement("1.1.1.1")), + HaveField("Spec.IgnitionSecretRef.Name", "foo"), + )) + }) }) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index b62aea08..144d77c3 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -132,7 +132,7 @@ func SetupTest() *corev1.Namespace { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), IPXEServiceURL: "http://localhost:5000", - Architecture: "amd64", + Architecture: "arm64", }).SetupWithManager(k8sManager)).To(Succeed()) Expect((&ServerBootConfigurationHTTPReconciler{