From 1c79728984af1b0b065ce5879581f41fa9f03da3 Mon Sep 17 00:00:00 2001 From: Geert Stappers Date: Thu, 5 Jan 2023 00:21:47 +0100 Subject: [PATCH] feat: add human-friendly entry point * Added /start as entry point, for humans and iPXE The '/poll/1/${netX/mac:hexhyp}' is fairly iPXE internal, not something that invites curious people to look better at shoelaces. Neither it is looking nice in DHCP server configuration. This change adds a HTTP handler for '/start'. And '/start' points to '/poll/1/${netX/mac:hexhyp}'. The benefit of it is that human visible documentation can replace the "voodoo" '/poll/1/${netX/mac:hexhyp}' with "friendly" '/start'. Because it is an addition are the existing HTTP handlers not effected, neither the installed deployments effected. Signed-off-by: Geert Stappers --- README.md | 4 ++-- docs/shoelaces.8.scd | 4 ++-- internal/handlers/polling.go | 10 ++++++++++ internal/polling/polling.go | 30 ++++++++++++++++++++++++++++++ internal/router/router.go | 3 +++ web/templates/html/index.html | 2 +- 6 files changed, 48 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 18d8e64..fec8e04 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ with your TFTP and Shoelaces server addresses. # dhcp.conf next-server ; if exists user-class and option user-class = "iPXE" { - filename "http:///poll/1/${netX/mac:hexhyp}"; + filename "http:///start"; } else { filename "undionly.kpxe"; } @@ -153,7 +153,7 @@ putting it in `dnsmasq.d/ipxe.conf`: ```txt dhcp-match=set:ipxe,175 # iPXE sends a 175 option. dhcp-boot=tag:!ipxe,undionly.kpxe -dhcp-boot=http:///poll/1/${netX/mac:hexhyp} +dhcp-boot=http:///start ``` The **${netX/mac:hexhyp}** strings represents the MAC address of the booting diff --git a/docs/shoelaces.8.scd b/docs/shoelaces.8.scd index 445994c..f803dd3 100644 --- a/docs/shoelaces.8.scd +++ b/docs/shoelaces.8.scd @@ -88,7 +88,7 @@ debug=true # dhcp.conf next-server ; if exists user-class and option user-class = "iPXE" { - filename "http:///poll/1/${netX/mac:hexhyp}"; + filename "http:///start"; } else { filename "undionly.kpxe"; } @@ -100,7 +100,7 @@ the following snippet: ``` dhcp-match=set:ipxe,175 # iPXE sends a 175 option. dhcp-boot=tag:!ipxe,undionly.kpxe -dhcp-boot=http:///poll/1/${netX/mac:hexhyp} +dhcp-boot=http:///start ``` A TFTP server such as *tftpd*(8) must be configured to serve the iPXE executable, diff --git a/internal/handlers/polling.go b/internal/handlers/polling.go index 12f36e2..51fd8ad 100644 --- a/internal/handlers/polling.go +++ b/internal/handlers/polling.go @@ -28,6 +28,16 @@ import ( "github.com/thousandeyes/shoelaces/internal/utils" ) +// StartPollingHandler is called by iPXE boot agents. It returns the poll script. +func StartPollingHandler(w http.ResponseWriter, r *http.Request) { + env := envFromRequest(r) + + script := polling.GenStartScript(env.Logger, env.BaseURL) + + w.Write([]byte(script)) +} + + // PollHandler is called by iPXE boot agents. It returns the boot script // specified on the configuration or, if the host is unknown, it makes it // retry for a while until the user specifies alternative IPXE boot script. diff --git a/internal/polling/polling.go b/internal/polling/polling.go index 853a751..cfd489f 100644 --- a/internal/polling/polling.go +++ b/internal/polling/polling.go @@ -34,6 +34,16 @@ import ( type ManualAction int const ( + startScript = "#!ipxe\n" + + "echo Shoelaces starts polling\n" + + "chain --autofree --replace \\\n" + + " http://{{.baseURL}}/poll/1/${netX/mac:hexhyp}\n" + + "#\n" + + "#\n" + + "# Do\n" + + "# curl http://{{.baseURL}}/poll/1/06-66-de-ad-be-ef\n" + + "# to get an idea about what iPXE will receive.\n" + maxRetry = 10 retryScript = "#!ipxe\n" + @@ -226,6 +236,26 @@ func setHostName(params map[string]interface{}, mac string) { } } +func GenStartScript(logger log.Logger, baseURL string) string { + variablesMap := map[string]interface{}{} + parsedTemplate := &bytes.Buffer{} + + tmpl, err := template.New("retry").Parse(startScript) + if err != nil { + logger.Info("component", "polling", "msg", "Error parsing start template") + panic(err) + } + + variablesMap["baseURL"] = baseURL + err = tmpl.Execute(parsedTemplate, variablesMap) + if err != nil { + logger.Info("component", "polling", "msg", "Error executing start template") + panic(err) + } + + return parsedTemplate.String() +} + func genBootScript(logger log.Logger, templateRenderer *templates.ShoelacesTemplates, baseURL string, script *mappings.Script) string { script.Params["baseURL"] = utils.BaseURLforEnvName(baseURL, script.Environment) text, err := templateRenderer.RenderTemplate(logger, script.Name, script.Params, script.Environment) diff --git a/internal/router/router.go b/internal/router/router.go index d729d16..1313705 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -53,6 +53,9 @@ func ShoelacesRouter(env *environment.Environment) http.Handler { r.PathPrefix("/configs/").Handler(http.StripPrefix("/configs/", handlers.TemplateServer())) + // Starting point for iPXE boot agents, usualy defined by DHCP server. + // Gets the iPXE boot agents into the polling loop. + r.HandleFunc("/start", handlers.StartPollingHandler).Methods("GET") // Called by iPXE boot agents, returns boot script specified on the configuration // or if the host is unknown makes it retry for a while until the user specifies // alternative ipxe boot script diff --git a/web/templates/html/index.html b/web/templates/html/index.html index 4f09ba9..ea93c01 100644 --- a/web/templates/html/index.html +++ b/web/templates/html/index.html @@ -31,7 +31,7 @@

Waiting for MACs...

-

Shoelaces is waiting for a server to boot. Using iPXE, your server should reach the following endpoint:
http://{{ .BaseURL }}/poll/1/<MAC>

+

Shoelaces is waiting for a server to boot. Using iPXE, your server should reach the following endpoint:
http://{{ .BaseURL }}/start

You can automate this process by setting up a DHCP server.