diff --git a/Makefile b/Makefile index 7ffd3c24..ba130fbe 100644 --- a/Makefile +++ b/Makefile @@ -53,13 +53,21 @@ generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." .PHONY: fmt -fmt: ## Run go fmt against code. - go fmt ./... +fmt: goimports ## Run goimports against code. + $(GOIMPORTS) -w . .PHONY: vet vet: ## Run go vet against code. go vet ./... +.PHONY: add-license +add-license: addlicense ## Add license headers to all go files. + find . -name '*.go' -exec $(ADDLICENSE) -f hack/license-header.txt {} + + +.PHONY: check-license +check-license: addlicense ## Check that every file has a license header present. + find . -name '*.go' -exec $(ADDLICENSE) -check -c 'IronCore authors' {} + + .PHONY: test test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out @@ -195,13 +203,10 @@ addlicense: $(ADDLICENSE) ## Download addlicense locally if necessary. $(ADDLICENSE): $(LOCALBIN) test -s $(LOCALBIN)/addlicense || GOBIN=$(LOCALBIN) go install github.com/google/addlicense@$(ADDLICENSE_VERSION) -.PHONY: add-license -add-license: addlicense ## Add license headers to all go files. - find . -name '*.go' -exec $(ADDLICENSE) -f hack/license-header.txt {} + - -.PHONY: check-license -check-license: addlicense ## Check that every file has a license header present. - find . -name '*.go' -exec $(ADDLICENSE) -check -c 'IronCore authors' {} + +.PHONY: goimports +goimports: $(GOIMPORTS) ## Download goimports locally if necessary. +$(GOIMPORTS): $(LOCALBIN) + $(call go-install-tool,$(GOIMPORTS),golang.org/x/tools/cmd/goimports,$(GOIMPORTS_VERSION)) # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary (ideally with version) diff --git a/api/v1alpha1/ipxebootconfig_types.go b/api/v1alpha1/ipxebootconfig_types.go index b097c822..2ee02ae6 100644 --- a/api/v1alpha1/ipxebootconfig_types.go +++ b/api/v1alpha1/ipxebootconfig_types.go @@ -14,12 +14,14 @@ import ( // IPXEBootConfigSpec defines the desired state of IPXEBootConfig type IPXEBootConfigSpec struct { // Important: Run "make" to regenerate code after modifying this file - SystemUUID string `json:"systemUUID,omitempty"` - SystemIP string `json:"systemIP,omitempty"` // TODO: Add the custom serialization. For now validate at the controller. - Image string `json:"image,omitempty"` - KernelURL string `json:"kernelURL,omitempty"` - InitrdURL string `json:"initrdURL,omitempty"` - SquashfsURL string `json:"squashfsURL,omitempty"` + SystemUUID string `json:"systemUUID,omitempty"` + SystemIP string `json:"systemIP,omitempty"` // TODO: Add the custom serialization. For now validate at the controller. + // TODO: remove image as this is not needed + Image string `json:"image,omitempty"` + KernelURL string `json:"kernelURL,omitempty"` + InitrdURL string `json:"initrdURL,omitempty"` + SquashfsURL string `json:"squashfsURL,omitempty"` + // TODO: remove later IPXEServerURL string `json:"ipxeServerURL,omitempty"` IgnitionSecretRef *corev1.LocalObjectReference `json:"ignitionSecretRef,omitempty"` } diff --git a/cmd/main.go b/cmd/main.go index 051f6a31..a6e60476 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,8 +7,12 @@ import ( "context" "crypto/tls" "flag" + "fmt" "os" + metalv1alpha1 "github.com/afritzler/metal-operator/api/v1alpha1" + "github.com/ironcore-dev/controller-utils/cmdutils/switches" + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -40,9 +44,16 @@ const ( systemIPIndexKey = "spec.systemIP" ) +const ( + // core controllers + ipxeBootConfigController = "ipxebootconfig" + serverBootConfigController = "serverbootconfig" +) + func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(metalv1alpha1.AddToScheme(scheme)) utilruntime.Must(bootv1alpha1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } @@ -58,7 +69,13 @@ func main() { var enableHTTP2 bool var ipxeServerAddr string var imageProxyServerAddr string + var ipxeServiceURL string + var ipxeServiceProtocol string + var ipxeServicePort int + flag.IntVar(&ipxeServicePort, "ipxe-service-port", 5000, "IPXE Service port to listen on.") + flag.StringVar(&ipxeServiceProtocol, "ipxe-service-protocol", "http", "IPXE Service Protocol.") + flag.StringVar(&ipxeServiceURL, "ipxe-service-url", "", "IPXE Service URL.") flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") flag.StringVar(&ipxeServerAddr, "ipxe-server-address", ":8082", "The address the ipxe-server binds to.") @@ -70,6 +87,20 @@ func main() { "If set the metrics endpoint is served securely") flag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") + + controllers := switches.New( + // core controllers + ipxeBootConfigController, + serverBootConfigController, + ) + + flag.Var(controllers, "controllers", + fmt.Sprintf("Controllers to enable. All controllers: %v. Disabled-by-default controllers: %v", + controllers.All(), + controllers.DisabledByDefault(), + ), + ) + opts := zap.Options{ Development: true, } @@ -78,6 +109,17 @@ func main() { ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + // set the correct ipxe service URL by getting the address from the environment + var ipxeServiceAddr string + if ipxeServiceURL == "" { + ipxeServiceAddr = os.Getenv("IPXE_SERVER_ADDRESS") + if ipxeServiceAddr == "" { + setupLog.Error(nil, "failed to set the ipxe service URL as no address is provided") + os.Exit(1) + } + ipxeServiceURL = fmt.Sprintf("%s://%s:%d", ipxeServiceProtocol, ipxeServiceAddr, ipxeServicePort) + } + // if the enable-http2 flag is false (the default), http/2 should be disabled // due to its vulnerabilities. More specifically, disabling http/2 will // prevent from being vulnerable to the HTTP/2 Stream Cancelation and @@ -126,12 +168,25 @@ func main() { os.Exit(1) } - if err = (&controller.IPXEBootConfigReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "IPXEBootConfig") - os.Exit(1) + if controllers.Enabled(ipxeBootConfigController) { + if err = (&controller.IPXEBootConfigReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "IPXEBootConfig") + os.Exit(1) + } + } + + if controllers.Enabled(serverBootConfigController) { + if err = (&controller.ServerBootConfigurationReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + IPXEServiceURL: ipxeServiceURL, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ServerBootConfiguration") + os.Exit(1) + } } //+kubebuilder:scaffold:builder @@ -155,7 +210,7 @@ func main() { } setupLog.Info("starting ipxe-server") - go ipxeserver.RunIPXEServer(ipxeServerAddr, mgr.GetClient(), serverLog.WithName("ipxeserver"), *defaultIpxeTemplateData) + go ipxeserver.RunIPXEServer(ipxeServerAddr, ipxeServiceURL, mgr.GetClient(), serverLog.WithName("ipxeserver"), *defaultIpxeTemplateData) setupLog.Info("starting image-proxy-server") go ipxeserver.RunImageProxyServer(imageProxyServerAddr, mgr.GetClient(), serverLog.WithName("imageproxyserver")) diff --git a/config/crd/bases/boot.ironcore.dev_ipxebootconfigs.yaml b/config/crd/bases/boot.ironcore.dev_ipxebootconfigs.yaml index 8593a4b5..40d798f9 100644 --- a/config/crd/bases/boot.ironcore.dev_ipxebootconfigs.yaml +++ b/config/crd/bases/boot.ironcore.dev_ipxebootconfigs.yaml @@ -60,10 +60,12 @@ spec: type: object x-kubernetes-map-type: atomic image: + description: 'TODO: remove image as this is not needed' type: string initrdURL: type: string ipxeServerURL: + description: 'TODO: remove later' type: string kernelURL: type: string diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 4a1fd365..468db5f7 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -12,6 +12,23 @@ rules: - get - list - watch +- apiGroups: + - boot.ironcore.dev + resources: + - ipxebootconfig + verbs: + - create + - delete + - get + - list + - patch + - watch +- apiGroups: + - boot.ironcore.dev + resources: + - ipxebootconfig/status + verbs: + - get - apiGroups: - boot.ironcore.dev resources: @@ -38,3 +55,25 @@ rules: - get - patch - update +- apiGroups: + - metal.ironcore.dev + resources: + - serverbootconfigurations + verbs: + - get + - list + - watch +- apiGroups: + - metal.ironcore.dev + resources: + - serverbootconfigurations/finalizers + verbs: + - update +- apiGroups: + - metal.ironcore.dev + resources: + - serverbootconfigurations/status + verbs: + - get + - patch + - update diff --git a/go.mod b/go.mod index 3925d9b6..52639d5a 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,12 @@ module github.com/ironcore-dev/ipxe-operator -go 1.21 +go 1.22.2 require ( + github.com/afritzler/metal-operator v0.0.0-20240412131812-1fa9454427d0 github.com/go-logr/logr v1.4.1 + github.com/ironcore-dev/controller-utils v0.9.3 + github.com/ironcore-dev/ironcore-image v0.2.1 github.com/onsi/ginkgo/v2 v2.17.1 github.com/onsi/gomega v1.32.0 github.com/opencontainers/image-spec v1.1.0 @@ -14,8 +17,11 @@ require ( ) require ( + github.com/Microsoft/hcsshim v0.11.4 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/containerd/containerd v1.7.14 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.8.0 // indirect @@ -36,6 +42,7 @@ require ( github.com/imdario/mergo v0.3.13 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.16.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -47,13 +54,14 @@ require ( github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect + go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect golang.org/x/net v0.20.0 // indirect golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/term v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect @@ -61,6 +69,8 @@ require ( golang.org/x/tools v0.17.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/grpc v1.58.3 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 146f5d7e..a2da4e39 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= +github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/afritzler/metal-operator v0.0.0-20240412131812-1fa9454427d0 h1:2TzFeArtwH+UWUvtBneoz5n2+xOVfSrtOHf91wyh0wY= +github.com/afritzler/metal-operator v0.0.0-20240412131812-1fa9454427d0/go.mod h1:haYUsrzsOZA61Z2pX7ES7KtLNF6JLI9LfIvkSTW/dhU= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -5,6 +9,10 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/containerd/containerd v1.7.14 h1:H/XLzbnGuenZEGK+v0RkwTdv2u1QFAruMe5N0GNPJwA= +github.com/containerd/containerd v1.7.14/go.mod h1:YMC9Qt5yzNqXx/fO4j/5yYVIHXSRrlB3H7sxkUTvspg= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -52,12 +60,18 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/ironcore-dev/controller-utils v0.9.3 h1:sTrnxSzX5RrLf4B8KrAH2axSC+gxfJXphkV6df2GSsw= +github.com/ironcore-dev/controller-utils v0.9.3/go.mod h1:djKnxDs0Hwxhhc0VmVY8tZnrOrElvrRV2jov/LiCZ2Y= +github.com/ironcore-dev/ironcore-image v0.2.1 h1:7LsIftIRX5btnSieo1J+xUSnW1tSIsGzYgD6NnpObyY= +github.com/ironcore-dev/ironcore-image v0.2.1/go.mod h1:BNaacvN5++9zGiTDJea4vvGDwHvPJE6S9Xb3G7hsFQU= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -98,6 +112,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -105,6 +121,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -116,8 +133,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -137,10 +154,13 @@ golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= @@ -166,6 +186,10 @@ gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/controller/helper.go b/internal/controller/helper.go new file mode 100644 index 00000000..6137b1f2 --- /dev/null +++ b/internal/controller/helper.go @@ -0,0 +1,23 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import "sigs.k8s.io/controller-runtime/pkg/client" + +const ( + fieldOwner = client.FieldOwner("boot.ironcore.dev/controller-manager") +) diff --git a/internal/controller/serverbootconfiguration_controller.go b/internal/controller/serverbootconfiguration_controller.go new file mode 100644 index 00000000..9ea99c02 --- /dev/null +++ b/internal/controller/serverbootconfiguration_controller.go @@ -0,0 +1,199 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "fmt" + "strings" + + "github.com/ironcore-dev/ipxe-operator/api/v1alpha1" + ironcoreimage "github.com/ironcore-dev/ironcore-image" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + metalv1alpha1 "github.com/afritzler/metal-operator/api/v1alpha1" + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type ServerBootConfigurationReconciler struct { + client.Client + Scheme *runtime.Scheme + IPXEServiceURL string +} + +//+kubebuilder:rbac:groups=metal.ironcore.dev,resources=serverbootconfigurations,verbs=get;list;watch +//+kubebuilder:rbac:groups=metal.ironcore.dev,resources=serverbootconfigurations/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=metal.ironcore.dev,resources=serverbootconfigurations/finalizers,verbs=update +//+kubebuilder:rbac:groups=boot.ironcore.dev,resources=ipxebootconfig,verbs=get;list;watch;create;delete;patch +//+kubebuilder:rbac:groups=boot.ironcore.dev,resources=ipxebootconfig/status,verbs=get + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +func (r *ServerBootConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := ctrl.LoggerFrom(ctx) + bootConfig := &metalv1alpha1.ServerBootConfiguration{} + if err := r.Get(ctx, req.NamespacedName, bootConfig); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + return r.reconcileExists(ctx, log, bootConfig) +} + +func (r *ServerBootConfigurationReconciler) reconcileExists(ctx context.Context, log logr.Logger, config *metalv1alpha1.ServerBootConfiguration) (ctrl.Result, error) { + if !config.DeletionTimestamp.IsZero() { + return r.delete(ctx, log, config) + } + return r.reconcile(ctx, log, config) +} + +func (r *ServerBootConfigurationReconciler) delete(ctx context.Context, log logr.Logger, config *metalv1alpha1.ServerBootConfiguration) (ctrl.Result, error) { + return ctrl.Result{}, nil +} + +func (r *ServerBootConfigurationReconciler) reconcile(ctx context.Context, log logr.Logger, config *metalv1alpha1.ServerBootConfiguration) (ctrl.Result, error) { + log.V(1).Info("Reconciling ServerBootConfiguration") + + systemUUID, err := r.getSystemUUIDFromBootConfig(ctx, config) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to get system UUID from BootConfig: %w", err) + } + log.V(1).Info("Got system UUID from BootConfig", "systemUUID", systemUUID) + + systemIP, err := r.getSystemIPFromBootConfig(ctx, config) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to get system IP from BootConfig: %w", err) + } + log.V(1).Info("Got system IP from BootConfig", "systemIP", systemIP) + + kernelURL, initrdURL, squashFSURL, err := r.getImageDetailsFromConfig(ctx, config) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to get image details from BootConfig: %w", err) + } + log.V(1).Info("Extracted OS image layer details") + + ipxeConfig := &v1alpha1.IPXEBootConfig{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "boot.ironcore.dev/v1alpha1", + Kind: "IPXEBootConfig", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: config.Namespace, + Name: config.Name, + }, + Spec: v1alpha1.IPXEBootConfigSpec{ + SystemUUID: systemUUID, + SystemIP: systemIP, + KernelURL: kernelURL, + InitrdURL: initrdURL, + SquashfsURL: squashFSURL, + IgnitionSecretRef: &v1.LocalObjectReference{Name: config.Spec.IgnitionSecretRef.Name}, + }, + } + + if err := controllerutil.SetControllerReference(config, ipxeConfig, r.Scheme); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to set controller reference: %w", err) + } + log.V(1).Info("Set controller reference") + + if err := r.Patch(ctx, ipxeConfig, client.Apply, fieldOwner, client.ForceOwnership); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to apply IPXE config: %w", err) + } + log.V(1).Info("Applied IPXE config for server boot config") + + if err := r.Get(ctx, client.ObjectKey{Namespace: config.Namespace, Name: config.Name}, ipxeConfig); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to get IPXE config: %w", err) + } + + if err := r.patchConfigStateFromIPXEState(ctx, ipxeConfig, config); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to patch server boot config state to %s: %w", ipxeConfig.Status.State, err) + } + log.V(1).Info("Patched server boot config state") + + log.V(1).Info("Reconciled ServerBootConfiguration") + return ctrl.Result{}, nil +} + +func (r *ServerBootConfigurationReconciler) patchConfigStateFromIPXEState(ctx context.Context, ipxeConfig *v1alpha1.IPXEBootConfig, config *metalv1alpha1.ServerBootConfiguration) error { + if ipxeConfig.Status.State == v1alpha1.IPXEBootConfigStateReady { + return r.patchState(ctx, config, metalv1alpha1.ServerBootConfigurationStateReady) + } + + if ipxeConfig.Status.State == v1alpha1.IPXEBootConfigStateError { + return r.patchState(ctx, config, metalv1alpha1.ServerBootConfigurationStateError) + } + return nil +} + +func (r *ServerBootConfigurationReconciler) patchState(ctx context.Context, config *metalv1alpha1.ServerBootConfiguration, state metalv1alpha1.ServerBootConfigurationState) error { + configBase := config.DeepCopy() + config.Status.State = state + if err := r.Status().Patch(ctx, config, client.MergeFrom(configBase)); err != nil { + return err + } + return nil +} + +func (r *ServerBootConfigurationReconciler) getSystemUUIDFromBootConfig(ctx context.Context, config *metalv1alpha1.ServerBootConfiguration) (string, error) { + server := &metalv1alpha1.Server{} + if err := r.Get(ctx, client.ObjectKey{Name: config.Spec.ServerRef.Name}, server); err != nil { + return "", fmt.Errorf("failed to get Server: %w", err) + } + + return server.Spec.UUID, nil +} + +func (r *ServerBootConfigurationReconciler) getSystemIPFromBootConfig(ctx context.Context, config *metalv1alpha1.ServerBootConfiguration) (string, error) { + server := &metalv1alpha1.Server{} + if err := r.Get(ctx, client.ObjectKey{Name: config.Spec.ServerRef.Name}, server); err != nil { + return "", fmt.Errorf("failed to get Server: %w", err) + } + + for _, nic := range server.Status.NetworkInterfaces { + // TODO: we will use the first NIC for now. Need to decide later what to do about it. + return nic.IP.String(), nil + } + + return "", nil +} + +func (r *ServerBootConfigurationReconciler) getImageDetailsFromConfig(ctx context.Context, config *metalv1alpha1.ServerBootConfiguration) (string, string, string, error) { + // http://[2a10:afc0:e013:d002::]:30007/image?imageName=ghcr.io/ironcore-dev/os-images/gardenlinux&version=1443.3&layerName=initramfs + imageDetails := strings.Split(config.Spec.Image, ":") + if len(imageDetails) != 2 { + return "", "", "", fmt.Errorf("invalid image format") + } + kernelURL := fmt.Sprintf("%s/image?imageName=%s&version=%s&layerName=%s", r.IPXEServiceURL, imageDetails[0], imageDetails[1], ironcoreimage.KernelLayerMediaType) + initrdURL := fmt.Sprintf("%s/image?imageName=%s&version=%s&layerName=%s", r.IPXEServiceURL, imageDetails[0], imageDetails[1], ironcoreimage.InitRAMFSLayerMediaType) + // TODO: move this const to ironcore-image + const squashFSMediaTypeLayer = "application/vnd.ironcore.image.squashfs.v1alpha1.squashfs" + squashFSURL := fmt.Sprintf("%s/image?imageName=%s&version=%s&layerName=%s", r.IPXEServiceURL, imageDetails[0], imageDetails[1], squashFSMediaTypeLayer) + + return kernelURL, initrdURL, squashFSURL, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ServerBootConfigurationReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&metalv1alpha1.ServerBootConfiguration{}). + Owns(&v1alpha1.IPXEBootConfig{}). + Complete(r) +} diff --git a/internal/controller/serverbootconfiguration_controller_test.go b/internal/controller/serverbootconfiguration_controller_test.go new file mode 100644 index 00000000..b8d10855 --- /dev/null +++ b/internal/controller/serverbootconfiguration_controller_test.go @@ -0,0 +1,98 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + metalv1alpha1 "github.com/afritzler/metal-operator/api/v1alpha1" + "github.com/ironcore-dev/ipxe-operator/api/v1alpha1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/utils/ptr" + . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var _ = Describe("ServerBootConfiguration Controller", func() { + ns := SetupTest() + + 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()) + DeferCleanup(k8sClient.Delete, server) + + 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: "foo:bar", + 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.SystemIP", "1.1.1.1"), + HaveField("Spec.KernelURL", "http://localhost:5000/image?imageName=foo&version=bar&layerName=application/vnd.ironcore.image.vmlinuz.v1alpha1.vmlinuz"), + HaveField("Spec.InitrdURL", "http://localhost:5000/image?imageName=foo&version=bar&layerName=application/vnd.ironcore.image.initramfs.v1alpha1.initramfs"), + HaveField("Spec.SquashfsURL", "http://localhost:5000/image?imageName=foo&version=bar&layerName=application/vnd.ironcore.image.squashfs.v1alpha1.squashfs"), + HaveField("Spec.IgnitionSecretRef.Name", "foo"), + )) + }) +}) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 38b8a1ff..f1121a2a 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -4,10 +4,19 @@ package controller import ( + "context" "fmt" "path/filepath" "runtime" "testing" + "time" + + metalv1alpha1 "github.com/afritzler/metal-operator/api/v1alpha1" + "github.com/ironcore-dev/controller-utils/modutils" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + ctrl "sigs.k8s.io/controller-runtime" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -16,6 +25,7 @@ import ( "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" + . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -23,6 +33,12 @@ import ( //+kubebuilder:scaffold:imports ) +const ( + pollingInterval = 50 * time.Millisecond + eventuallyTimeout = 3 * time.Second + consistentlyDuration = 1 * time.Second +) + // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. @@ -31,6 +47,10 @@ var k8sClient client.Client var testEnv *envtest.Environment func TestControllers(t *testing.T) { + SetDefaultConsistentlyPollingInterval(pollingInterval) + SetDefaultEventuallyPollingInterval(pollingInterval) + SetDefaultEventuallyTimeout(eventuallyTimeout) + SetDefaultConsistentlyDuration(consistentlyDuration) RegisterFailHandler(Fail) RunSpecs(t, "Controller Suite") @@ -41,7 +61,10 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + CRDDirectoryPaths: []string{ + filepath.Join("..", "..", "config", "crd", "bases"), + filepath.Join(modutils.Dir("github.com/afritzler/metal-operator", "config", "crd", "bases")), + }, ErrorIfCRDPathMissing: true, // The BinaryAssetsDirectory is only required if you want to run the tests directly @@ -59,8 +82,10 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - err = bootv1alpha1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) + DeferCleanup(testEnv.Stop) + + Expect(bootv1alpha1.AddToScheme(scheme.Scheme)).NotTo(HaveOccurred()) + Expect(metalv1alpha1.AddToScheme(scheme.Scheme)).NotTo(HaveOccurred()) //+kubebuilder:scaffold:scheme @@ -68,10 +93,42 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) + // set komega client + SetClient(k8sClient) }) -var _ = AfterSuite(func() { - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) -}) +func SetupTest() *corev1.Namespace { + ns := &corev1.Namespace{} + + BeforeEach(func(ctx SpecContext) { + var mgrCtx context.Context + mgrCtx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) + + *ns = corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "test-", + }, + } + Expect(k8sClient.Create(ctx, ns)).To(Succeed(), "failed to create test namespace") + DeferCleanup(k8sClient.Delete, ns) + + k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + }) + Expect(err).ToNot(HaveOccurred()) + + Expect((&ServerBootConfigurationReconciler{ + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + IPXEServiceURL: "http://localhost:5000", + }).SetupWithManager(k8sManager)).To(Succeed()) + + go func() { + defer GinkgoRecover() + Expect(k8sManager.Start(mgrCtx)).To(Succeed(), "failed to start manager") + }() + }) + + return ns +} diff --git a/server/ipxeserver.go b/server/ipxeserver.go index b15096d7..6b871454 100644 --- a/server/ipxeserver.go +++ b/server/ipxeserver.go @@ -24,9 +24,9 @@ type IPXETemplateData struct { IPXEServerURL string } -func RunIPXEServer(ipxeServerAddr string, k8sClient client.Client, log logr.Logger, defaultIpxeTemplateData IPXETemplateData) { +func RunIPXEServer(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, defaultIpxeTemplateData) + handleIPXE(w, r, k8sClient, log, ipxeServiceURL, defaultIpxeTemplateData) }) http.HandleFunc("/ignition/", func(w http.ResponseWriter, r *http.Request) { @@ -41,7 +41,7 @@ func RunIPXEServer(ipxeServerAddr string, k8sClient client.Client, log logr.Logg } } -func handleIPXE(w http.ResponseWriter, r *http.Request, k8sClient client.Client, log logr.Logger, defaultIpxeTemplateData IPXETemplateData) { +func handleIPXE(w http.ResponseWriter, r *http.Request, k8sClient client.Client, log logr.Logger, ipxeServiceURL string, defaultIpxeTemplateData IPXETemplateData) { log.Info("Processing IPXE request", "method", r.Method, "path", r.URL.Path, "clientIP", r.RemoteAddr) ctx := r.Context() @@ -68,7 +68,7 @@ func handleIPXE(w http.ResponseWriter, r *http.Request, k8sClient client.Client, KernelURL: config.Spec.KernelURL, InitrdURL: config.Spec.InitrdURL, SquashfsURL: config.Spec.SquashfsURL, - IPXEServerURL: config.Spec.IPXEServerURL, + IPXEServerURL: ipxeServiceURL, } }