diff --git a/bamboo.go b/bamboo.go index 0d3c190..7cadaf7 100644 --- a/bamboo.go +++ b/bamboo.go @@ -21,6 +21,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" "git.veillax.com/Veillax/reef/docker" @@ -34,6 +35,11 @@ type Config struct { Token string DataDir string // holds this agent's WireGuard key Docker bool // report local Docker containers so they can be picked as targets + + // Version, AutoUpdate and UpdateNow work as in blacktip.Config. + Version string + AutoUpdate bool + UpdateNow func(ctx context.Context) (newVersion string, err error) } type Bamboo struct { @@ -50,6 +56,10 @@ type Bamboo struct { edges []model.EdgeRef lastRep string + updating atomic.Bool + updateMsg string // outcome of the update Hammerhead asked for, reported back + updateID string // the request that updateMsg answers + mail map[string]*mailRun mailErr map[string]string // mail egress ID -> "" (listening) or an error } @@ -143,6 +153,9 @@ func (b *Bamboo) apply(cfg *model.SiteConfig) { b.mu.Lock() b.version = cfg.Version b.edges = cfg.Edges + if cfg.UpdateID != "" { + b.requestUpdate(cfg.UpdateID) + } if cfg.TunnelIP != b.tunIP && b.tun != nil { b.tun.Close() b.tun = nil @@ -246,7 +259,11 @@ func (b *Bamboo) report() { containers, _ = docker.List(ctx) cancel() } - body, _ := json.Marshal(model.SiteReport{PublicKey: b.wgPub, Connected: ids, Containers: containers, MailEgress: b.mailStatus()}) + b.mu.Lock() + msg, uid := b.updateMsg, b.updateID + 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}) 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") @@ -414,3 +431,38 @@ func serveUDP(pc net.PacketConn) { } } } + +// requestUpdate acts on "update now" from Hammerhead (id identifies the request). It only ever installs +// what this machine's own auto-update would (the newest release from its configured server), and only +// when auto-update is on. Caller holds b.mu. +func (b *Bamboo) requestUpdate(id string) { + if id == b.updateID { + return // already handled, or in progress + } + b.updateID, b.updateMsg = id, "" + 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)" + go b.report() + return + } + if !b.updating.CompareAndSwap(false, true) { + b.updateMsg = "another update is already running" + 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 + b.mu.Lock() + switch { + case err != nil: + b.updateMsg = "update failed: " + err.Error() + case tag == "": + b.updateMsg = "already on the newest release" + default: + b.updateMsg = "updated to " + tag + ", restarting" + } + b.mu.Unlock() + b.report() + }() +} diff --git a/cmd/bamboo/main.go b/cmd/bamboo/main.go index 194e9c4..2f63e42 100644 --- a/cmd/bamboo/main.go +++ b/cmd/bamboo/main.go @@ -49,10 +49,13 @@ func main() { log.Printf("bamboo %s starting", version) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() + upd := selfupdate.Config{Server: "https://git.veillax.com", Repo: "Veillax/bamboo", App: "bamboo", Current: version, Logf: log.Printf} if *autoUpdate { - go selfupdate.Run(ctx, selfupdate.Config{Server: "https://git.veillax.com", Repo: "Veillax/bamboo", App: "bamboo", Current: version, Logf: log.Printf}) + go selfupdate.Run(ctx, upd) } - b, err := bamboo.New(bamboo.Config{Hammerhead: strings.TrimRight(*hh, "/"), Token: *token, DataDir: *data, Docker: *dockerOn}) + 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) }}) if err != nil { log.Fatal(err) } diff --git a/go.mod b/go.mod index 949f9c8..efcec2f 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module git.veillax.com/Veillax/bamboo go 1.27.3 -require git.veillax.com/Veillax/reef v0.1.3 +require git.veillax.com/Veillax/reef v0.1.6 require ( github.com/Microsoft/go-winio v0.6.4 // indirect diff --git a/go.sum b/go.sum index 4e37399..630ec7c 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,10 @@ git.veillax.com/Veillax/reef v0.1.0 h1:O0OhEKFx2CsvACJ20sfzJNjGsngomtfDdKdxu4ROp git.veillax.com/Veillax/reef v0.1.0/go.mod h1:ZBUd/BARO5kkpUNTP81TTY1aQXLHFR6bKDhCxckjHso= git.veillax.com/Veillax/reef v0.1.3 h1:RxSq2xwaup1+MieAkhlvOTZnYwPf19thFHEAe/7VxsI= git.veillax.com/Veillax/reef v0.1.3/go.mod h1:ZBUd/BARO5kkpUNTP81TTY1aQXLHFR6bKDhCxckjHso= +git.veillax.com/Veillax/reef v0.1.5 h1:BydjLJsfGgU4zEIqUsePJRjPi9NgbFdmvq0M8dvJCgM= +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= 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=