mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
d0a56a8870
containerboot's main.go had grown to well over 1000 lines with lots of disparate bits of functionality. This commit is pure copy- paste to group related functionality outside of the main function into its own set of files. Everything is still in the main package to keep the diff incremental and reviewable. Updates #cleanup Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
97 lines
2.5 KiB
Go
97 lines
2.5 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build linux
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
"tailscale.com/client/tailscale"
|
|
"tailscale.com/ipn"
|
|
)
|
|
|
|
// watchServeConfigChanges watches path for changes, and when it sees one, reads
|
|
// the serve config from it, replacing ${TS_CERT_DOMAIN} with certDomain, and
|
|
// applies it to lc. It exits when ctx is canceled. cdChanged is a channel that
|
|
// is written to when the certDomain changes, causing the serve config to be
|
|
// re-read and applied.
|
|
func watchServeConfigChanges(ctx context.Context, path string, cdChanged <-chan bool, certDomainAtomic *atomic.Pointer[string], lc *tailscale.LocalClient) {
|
|
if certDomainAtomic == nil {
|
|
panic("cd must not be nil")
|
|
}
|
|
var tickChan <-chan time.Time
|
|
var eventChan <-chan fsnotify.Event
|
|
if w, err := fsnotify.NewWatcher(); err != nil {
|
|
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()
|
|
if err := w.Add(filepath.Dir(path)); err != nil {
|
|
log.Fatalf("failed to add fsnotify watch: %v", err)
|
|
}
|
|
eventChan = w.Events
|
|
}
|
|
|
|
var certDomain string
|
|
var prevServeConfig *ipn.ServeConfig
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-cdChanged:
|
|
certDomain = *certDomainAtomic.Load()
|
|
case <-tickChan:
|
|
case <-eventChan:
|
|
// 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 == "" {
|
|
continue
|
|
}
|
|
sc, err := readServeConfig(path, certDomain)
|
|
if err != nil {
|
|
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 {
|
|
log.Fatalf("failed to set serve config: %v", err)
|
|
}
|
|
prevServeConfig = sc
|
|
}
|
|
}
|
|
|
|
// readServeConfig reads the ipn.ServeConfig from path, replacing
|
|
// ${TS_CERT_DOMAIN} with certDomain.
|
|
func readServeConfig(path, certDomain string) (*ipn.ServeConfig, error) {
|
|
if path == "" {
|
|
return nil, nil
|
|
}
|
|
j, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
j = bytes.ReplaceAll(j, []byte("${TS_CERT_DOMAIN}"), []byte(certDomain))
|
|
var sc ipn.ServeConfig
|
|
if err := json.Unmarshal(j, &sc); err != nil {
|
|
return nil, err
|
|
}
|
|
return &sc, nil
|
|
}
|