Skip to content

Commit 519eea8

Browse files
authored
Fill in LocalIP and add ApiBaseUrl to manifest.ContentContext for HTTP/TFTP templates (#18)
I noticed that the `LocalIP` field of `manifest.ContentContext` was not filled in when evaluating HTTP templates, so I fixed that. I also added a `ApiBaseUrl` field, so that templates can construct `netbootd` API requests, for example to have a Kickstart script hit the `/api/self/suspend-boot` endpoint in a `%post` script when installation has finished successfully.
1 parent b24d40b commit 519eea8

File tree

6 files changed

+59
-6
lines changed

6 files changed

+59
-6
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ mounts:
124124
appendSuffix: true
125125

126126
- path: /install.ipxe
127-
# The templating context provides access to: .LocalIP, .RemoteIP, .HttpBaseUrl and .Manifest.
127+
# The templating context provides access to: .LocalIP, .RemoteIP, .HttpBaseUrl, .ApiBaseUrl and .Manifest.
128128
# Sprig functions are available: masterminds.github.io/sprig
129129
content: |
130130
#!ipxe

cmd/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ var serverCmd = &cobra.Command{
7676
_ = store.LoadFromDirectory(viper.GetString("manifestPath"))
7777
}
7878
store.GlobalHints.HttpPort = viper.GetInt("http.port")
79+
store.GlobalHints.ApiPort = viper.GetInt("api.port")
7980

8081
// DHCP
8182
dhcpServer, err := dhcpd.NewServer(viper.GetString("address"), viper.GetString("interface"), store)

httpd/handler.go

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ package httpd
33
import (
44
"bytes"
55
_ "embed"
6+
"errors"
7+
"fmt"
68
"io"
79
"net"
810
"net/http"
911
"net/http/httputil"
1012
"net/url"
1113
"os"
1214
"path/filepath"
15+
"strconv"
1316
"strings"
1417
"text/template"
1518
"time"
@@ -23,9 +26,44 @@ type Handler struct {
2326
server *Server
2427
}
2528

29+
func parseIPFromHostPort(hostPort string) (net.IP, error) {
30+
host, _, err := net.SplitHostPort(hostPort)
31+
if err != nil {
32+
return nil, err
33+
}
34+
ip := net.ParseIP(host)
35+
if ip == nil {
36+
return nil, fmt.Errorf("%s: unable to parse ip", host)
37+
}
38+
return ip, nil
39+
}
40+
41+
func parseRemoteIP(r *http.Request) (net.IP, error) {
42+
return parseIPFromHostPort(r.RemoteAddr)
43+
}
44+
45+
func parseLocalIP(r *http.Request) (net.IP, error) {
46+
lip, ok := r.Context().Value(http.LocalAddrContextKey).(net.Addr)
47+
if !ok {
48+
return nil, errors.New("local address not found in request context")
49+
}
50+
if lip == nil {
51+
return nil, errors.New("nil local address")
52+
}
53+
return parseIPFromHostPort(lip.String())
54+
}
55+
2656
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
27-
ip, _, err := net.SplitHostPort(r.RemoteAddr)
28-
raddr := net.ParseIP(ip)
57+
raddr, err := parseRemoteIP(r)
58+
if err != nil {
59+
http.Error(w, "unable to determine remote address: "+err.Error(), http.StatusInternalServerError)
60+
return
61+
}
62+
laddr, err := parseLocalIP(r)
63+
if err != nil {
64+
http.Error(w, "unable to determine local address: "+err.Error(), http.StatusInternalServerError)
65+
return
66+
}
2967

3068
h.server.logger.Info().
3169
Str("path", r.RequestURI).
@@ -95,11 +133,16 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
95133
buf := new(bytes.Buffer)
96134

97135
err = tmpl.Execute(buf, mfest.ContentContext{
136+
LocalIP: laddr,
98137
RemoteIP: raddr,
99138
HttpBaseUrl: &url.URL{
100139
Scheme: "http",
101140
Host: r.Host,
102141
},
142+
ApiBaseUrl: &url.URL{
143+
Scheme: "http",
144+
Host: net.JoinHostPort(laddr.String(), strconv.Itoa(h.server.store.GlobalHints.ApiPort)),
145+
},
103146
Manifest: manifest,
104147
})
105148
if err != nil {
@@ -134,11 +177,11 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
134177
return
135178
} else if mount.LocalDir != "" {
136179
path := filepath.Join(mount.LocalDir, mount.Path)
137-
180+
138181
if mount.AppendSuffix {
139182
path = filepath.Join(mount.LocalDir, strings.TrimPrefix(r.URL.Path, mount.Path))
140183
}
141-
184+
142185
if !strings.HasPrefix(path, mount.LocalDir) {
143186
h.server.logger.Error().
144187
Err(err).

manifest/schema.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ type ContentContext struct {
103103
RemoteIP net.IP
104104
// Base URL to the HTTP service (IP and port) - not API
105105
HttpBaseUrl *url.URL
106+
// Base URL to the API service (IP and port)
107+
ApiBaseUrl *url.URL
106108
// Copy of Manifest
107109
Manifest *Manifest
108110
}

store/store.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type Store struct {
3636
// sort of global config
3737
GlobalHints struct {
3838
HttpPort int
39+
ApiPort int
3940
}
4041
}
4142

tftpd/handler.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import (
66
"errors"
77
"fmt"
88
"io"
9+
"net"
910
"net/http"
1011
"net/url"
1112
"os"
1213
"path/filepath"
14+
"strconv"
1315
"strings"
1416
"text/template"
1517

@@ -139,7 +141,11 @@ func (server *Server) tftpReadHandler(filename string, rf io.ReaderFrom) error {
139141
RemoteIP: raddr.IP,
140142
HttpBaseUrl: &url.URL{
141143
Scheme: "http",
142-
Host: fmt.Sprintf("%s:%d", laddr.String(), server.store.GlobalHints.HttpPort),
144+
Host: net.JoinHostPort(laddr.String(), strconv.Itoa(server.store.GlobalHints.HttpPort)),
145+
},
146+
ApiBaseUrl: &url.URL{
147+
Scheme: "http",
148+
Host: net.JoinHostPort(laddr.String(), strconv.Itoa(server.store.GlobalHints.ApiPort)),
143149
},
144150
Manifest: manifest,
145151
})

0 commit comments

Comments
 (0)