A machine is titled by its own hostname, and follows a rename
The name a machine was added under stays its identity — the URL, its accent, what the hub's API calls it — and is what the Machines page shows as 'Added as'. The hostname is read live rather than at start-up, so renaming a machine reaches the console without a restart. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ec94cb8bff
commit
dbfe163a75
@@ -210,7 +210,7 @@ func (e *alertEngine) evaluate() {
|
||||
}
|
||||
}
|
||||
qb, _ := json.Marshal(query)
|
||||
machines := append([]string{e.s.host}, e.s.nodes.Names()...)
|
||||
machines := append([]string{e.s.hostName()}, e.s.nodes.Names()...)
|
||||
results := map[string]map[string]any{} // machine → ruleID → value | {id: value}
|
||||
reach := map[string]bool{}
|
||||
var wg sync.WaitGroup
|
||||
@@ -221,7 +221,7 @@ func (e *alertEngine) evaluate() {
|
||||
defer wg.Done()
|
||||
var res map[string]any
|
||||
ok := false
|
||||
if m == e.s.host {
|
||||
if m == e.s.hostName() {
|
||||
if out, err := sensors.Shape(e.s.sensors.All(), qb, false); err == nil {
|
||||
res, ok = out.(map[string]any)
|
||||
}
|
||||
@@ -515,6 +515,6 @@ func (s *Server) alertsTest(w http.ResponseWriter, _ *http.Request) {
|
||||
jsonErr(w, 400, "no channels to test")
|
||||
return
|
||||
}
|
||||
e.notify(channels, AlertEvent{T: time.Now().UnixMilli(), Kind: "firing", Rule: "Test alert", Machine: s.host, Sensor: "hamlab.test", Value: "this is only a test", Severity: "info"})
|
||||
e.notify(channels, AlertEvent{T: time.Now().UnixMilli(), Kind: "firing", Rule: "Test alert", Machine: s.hostName(), Sensor: "hamlab.test", Value: "this is only a test", Severity: "info"})
|
||||
w.WriteHeader(204)
|
||||
}
|
||||
|
||||
+29
-8
@@ -73,15 +73,17 @@ type Server struct {
|
||||
version string
|
||||
started time.Time
|
||||
user string
|
||||
host string
|
||||
host string // the machine's hostname as last read; use hostName(), it can change under us
|
||||
hostMu sync.Mutex
|
||||
hostAt time.Time
|
||||
ui fs.FS
|
||||
mux *http.ServeMux
|
||||
}
|
||||
|
||||
func New(cfg *config.Config, sessions *auth.Sessions, mgr *term.Manager, col *metrics.Collector, st *store.Store, svc *services.Manager, cr *cron.Manager, tm *tasks.Manager, up *updates.Manager, sto *storage.Manager, fsLocal, fsSudo files.FS, nm *nodes.Manager, fingerprint string, ui fs.FS, version string) *Server {
|
||||
host, _ := os.Hostname()
|
||||
s := &Server{cfg: cfg, sessions: sessions, mgr: mgr, metrics: col, sensors: sensors.NewRegistry(col), store: st, svc: svc, cron: cr, tasks: tm, updates: up, storage: sto, fsLocal: fsLocal, fsSudo: fsSudo, nodes: nm, fingerprint: fingerprint, ui: ui, version: version,
|
||||
started: time.Now(), host: host, user: os.Getenv("USER")}
|
||||
started: time.Now(), user: os.Getenv("USER")}
|
||||
s.hostName() // read it once now so the first request doesn't have to
|
||||
if u, err := user.Current(); err == nil && u.Username != "" {
|
||||
s.user = u.Username
|
||||
}
|
||||
@@ -572,7 +574,7 @@ func (s *Server) revokeToken(w http.ResponseWriter, r *http.Request) {
|
||||
// ---- nodes: the machines this hub manages ----
|
||||
|
||||
func (s *Server) listNodes(w http.ResponseWriter, _ *http.Request) {
|
||||
jsonOK(w, 200, map[string]any{"nodes": s.nodes.List(), "host": s.host, "fingerprint": s.fingerprint})
|
||||
jsonOK(w, 200, map[string]any{"nodes": s.nodes.List(), "host": s.hostName(), "fingerprint": s.fingerprint})
|
||||
}
|
||||
|
||||
func (s *Server) addNode(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -797,6 +799,25 @@ func clientIP(r *http.Request) string {
|
||||
|
||||
// ---- me ----
|
||||
|
||||
// hostName is this machine's hostname now, not when the daemon started: rename a machine
|
||||
// (hostnamectl, or a DHCP lease) and the console says so within a few seconds — in its header,
|
||||
// in the machine menu, in alerts and in the fleet's sensor queries — without a restart. Read
|
||||
// through a short cache because it is asked for on most requests and on every alert pass.
|
||||
func (s *Server) hostName() string {
|
||||
s.hostMu.Lock()
|
||||
defer s.hostMu.Unlock()
|
||||
if time.Since(s.hostAt) > 5*time.Second {
|
||||
if h, err := os.Hostname(); err == nil && h != "" {
|
||||
if s.host != "" && h != s.host {
|
||||
log.Printf("hostname: %s is now %s", s.host, h)
|
||||
}
|
||||
s.host = h
|
||||
}
|
||||
s.hostAt = time.Now()
|
||||
}
|
||||
return s.host
|
||||
}
|
||||
|
||||
func (s *Server) me(w http.ResponseWriter, _ *http.Request) {
|
||||
load := ""
|
||||
if b, err := os.ReadFile("/proc/loadavg"); err == nil {
|
||||
@@ -820,7 +841,7 @@ func (s *Server) me(w http.ResponseWriter, _ *http.Request) {
|
||||
}
|
||||
})
|
||||
jsonOK(w, 200, map[string]any{
|
||||
"host": s.host, "user": s.user, "version": s.version,
|
||||
"host": s.hostName(), "user": s.user, "version": s.version,
|
||||
"load": load, "uptime": uptime, "service_started": s.started,
|
||||
"reboot_needed": rebootErr == nil, "fingerprint": s.fingerprint, "arch": runtime.GOARCH, "demo_url": demoURL, "addr": addr,
|
||||
})
|
||||
@@ -1086,7 +1107,7 @@ func (s *Server) fleetQuery(w http.ResponseWriter, r *http.Request) {
|
||||
jsonErr(w, 400, err.Error())
|
||||
return
|
||||
}
|
||||
names := append([]string{s.host}, s.nodes.Names()...)
|
||||
names := append([]string{s.hostName()}, s.nodes.Names()...)
|
||||
if len(want) > 0 {
|
||||
names = names[:0]
|
||||
for _, n := range want {
|
||||
@@ -1107,7 +1128,7 @@ func (s *Server) fleetQuery(w http.ResponseWriter, r *http.Request) {
|
||||
var mu sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
for _, name := range names {
|
||||
if name == s.host {
|
||||
if name == s.hostName() {
|
||||
out[name] = local
|
||||
continue
|
||||
}
|
||||
@@ -1478,7 +1499,7 @@ func (s *Server) power(w http.ResponseWriter, r *http.Request) {
|
||||
Confirm string `json:"confirm"`
|
||||
}
|
||||
_ = json.NewDecoder(io.LimitReader(r.Body, 4096)).Decode(&body)
|
||||
if body.Confirm != s.host {
|
||||
if body.Confirm != s.hostName() {
|
||||
jsonErr(w, 400, "confirmation must be the hostname")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ func (s *Server) hubStacks(ctx context.Context, node string) ([]stack, error) {
|
||||
st.Note = note
|
||||
continue
|
||||
}
|
||||
st.Remote, st.HeldBy = remote, s.host
|
||||
st.Remote, st.HeldBy = remote, s.hostName()
|
||||
}
|
||||
var held []stack
|
||||
for _, st := range res.Stacks {
|
||||
|
||||
Reference in New Issue
Block a user