From 74a3dc02c81d8407bb7d678e96c32bcac02f9a11 Mon Sep 17 00:00:00 2001 From: Hardik Dodiya Date: Tue, 6 Aug 2024 12:48:52 +0200 Subject: [PATCH] Support multiple ignition formats --- api/v1alpha1/constants.go | 15 +++++ api/v1alpha1/ipxebootconfig_types.go | 7 --- server/bootserver.go | 84 ++++++++++++++++++---------- 3 files changed, 68 insertions(+), 38 deletions(-) create mode 100644 api/v1alpha1/constants.go diff --git a/api/v1alpha1/constants.go b/api/v1alpha1/constants.go new file mode 100644 index 00000000..deb7dcf2 --- /dev/null +++ b/api/v1alpha1/constants.go @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package v1alpha1 + +const ( + DefaultIgnitionKey = "ignition" // Key for accessing Ignition configuration data within a Kubernetes Secret object. + DefaultIPXEScriptKey = "ipxe-script" // Key for accessing iPXE script data within the iPXE-specific Secret object. + SystemUUIDIndexKey = "spec.systemUUID" // Field to index resources by their system UUID. + SystemIPIndexKey = "spec.systemIPs" // Field to index resources by their system IP addresses. + DefaultFormatKey = "format" // Key for determining the format of the data stored in a Secret, such as fcos or plain-ignition. + IgnitionV2Format = "ignitionv2" // Specifies the format value corresponding to Ignition config version 2. + IgnitionV3Format = "ignitionv3" // Specifies the format value corresponding to Ignition config version 3. + FCOSFormat = "fcos" // Specifies the format value used for Fedora CoreOS specific configurations. +) diff --git a/api/v1alpha1/ipxebootconfig_types.go b/api/v1alpha1/ipxebootconfig_types.go index b9d96a9b..846f2a49 100644 --- a/api/v1alpha1/ipxebootconfig_types.go +++ b/api/v1alpha1/ipxebootconfig_types.go @@ -65,13 +65,6 @@ type IPXEBootConfigList struct { Items []IPXEBootConfig `json:"items"` } -const ( - DefaultIgnitionKey = "ignition" - DefaultIPXEScriptKey = "ipxe-script" - SystemUUIDIndexKey = "spec.systemUUID" - SystemIPIndexKey = "spec.systemIPs" -) - func init() { SchemeBuilder.Register(&IPXEBootConfig{}, &IPXEBootConfigList{}) } diff --git a/server/bootserver.go b/server/bootserver.go index 62760248..ec684ca9 100644 --- a/server/bootserver.go +++ b/server/bootserver.go @@ -4,6 +4,7 @@ package server import ( + "context" "encoding/json" "fmt" "net" @@ -14,6 +15,7 @@ import ( "text/template" corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -157,28 +159,32 @@ func handleIgnitionIPXEBoot(w http.ResponseWriter, r *http.Request, k8sClient cl return } - // TODO: Assuming UUID is unique. ipxeBootConfig := ipxeBootConfigList.Items[0] - ignitionSecret := &corev1.Secret{} - if err := k8sClient.Get(ctx, client.ObjectKey{Name: ipxeBootConfig.Spec.IgnitionSecretRef.Name, Namespace: ipxeBootConfig.Namespace}, ignitionSecret); err != nil { - http.Error(w, "Resource Not Found", http.StatusNotFound) - log.Info("Error: Failed to get Ignition secret", "error", err.Error()) - return + ignitionSecret := corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Name: ipxeBootConfig.Spec.IgnitionSecretRef.Name, + Namespace: ipxeBootConfig.Namespace, + }, } - - ignitionData, ok := ignitionSecret.Data[bootv1alpha1.DefaultIgnitionKey] - if !ok { + ignitionData, ignitionFormat, err := fetchIgnitionData(ctx, k8sClient, ignitionSecret) + if err != nil { http.Error(w, "Resource Not Found", http.StatusNotFound) - log.Info("Error: Ignition data not found in secret") + log.Info("Failed to fetch IgnitionData", "error", err.Error()) return } - ignitionJSONData, err := renderIgnition(ignitionData) - if err != nil { - log.Info("Failed to render the ignition data to json", "error", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return + var ignitionJSONData []byte + switch strings.TrimSpace(ignitionFormat) { + case bootv1alpha1.FCOSFormat: + ignitionJSONData, err = renderIgnition(ignitionData) + if err != nil { + log.Info("Failed to render the ignition data to json", "error", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + default: + ignitionJSONData = ignitionData } w.WriteHeader(http.StatusOK) @@ -222,28 +228,32 @@ func handleIgnitionHTTPBoot(w http.ResponseWriter, r *http.Request, k8sClient cl return } - // TODO: Assuming UUID is unique. - HTTPBootConfig := HTTPBootConfigList.Items[0] + httpBootConfig := HTTPBootConfigList.Items[0] - ignitionSecret := &corev1.Secret{} - if err := k8sClient.Get(ctx, client.ObjectKey{Name: HTTPBootConfig.Spec.IgnitionSecretRef.Name, Namespace: HTTPBootConfig.Spec.IgnitionSecretRef.Namespace}, ignitionSecret); err != nil { - http.Error(w, "Resource Not Found", http.StatusNotFound) - log.Info("Error: Failed to get Ignition secret", "error", err.Error()) - return + ignitionSecret := corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Name: httpBootConfig.Spec.IgnitionSecretRef.Name, + Namespace: httpBootConfig.Spec.IgnitionSecretRef.Namespace, + }, } - - ignitionData, ok := ignitionSecret.Data[bootv1alpha1.DefaultIgnitionKey] - if !ok { + ignitionData, ignitionFormat, err := fetchIgnitionData(ctx, k8sClient, ignitionSecret) + if err != nil { http.Error(w, "Resource Not Found", http.StatusNotFound) - log.Info("Error: Ignition data not found in secret") + log.Info("Failed to fetch IgnitionData", "error", err.Error()) return } - ignitionJSONData, err := renderIgnition(ignitionData) - if err != nil { - log.Info("Failed to render the ignition data to json", "error", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return + var ignitionJSONData []byte + switch strings.TrimSpace(ignitionFormat) { + case bootv1alpha1.FCOSFormat: + ignitionJSONData, err = renderIgnition(ignitionData) + if err != nil { + log.Info("Failed to render the ignition data to json", "error", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + default: + ignitionJSONData = ignitionData } w.WriteHeader(http.StatusOK) @@ -255,6 +265,18 @@ func handleIgnitionHTTPBoot(w http.ResponseWriter, r *http.Request, k8sClient cl } } +func fetchIgnitionData(ctx context.Context, k8sClient client.Client, ignitionSecret corev1.Secret) ([]byte, string, error) { + secretObj := &corev1.Secret{} + if err := k8sClient.Get(ctx, client.ObjectKey{Name: ignitionSecret.Name, Namespace: ignitionSecret.Namespace}, secretObj); err != nil { + return nil, "", fmt.Errorf("failed to get the Ignition Secret %w", err) + } + ignitionData, ok := secretObj.Data[bootv1alpha1.DefaultIgnitionKey] + if !ok { + return nil, "", fmt.Errorf("secret data-key:ignition not found") + } + return ignitionData, string(secretObj.Data[bootv1alpha1.DefaultFormatKey]), nil +} + func renderIgnition(yamlData []byte) ([]byte, error) { translateOptions := butanecommon.TranslateBytesOptions{ Raw: true,