tailscale/kube/localclient/local-client.go
Brad Fitzpatrick 9f343fdc0c client/local, ipn/localapi, all: add CertDomains and DNSConfig accessors
Add two narrow LocalAPI accessors so callers don't have to subscribe to
the IPN bus and pull a full *netmap.NetworkMap just to read DNS-shaped
fields:

  - GET /localapi/v0/cert-domains returns DNS.CertDomains.
  - GET /localapi/v0/dns-config returns the full tailcfg.DNSConfig.

Migrate in-tree callers off the netmap-on-the-bus pattern:

  - kube/certs.waitForCertDomain still wakes on the IPN bus but now
    queries CertDomains via LocalClient.CertDomains rather than
    reading n.NetMap.DNS.CertDomains. The kube LocalClient interface
    and FakeLocalClient gain a CertDomains method.
  - cmd/tailscale dns status calls LocalClient.DNSConfig directly
    instead of opening a NotifyInitialNetMap watcher.
  - cmd/tailscale configure kubeconfig switches from a netmap watcher
    + serviceDNSRecordFromNetMap to LocalClient.DNSConfig +
    serviceDNSRecordFromDNSConfig.

This is part of a series moving callers away from depending on the
netmap traveling on the IPN bus, so the bus payload can shrink in a
later change.

Updates #12542

Change-Id: Ie10204e141d085fbac183b4cfe497226b670ad6c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2026-04-30 13:50:46 -07:00

65 lines
1.8 KiB
Go

// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
// Package localclient provides an interface for all the local.Client methods
// kube needs to use, so that we can easily mock it in tests.
package localclient
import (
"context"
"io"
"tailscale.com/client/local"
"tailscale.com/ipn"
)
// LocalClient is roughly a subset of the local.Client struct's methods, used
// 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)
CertDomains(ctx context.Context) ([]string, error)
CertIssuer
}
// IPNBusWatcher is local.IPNBusWatcher's methods restated in an interface to
// allow for easier mocking in tests.
type IPNBusWatcher interface {
io.Closer
Next() (ipn.Notify, error)
}
type CertIssuer interface {
CertPair(context.Context, string) ([]byte, []byte, error)
}
// New returns a LocalClient that wraps the provided local.Client.
func New(lc *local.Client) LocalClient {
return &localClient{lc: lc}
}
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)
}
func (lc *localClient) CertPair(ctx context.Context, domain string) ([]byte, []byte, error) {
return lc.lc.CertPair(ctx, domain)
}
func (lc *localClient) CertDomains(ctx context.Context) ([]string, error) {
return lc.lc.CertDomains(ctx)
}