ipn/ipnlocal: periodically run auto-updates when "offline" (#12118)

When the client is disconnected from control for any reason (typically
just turned off), we should still attempt to update if auto-updates are
enabled. This may help users who turn tailscale on infrequently for
accessing resources.

RELNOTE: Apply auto-updates even if the node is down or disconnected
from the coordination server.

Updates #12117

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
This commit is contained in:
Andrew Lytvynov
2024-06-03 19:24:53 -07:00
committed by GitHub
parent 2f2f588c80
commit bc4c8b65c7
4 changed files with 155 additions and 52 deletions

View File

@@ -4,7 +4,6 @@
package ipnlocal
import (
"bytes"
"crypto/x509"
"encoding/json"
"encoding/pem"
@@ -307,60 +306,11 @@ func handleC2NUpdatePost(b *LocalBackend, w http.ResponseWriter, r *http.Request
return
}
// Check if update was already started, and mark as started.
if !b.trySetC2NUpdateStarted() {
res.Err = "update already started"
return
}
defer func() {
// Clear the started flag if something failed.
if res.Err != "" {
b.setC2NUpdateStarted(false)
}
}()
cmdTS, err := findCmdTailscale()
if err != nil {
res.Err = fmt.Sprintf("failed to find cmd/tailscale binary: %v", err)
return
}
var ver struct {
Long string `json:"long"`
}
out, err := exec.Command(cmdTS, "version", "--json").Output()
if err != nil {
res.Err = fmt.Sprintf("failed to find cmd/tailscale binary: %v", err)
return
}
if err := json.Unmarshal(out, &ver); err != nil {
res.Err = "invalid JSON from cmd/tailscale version --json"
return
}
if ver.Long != version.Long() {
res.Err = "cmd/tailscale version mismatch"
return
}
cmd := tailscaleUpdateCmd(cmdTS)
buf := new(bytes.Buffer)
cmd.Stdout = buf
cmd.Stderr = buf
b.logf("c2n: running %q", strings.Join(cmd.Args, " "))
if err := cmd.Start(); err != nil {
res.Err = fmt.Sprintf("failed to start cmd/tailscale update: %v", err)
if err := b.startAutoUpdate("c2n"); err != nil {
res.Err = err.Error()
return
}
res.Started = true
// Run update asynchronously and respond that it started.
go func() {
if err := cmd.Wait(); err != nil {
b.logf("c2n: update command failed: %v, output: %s", err, buf)
} else {
b.logf("c2n: update complete")
}
b.setC2NUpdateStarted(false)
}()
}
func handleC2NPostureIdentityGet(b *LocalBackend, w http.ResponseWriter, r *http.Request) {