cmd/containerboot: account for k8s secret reflection in fsnotify

On k8s the serve-config secret mount is symlinked so checking against
the Name makes us miss the events.

Updates #7895

Signed-off-by: Maisem Ali <maisem@tailscale.com>
raggi/netfilter-runtime
Maisem Ali 10 months ago committed by Maisem Ali
parent ce5909dafc
commit 9430481926

@ -40,7 +40,8 @@
// - TS_SERVE_CONFIG: if specified, is the file path where the ipn.ServeConfig is located. // - TS_SERVE_CONFIG: if specified, is the file path where the ipn.ServeConfig is located.
// It will be applied once tailscaled is up and running. If the file contains // It will be applied once tailscaled is up and running. If the file contains
// ${TS_CERT_DOMAIN}, it will be replaced with the value of the available FQDN. // ${TS_CERT_DOMAIN}, it will be replaced with the value of the available FQDN.
// It cannot be used in conjunction with TS_DEST_IP. // It cannot be used in conjunction with TS_DEST_IP. The file is watched for changes,
// and will be re-applied when it changes.
// //
// When running on Kubernetes, containerboot defaults to storing state in the // When running on Kubernetes, containerboot defaults to storing state in the
// "tailscale" kube secret. To store state on local disk instead, set // "tailscale" kube secret. To store state on local disk instead, set
@ -64,6 +65,7 @@ import (
"os/exec" "os/exec"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"reflect"
"strconv" "strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
@ -358,25 +360,33 @@ func watchServeConfigChanges(ctx context.Context, path string, cdChanged <-chan
if certDomainAtomic == nil { if certDomainAtomic == nil {
panic("cd must not be nil") panic("cd must not be nil")
} }
var tickChan <-chan time.Time
w, err := fsnotify.NewWatcher() w, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
log.Fatalf("failed to create fsnotify watcher: %v", err) log.Printf("failed to create fsnotify watcher, timer-only mode: %v", err)
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
tickChan = ticker.C
} else {
defer w.Close()
} }
defer w.Close()
if err := w.Add(filepath.Dir(path)); err != nil { if err := w.Add(filepath.Dir(path)); err != nil {
log.Fatalf("failed to add fsnotify watch: %v", err) log.Fatalf("failed to add fsnotify watch: %v", err)
} }
var certDomain string var certDomain string
var prevServeConfig *ipn.ServeConfig
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return return
case <-cdChanged: case <-cdChanged:
certDomain = *certDomainAtomic.Load() certDomain = *certDomainAtomic.Load()
case e := <-w.Events: case <-tickChan:
if e.Name != path { case <-w.Events:
continue // We can't do any reasonable filtering on the event because of how
} // k8s handles these mounts. So just re-read the file and apply it
// if it's changed.
} }
if certDomain == "" { if certDomain == "" {
continue continue
@ -385,9 +395,14 @@ func watchServeConfigChanges(ctx context.Context, path string, cdChanged <-chan
if err != nil { if err != nil {
log.Fatalf("failed to read serve config: %v", err) log.Fatalf("failed to read serve config: %v", err)
} }
if prevServeConfig != nil && reflect.DeepEqual(sc, prevServeConfig) {
continue
}
log.Printf("Applying serve config")
if err := lc.SetServeConfig(ctx, sc); err != nil { if err := lc.SetServeConfig(ctx, sc); err != nil {
log.Fatalf("failed to set serve config: %v", err) log.Fatalf("failed to set serve config: %v", err)
} }
prevServeConfig = sc
} }
} }

Loading…
Cancel
Save