Report update progress and log to Hammerhead while updating
CI / test (push) Successful in 4m52s
Release / release (push) Successful in 7m36s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
VeillaxandClaude Sonnet 5.5 committed 2026-10-26 17:58:37 -06:00
1 parent 2943af9bbf
commit 17c222c946
4 files changed
+51 -6

No files matched your search

+43 -4
View File
@@ -39,7 +39,7 @@ type Config struct {
// Version, AutoUpdate and UpdateNow work as in blacktip.Config.
Version string
AutoUpdate bool
UpdateNow func(ctx context.Context) (newVersion string, err error)
UpdateNow func(ctx context.Context, progress func(stage string, pct int, line string)) (newVersion string, err error)
}
type Bamboo struct {
@@ -59,6 +59,10 @@ type Bamboo struct {
updating atomic.Bool
updateMsg string // outcome of the update Hammerhead asked for, reported back
updateID string // the request that updateMsg answers
updStage string // progress of the running update, reported to Hammerhead
updPct int
updLog []string
updSent time.Time
mail map[string]*mailRun
mailErr map[string]string // mail egress ID -> "" (listening) or an error
@@ -260,10 +264,10 @@ func (b *Bamboo) report() {
cancel()
}
b.mu.Lock()
msg, uid := b.updateMsg, b.updateID
msg, uid, stage, pct, ulog := b.updateMsg, b.updateID, b.updStage, b.updPct, append([]string(nil), b.updLog...)
b.mu.Unlock()
body, _ := json.Marshal(model.SiteReport{PublicKey: b.wgPub, Connected: ids, Containers: containers, MailEgress: b.mailStatus(),
Version: b.cfg.Version, AutoUpdate: b.cfg.AutoUpdate, UpdateMsg: msg, UpdateID: uid})
Version: b.cfg.Version, AutoUpdate: b.cfg.AutoUpdate, UpdateMsg: msg, UpdateID: uid, UpdateStage: stage, UpdatePct: pct, UpdateLog: ulog})
req, _ := http.NewRequest("POST", b.cfg.Hammerhead+"/site/report", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+b.cfg.Token)
req.Header.Set("Content-Type", "application/json")
@@ -440,19 +444,25 @@ func (b *Bamboo) requestUpdate(id string) {
return // already handled, or in progress
}
b.updateID, b.updateMsg = id, ""
b.updStage, b.updPct, b.updLog = "starting", 0, nil
b.updNote("Update requested from the dashboard")
if !b.cfg.AutoUpdate || b.cfg.UpdateNow == nil {
b.updateMsg = "auto-update is off on this machine (set BAMBOO_AUTO_UPDATE=1 and restart it)"
b.updStage = "failed"
b.updNote("Refused: " + b.updateMsg)
go b.report()
return
}
if !b.updating.CompareAndSwap(false, true) {
b.updateMsg = "another update is already running"
b.updStage = "failed"
b.updNote("Refused: " + b.updateMsg)
go b.report()
return
}
go func() {
defer b.updating.Store(false)
tag, err := b.cfg.UpdateNow(b.ctx) // on success this restarts the process and does not return
tag, err := b.cfg.UpdateNow(b.ctx, b.updProgress) // on success this restarts the process and does not return
b.mu.Lock()
switch {
case err != nil:
@@ -466,3 +476,32 @@ func (b *Bamboo) requestUpdate(id string) {
b.report()
}()
}
// updNote adds a timestamped line to the update log. Caller holds b.mu.
func (b *Bamboo) updNote(line string) {
b.updLog = append(b.updLog, time.Now().Format("15:04:05")+" "+line)
if len(b.updLog) > 200 {
b.updLog = b.updLog[len(b.updLog)-200:]
}
}
// updProgress is called by the updater as it works; Hammerhead gets a fresh report at most once a
// second (and straight away for every new log line), so its progress bar follows along.
func (b *Bamboo) updProgress(stage string, pct int, line string) {
b.mu.Lock()
b.updStage = stage
if pct >= 0 {
b.updPct = pct
}
if line != "" {
b.updNote(line)
}
send := line != "" || time.Since(b.updSent) > time.Second
if send {
b.updSent = time.Now()
}
b.mu.Unlock()
if send {
go b.report()
}
}
+5 -1
View File
@@ -55,7 +55,11 @@ func main() {
}
b, err := bamboo.New(bamboo.Config{Hammerhead: strings.TrimRight(*hh, "/"), Token: *token, DataDir: *data, Docker: *dockerOn,
Version: version, AutoUpdate: *autoUpdate,
UpdateNow: func(ctx context.Context) (string, error) { return selfupdate.CheckOnce(ctx, upd) }})
UpdateNow: func(ctx context.Context, progress func(string, int, string)) (string, error) {
c := upd
c.Progress = func(e selfupdate.Event) { progress(e.Stage, e.Pct, e.Line) }
return selfupdate.CheckOnce(ctx, c)
}})
if err != nil {
log.Fatal(err)
}
+1 -1
View File
@@ -2,7 +2,7 @@ module git.veillax.com/Veillax/bamboo
go 1.27.3
require git.veillax.com/Veillax/reef v0.1.6
require git.veillax.com/Veillax/reef v0.1.7
require (
github.com/Microsoft/go-winio v0.6.4 // indirect
+2
View File
@@ -6,6 +6,8 @@ git.veillax.com/Veillax/reef v0.1.5 h1:BydjLJsfGgU4zEIqUsePJRjPi9NgbFdmvq0M8dvJC
git.veillax.com/Veillax/reef v0.1.5/go.mod h1:ZBUd/BARO5kkpUNTP81TTY1aQXLHFR6bKDhCxckjHso=
git.veillax.com/Veillax/reef v0.1.6 h1:NgZ4u2Z3hJ+BYD/zX9lov8fox4s2i8txtYNOpIsikVo=
git.veillax.com/Veillax/reef v0.1.6/go.mod h1:ZBUd/BARO5kkpUNTP81TTY1aQXLHFR6bKDhCxckjHso=
git.veillax.com/Veillax/reef v0.1.7 h1:5C2udonlJac2xvDOE5e+zDxej+AbZ9ttrNSE4owmwuU=
git.veillax.com/Veillax/reef v0.1.7/go.mod h1:ZBUd/BARO5kkpUNTP81TTY1aQXLHFR6bKDhCxckjHso=
github.com/Microsoft/go-winio v0.6.4 h1:kJF2pamtYCWXf0xAT8sAkwKC0pMl2HYBrRPHAqyUc+g=
github.com/Microsoft/go-winio v0.6.4/go.mod h1:1xl5JXG4ze/m74GH6Dc4Mwxfc3VL5siRVQjIAlHH07U=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=