control/controlclient: replace a status func with Observer interface

For now the method has only one interface (the same as the func it's
replacing) but it will grow, eventually with the goal to remove the
controlclient.Status type for most purposes.

Updates #1909

Change-Id: I715c8bf95e3f5943055a94e76af98d988558a2f2
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/9125/head
Brad Fitzpatrick 1 year ago committed by Brad Fitzpatrick
parent a64593d7ef
commit 55bb7314f2

@ -145,7 +145,7 @@ type Auto struct {
closed bool closed bool
updateCh chan struct{} // readable when we should inform the server of a change updateCh chan struct{} // readable when we should inform the server of a change
newMapCh chan struct{} // readable when we must restart a map request newMapCh chan struct{} // readable when we must restart a map request
statusFunc func(Status) // called to update Client status; always non-nil observer Observer // called to update Client status; always non-nil
unregisterHealthWatch func() unregisterHealthWatch func()
@ -194,8 +194,8 @@ func NewNoStart(opts Options) (_ *Auto, err error) {
} }
}() }()
if opts.Status == nil { if opts.Observer == nil {
return nil, errors.New("missing required Options.Status") return nil, errors.New("missing required Options.Observer")
} }
if opts.Logf == nil { if opts.Logf == nil {
opts.Logf = func(fmt string, args ...any) {} opts.Logf = func(fmt string, args ...any) {}
@ -213,7 +213,7 @@ func NewNoStart(opts Options) (_ *Auto, err error) {
authDone: make(chan struct{}), authDone: make(chan struct{}),
mapDone: make(chan struct{}), mapDone: make(chan struct{}),
updateDone: make(chan struct{}), updateDone: make(chan struct{}),
statusFunc: opts.Status, observer: opts.Observer,
} }
c.authCtx, c.authCancel = context.WithCancel(context.Background()) c.authCtx, c.authCancel = context.WithCancel(context.Background())
c.authCtx = sockstats.WithSockStats(c.authCtx, sockstats.LabelControlClientAuto, opts.Logf) c.authCtx = sockstats.WithSockStats(c.authCtx, sockstats.LabelControlClientAuto, opts.Logf)
@ -669,7 +669,7 @@ func (c *Auto) sendStatus(who string, err error, url string, nm *netmap.NetworkM
State: state, State: state,
Err: err, Err: err,
} }
c.statusFunc(new) c.observer.SetControlClientStatus(new)
c.mu.Lock() c.mu.Lock()
c.inSendStatus-- c.inSendStatus--

@ -101,6 +101,12 @@ type Direct struct {
lastPingURL string // last PingRequest.URL received, for dup suppression lastPingURL string // last PingRequest.URL received, for dup suppression
} }
// Observer is implemented by users of the control client (such as LocalBackend)
// to get notified of changes in the control client's status.
type Observer interface {
SetControlClientStatus(Status)
}
type Options struct { type Options struct {
Persist persist.Persist // initial persistent data Persist persist.Persist // initial persistent data
GetMachinePrivateKey func() (key.MachinePrivate, error) // returns the machine key to use GetMachinePrivateKey func() (key.MachinePrivate, error) // returns the machine key to use
@ -122,8 +128,9 @@ type Options struct {
Dialer *tsdial.Dialer // non-nil Dialer *tsdial.Dialer // non-nil
C2NHandler http.Handler // or nil C2NHandler http.Handler // or nil
// Status is called when there's a change in status. // Observer is called when there's a change in status to report
Status func(Status) // from the control client.
Observer Observer
// SkipIPForwardingCheck declares that the host's IP // SkipIPForwardingCheck declares that the host's IP
// forwarding works and should not be double-checked by the // forwarding works and should not be double-checked by the

@ -888,9 +888,9 @@ func (b *LocalBackend) SetDecompressor(fn func() (controlclient.Decompressor, er
b.newDecompressor = fn b.newDecompressor = fn
} }
// setClientStatus is the callback invoked by the control client whenever it posts a new status. // SetControlClientStatus is the callback invoked by the control client whenever it posts a new status.
// Among other things, this is where we update the netmap, packet filters, DNS and DERP maps. // Among other things, this is where we update the netmap, packet filters, DNS and DERP maps.
func (b *LocalBackend) setClientStatus(st controlclient.Status) { func (b *LocalBackend) SetControlClientStatus(st controlclient.Status) {
// The following do not depend on any data for which we need to lock b. // The following do not depend on any data for which we need to lock b.
if st.Err != nil { if st.Err != nil {
// TODO(crawshaw): display in the UI. // TODO(crawshaw): display in the UI.
@ -947,7 +947,7 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
// Call ourselves with the current status again; the logic in // Call ourselves with the current status again; the logic in
// setClientStatus will take care of updating the expired field // setClientStatus will take care of updating the expired field
// of peers in the netmap. // of peers in the netmap.
b.setClientStatus(st) b.SetControlClientStatus(st)
}) })
} }
} }
@ -1457,7 +1457,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
OnClientVersion: b.onClientVersion, OnClientVersion: b.onClientVersion,
OnControlTime: b.em.onControlTime, OnControlTime: b.em.onControlTime,
Dialer: b.Dialer(), Dialer: b.Dialer(),
Status: b.setClientStatus, Observer: b,
C2NHandler: http.HandlerFunc(b.handleC2N), C2NHandler: http.HandlerFunc(b.handleC2N),
DialPlan: &b.dialPlan, // pointer because it can't be copied DialPlan: &b.dialPlan, // pointer because it can't be copied

@ -29,6 +29,12 @@ import (
"tailscale.com/util/must" "tailscale.com/util/must"
) )
type observerFunc func(controlclient.Status)
func (f observerFunc) SetControlClientStatus(s controlclient.Status) {
f(s)
}
func fakeControlClient(t *testing.T, c *http.Client) *controlclient.Auto { func fakeControlClient(t *testing.T, c *http.Client) *controlclient.Auto {
hi := hostinfo.New() hi := hostinfo.New()
ni := tailcfg.NetInfo{LinkType: "wired"} ni := tailcfg.NetInfo{LinkType: "wired"}
@ -43,7 +49,7 @@ func fakeControlClient(t *testing.T, c *http.Client) *controlclient.Auto {
}, },
HTTPTestClient: c, HTTPTestClient: c,
NoiseTestClient: c, NoiseTestClient: c,
Status: func(controlclient.Status) {}, Observer: observerFunc(func(controlclient.Status) {}),
} }
cc, err := controlclient.NewNoStart(opts) cc, err := controlclient.NewNoStart(opts)

@ -162,7 +162,7 @@ func (cc *mockControl) send(err error, url string, loginFinished bool, nm *netma
cc.authBlocked = false cc.authBlocked = false
cc.mu.Unlock() cc.mu.Unlock()
} }
if cc.opts.Status != nil { if cc.opts.Observer != nil {
pv := cc.persist.View() pv := cc.persist.View()
s := controlclient.Status{ s := controlclient.Status{
URL: url, URL: url,
@ -175,7 +175,7 @@ func (cc *mockControl) send(err error, url string, loginFinished bool, nm *netma
} else if url == "" && err == nil && nm == nil { } else if url == "" && err == nil && nm == nil {
s.LogoutFinished = &empty.Message{} s.LogoutFinished = &empty.Message{}
} }
cc.opts.Status(s) cc.opts.Observer.SetControlClientStatus(s)
} }
} }

Loading…
Cancel
Save