Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (

bootv1alpha1 "github.com/ironcore-dev/ipxe-operator/api/v1alpha1"
"github.com/ironcore-dev/ipxe-operator/internal/controller"
ipxeserver "github.com/ironcore-dev/ipxe-operator/server"
bootserver "github.com/ironcore-dev/ipxe-operator/server"
//+kubebuilder:scaffold:imports
)

Expand Down Expand Up @@ -217,16 +217,21 @@ func main() {
os.Exit(1)
}

if err := IndexHTTPBootConfigBySystemUUID(ctx, mgr); err != nil {
setupLog.Error(err, "unable to set up indexer for HTTPBootConfig SystemUUID")
os.Exit(1)
}

if err := IndexIPXEBootConfigBySystemIP(ctx, mgr); err != nil {
setupLog.Error(err, "unable to set up indexer for IPXEBootConfig SystemIP")
os.Exit(1)
}

setupLog.Info("starting ipxe-server")
go ipxeserver.RunIPXEServer(ipxeServerAddr, ipxeServiceURL, mgr.GetClient(), serverLog.WithName("ipxeserver"), *defaultIpxeTemplateData)
setupLog.Info("starting boot-server")
go bootserver.RunBootServer(ipxeServerAddr, ipxeServiceURL, mgr.GetClient(), serverLog.WithName("bootserver"), *defaultIpxeTemplateData)

setupLog.Info("starting image-proxy-server")
go ipxeserver.RunImageProxyServer(imageProxyServerAddr, mgr.GetClient(), serverLog.WithName("imageproxyserver"))
go bootserver.RunImageProxyServer(imageProxyServerAddr, mgr.GetClient(), serverLog.WithName("imageproxyserver"))

setupLog.Info("starting manager")
if err := mgr.Start(ctx); err != nil {
Expand Down Expand Up @@ -258,8 +263,20 @@ func IndexIPXEBootConfigBySystemIP(ctx context.Context, mgr ctrl.Manager) error
)
}

func NewDefaultIPXETemplateData() *ipxeserver.IPXETemplateData {
var cfg ipxeserver.IPXETemplateData
func IndexHTTPBootConfigBySystemUUID(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(
ctx,
&bootv1alpha1.HTTPBootConfig{},
systemUUIDIndexKey,
func(Obj client.Object) []string {
HTTPBootConfig := Obj.(*bootv1alpha1.HTTPBootConfig)
return []string{HTTPBootConfig.Spec.SystemUUID}
},
)
}

func NewDefaultIPXETemplateData() *bootserver.IPXETemplateData {
var cfg bootserver.IPXETemplateData
flag.StringVar(&cfg.KernelURL, "default-kernel-url", "", "Default URL for the kernel")
flag.StringVar(&cfg.InitrdURL, "default-initrd-url", "", "Default URL for the initrd")
flag.StringVar(&cfg.SquashfsURL, "default-squashfs-url", "", "Default URL for the squashfs")
Expand Down
88 changes: 74 additions & 14 deletions server/ipxeserver.go → server/bootserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,36 @@ type IPXETemplateData struct {
IPXEServerURL string
}

func RunIPXEServer(ipxeServerAddr string, ipxeServiceURL string, k8sClient client.Client, log logr.Logger, defaultIpxeTemplateData IPXETemplateData) {
func RunBootServer(ipxeServerAddr string, ipxeServiceURL string, k8sClient client.Client, log logr.Logger, defaultIpxeTemplateData IPXETemplateData) {
http.HandleFunc("/ipxe", func(w http.ResponseWriter, r *http.Request) {
handleIPXE(w, r, k8sClient, log, ipxeServiceURL, defaultIpxeTemplateData)
})

http.HandleFunc("/ignition/", func(w http.ResponseWriter, r *http.Request) {
handleIgnition(w, r, k8sClient, log)
uuid := path.Base(r.URL.Path)
if uuid == "" {
http.Error(w, "Bad Request: UUID is required", http.StatusBadRequest)
return
}

ipxeBootConfigList := &bootv1alpha1.IPXEBootConfigList{}
err := k8sClient.List(r.Context(), ipxeBootConfigList, client.MatchingFields{"spec.systemUUID": uuid})
if client.IgnoreNotFound(err) != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}

if len(ipxeBootConfigList.Items) == 0 {
log.Info("No IPXEBootConfig found with given UUID")
handleIgnitionHTTPBoot(w, r, k8sClient, log, uuid)
} else {
handleIgnitionIPXEBoot(w, r, k8sClient, log, uuid)
}
})

// TODO: Use Logger
log.Info("Starting ipxe server", "address", ipxeServerAddr)
log.Info("Starting boot server", "address", ipxeServerAddr)
if err := http.ListenAndServe(ipxeServerAddr, nil); err != nil {
log.Error(err, "failed to start ipxe server")
log.Error(err, "failed to start boot server")
panic(err)
}
}
Expand Down Expand Up @@ -100,17 +117,10 @@ func handleIPXE(w http.ResponseWriter, r *http.Request, k8sClient client.Client,
}
}

func handleIgnition(w http.ResponseWriter, r *http.Request, k8sClient client.Client, log logr.Logger) {
func handleIgnitionIPXEBoot(w http.ResponseWriter, r *http.Request, k8sClient client.Client, log logr.Logger, uuid string) {
log.Info("Processing Ignition request", "method", r.Method, "path", r.URL.Path, "clientIP", r.RemoteAddr)
ctx := r.Context()

uuid := path.Base(r.URL.Path)
if uuid == "" {
http.Error(w, "Bad Request", http.StatusBadRequest)
log.Info("Error: UUID is required")
return
}

ipxeBootConfigList := &bootv1alpha1.IPXEBootConfigList{}
if err := k8sClient.List(ctx, ipxeBootConfigList, client.MatchingFields{"spec.systemUUID": uuid}); err != nil {
http.Error(w, "Resource Not Found", http.StatusNotFound)
Expand All @@ -120,7 +130,7 @@ func handleIgnition(w http.ResponseWriter, r *http.Request, k8sClient client.Cli

if len(ipxeBootConfigList.Items) == 0 {
http.Error(w, "Resource Not Found", http.StatusNotFound)
log.Info("Error: No IPXEBootConfig found with given UUID")
log.Info("No IPXEBootConfig found with given UUID")
return
}

Expand Down Expand Up @@ -157,6 +167,56 @@ func handleIgnition(w http.ResponseWriter, r *http.Request, k8sClient client.Cli
}
}

func handleIgnitionHTTPBoot(w http.ResponseWriter, r *http.Request, k8sClient client.Client, log logr.Logger, uuid string) {
log.Info("Processing Ignition request", "method", r.Method, "path", r.URL.Path, "clientIP", r.RemoteAddr)
ctx := r.Context()

HTTPBootConfigList := &bootv1alpha1.HTTPBootConfigList{}
if err := k8sClient.List(ctx, HTTPBootConfigList, client.MatchingFields{"spec.systemUUID": uuid}); err != nil {
http.Error(w, "Resource Not Found", http.StatusNotFound)
log.Info("Failed to find HTTPBootConfigList", "error", err.Error())
return
}

if len(HTTPBootConfigList.Items) == 0 {
http.Error(w, "Resource Not Found", http.StatusNotFound)
log.Info("No HTTPBootConfig found with given UUID")
return
}

// TODO: Assuming UUID is unique.
HTTPBootConfig := HTTPBootConfigList.Items[0]

ignitionSecret := &corev1.Secret{}
if err := k8sClient.Get(ctx, client.ObjectKey{Name: HTTPBootConfig.Spec.IgnitionSecretRef.Name, Namespace: HTTPBootConfig.Namespace}, ignitionSecret); err != nil {
http.Error(w, "Resource Not Found", http.StatusNotFound)
log.Info("Error: Failed to get Ignition secret", "error", err.Error())
return
}

ignitionData, ok := ignitionSecret.Data[bootv1alpha1.DefaultIgnitionKey]
if !ok {
http.Error(w, "Resource Not Found", http.StatusNotFound)
log.Info("Error: Ignition data not found in secret")
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
}

w.WriteHeader(http.StatusOK)
_, err = w.Write(ignitionJSONData)
if err != nil {
log.Info("Failed to write the ignition http response", "error", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
}

func renderIgnition(yamlData []byte) ([]byte, error) {
translateOptions := butanecommon.TranslateBytesOptions{
Raw: true,
Expand Down