cmd/containerboot,kube: enable autoadvertisement of Tailscale services on containerboot (#18527)

* cmd/containerboot,kube/services: support the ability to automatically advertise services on startup

Updates #17769

Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>

* cmd/containerboot: don't assume we want to use kube state store if in kubernetes

Fixes #8188

Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>

---------

Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
This commit is contained in:
Tom Meadows
2026-02-20 15:52:34 -08:00
committed by GitHub
parent 2d64c0dab3
commit 8890c3c413
12 changed files with 349 additions and 65 deletions
+23
View File
@@ -12,6 +12,29 @@ import (
type FakeLocalClient struct {
FakeIPNBusWatcher
SetServeCalled bool
EditPrefsCalls []*ipn.MaskedPrefs
GetPrefsResult *ipn.Prefs
}
func (m *FakeLocalClient) SetServeConfig(ctx context.Context, cfg *ipn.ServeConfig) error {
m.SetServeCalled = true
return nil
}
func (m *FakeLocalClient) EditPrefs(ctx context.Context, mp *ipn.MaskedPrefs) (*ipn.Prefs, error) {
m.EditPrefsCalls = append(m.EditPrefsCalls, mp)
if m.GetPrefsResult == nil {
return &ipn.Prefs{}, nil
}
return m.GetPrefsResult, nil
}
func (m *FakeLocalClient) GetPrefs(ctx context.Context) (*ipn.Prefs, error) {
if m.GetPrefsResult == nil {
return &ipn.Prefs{}, nil
}
return m.GetPrefsResult, nil
}
func (f *FakeLocalClient) WatchIPNBus(ctx context.Context, mask ipn.NotifyWatchOpt) (IPNBusWatcher, error) {
+10
View File
@@ -17,6 +17,8 @@ import (
// for easier testing.
type LocalClient interface {
WatchIPNBus(ctx context.Context, mask ipn.NotifyWatchOpt) (IPNBusWatcher, error)
SetServeConfig(context.Context, *ipn.ServeConfig) error
EditPrefs(ctx context.Context, mp *ipn.MaskedPrefs) (*ipn.Prefs, error)
CertIssuer
}
@@ -40,6 +42,14 @@ type localClient struct {
lc *local.Client
}
func (lc *localClient) SetServeConfig(ctx context.Context, config *ipn.ServeConfig) error {
return lc.lc.SetServeConfig(ctx, config)
}
func (lc *localClient) EditPrefs(ctx context.Context, mp *ipn.MaskedPrefs) (*ipn.Prefs, error) {
return lc.lc.EditPrefs(ctx, mp)
}
func (lc *localClient) WatchIPNBus(ctx context.Context, mask ipn.NotifyWatchOpt) (IPNBusWatcher, error) {
return lc.lc.WatchIPNBus(ctx, mask)
}
+37
View File
@@ -12,9 +12,46 @@ import (
"tailscale.com/client/local"
"tailscale.com/ipn"
"tailscale.com/kube/localclient"
"tailscale.com/types/logger"
)
// EnsureServicesAdvertised is a function that gets called on containerboot
// startup and ensures that Services get advertised if they exist.
func EnsureServicesAdvertised(ctx context.Context, services []string, lc localclient.LocalClient, logf logger.Logf) error {
if _, err := lc.EditPrefs(ctx, &ipn.MaskedPrefs{
AdvertiseServicesSet: true,
Prefs: ipn.Prefs{
AdvertiseServices: services,
},
}); err != nil {
// EditPrefs only returns an error if it fails _set_ its local prefs.
// If it fails to _persist_ the prefs in state, we don't get an error
// and we continue waiting below, as control will failover as usual.
return fmt.Errorf("error setting prefs AdvertiseServices: %w", err)
}
// Services use the same (failover XOR regional routing) mechanism that
// HA subnet routers use. Unfortunately we don't yet get a reliable signal
// from control that it's responded to our unadvertisement, so the best we
// can do is wait for 20 seconds, where 15s is the approximate maximum time
// it should take for control to choose a new primary, and 5s is for buffer.
//
// Note: There is no guarantee that clients have been _informed_ of the new
// primary no matter how long we wait. We would need a mechanism to await
// netmap updates for peers to know for sure.
//
// See https://tailscale.com/kb/1115/high-availability for more details.
// TODO(tomhjp): Wait for a netmap update instead of sleeping when control
// supports that.
select {
case <-ctx.Done():
return nil
case <-time.After(20 * time.Second):
return nil
}
}
// EnsureServicesNotAdvertised is a function that gets called on containerboot
// or k8s-proxy termination and ensures that any currently advertised Services
// get unadvertised to give clients time to switch to another node before this