CI / test (push) Successful in 4m48s
Saving settings replaced the whole Settings value, which would have dropped SelfEdgeID and made the next start create a second self edge with a new token. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
122 lines
3.6 KiB
Go
122 lines
3.6 KiB
Go
package hammerhead
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.veillax.com/Veillax/hammerhead/internal/secretbox"
|
|
"git.veillax.com/Veillax/reef/model"
|
|
)
|
|
|
|
func edgeConfigOf(t *testing.T, s *Server, token string) model.EdgeConfig {
|
|
t.Helper()
|
|
ts := httptest.NewServer(s.Handler())
|
|
defer ts.Close()
|
|
req, _ := http.NewRequest("GET", ts.URL+"/edge/config?v=0", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
var cfg model.EdgeConfig
|
|
if resp.StatusCode != 200 {
|
|
t.Fatalf("edge config: %s", resp.Status)
|
|
}
|
|
json.NewDecoder(resp.Body).Decode(&cfg)
|
|
return cfg
|
|
}
|
|
|
|
func tokenFile(t *testing.T, dir string) string {
|
|
b, err := os.ReadFile(filepath.Join(dir, "self-edge.env"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return strings.TrimSpace(strings.SplitN(strings.SplitN(string(b), "BLACKTIP_TOKEN=", 2)[1], "\n", 2)[0])
|
|
}
|
|
|
|
var key = secretbox.GenerateKey()
|
|
|
|
func TestSelfEdgeServesTheDashboardWithoutAProxy(t *testing.T) {
|
|
dir := t.TempDir()
|
|
opts := Options{MasterKey: key, SelfDomain: "Hammerhead.Example.com", SelfTarget: "127.0.0.1:8080"}
|
|
s, err := New(dir, opts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
token := tokenFile(t, dir)
|
|
cfg := edgeConfigOf(t, s, token)
|
|
if len(cfg.Mappings) != 1 || len(cfg.Local) != 1 {
|
|
t.Fatalf("mappings=%d local=%d", len(cfg.Mappings), len(cfg.Local))
|
|
}
|
|
m := cfg.Mappings[0]
|
|
if m.Proto != model.ProtoHTTPS || m.Hostname != "hammerhead.example.com" || m.Port != 443 || !m.RedirectHTTP || !m.Enabled ||
|
|
len(m.Targets) != 1 || m.Targets[0].Host != "127.0.0.1" || m.Targets[0].Port != 8080 || m.Targets[0].SiteID != cfg.Local[0] {
|
|
t.Fatalf("unexpected dashboard route: %+v", m)
|
|
}
|
|
if m.Auth.Protected() {
|
|
t.Fatal("the dashboard has its own login and must not sit behind resource auth")
|
|
}
|
|
edgeID := s.st.Settings.SelfEdgeID
|
|
if info, _ := os.Stat(filepath.Join(dir, "self-edge.env")); info.Mode().Perm()&0o077 != 0 && os.PathSeparator == '/' {
|
|
t.Fatalf("token file is readable by others: %v", info.Mode())
|
|
}
|
|
s.Close()
|
|
|
|
// a restart keeps the same edge and token instead of making a new one
|
|
s2, err := New(dir, opts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer s2.Close()
|
|
if s2.st.Settings.SelfEdgeID != edgeID || tokenFile(t, dir) != token || len(s2.st.Edges) != 1 {
|
|
t.Fatal("restart changed the self edge")
|
|
}
|
|
if len(edgeConfigOf(t, s2, token).Mappings) != 1 {
|
|
t.Fatal("route missing after restart")
|
|
}
|
|
}
|
|
|
|
func TestNoSelfEdgeUnlessAsked(t *testing.T) {
|
|
dir := t.TempDir()
|
|
s, err := New(dir, Options{MasterKey: key})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer s.Close()
|
|
if len(s.st.Edges) != 0 {
|
|
t.Fatal("an edge appeared without HAMMERHEAD_SELF_DOMAIN")
|
|
}
|
|
if _, err := os.Stat(filepath.Join(dir, "self-edge.env")); err == nil {
|
|
t.Fatal("token file written without HAMMERHEAD_SELF_DOMAIN")
|
|
}
|
|
}
|
|
|
|
func TestSavingSettingsKeepsTheSelfEdge(t *testing.T) {
|
|
dir := t.TempDir()
|
|
opts := Options{AdminEmail: "admin@test.local", AdminPassword: "password123", MasterKey: key, SelfDomain: "hh.example.com", SelfTarget: "127.0.0.1:8080"}
|
|
s, err := New(dir, opts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
token := tokenFile(t, dir)
|
|
do := apiClient(t, s)
|
|
if code, b := do("PUT", "/api/admin/settings", map[string]any{"dashboardUrl": "https://hh.example.com", "acmeEmail": "me@example.com"}); code != 200 {
|
|
t.Fatalf("settings: %d %s", code, b)
|
|
}
|
|
s.Close()
|
|
s2, err := New(dir, opts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer s2.Close()
|
|
if len(s2.st.Edges) != 1 || tokenFile(t, dir) != token {
|
|
t.Fatalf("saving settings lost the self edge: %d edges", len(s2.st.Edges))
|
|
}
|
|
}
|