// Copyright (c) Tailscale Inc & contributors // SPDX-License-Identifier: BSD-3-Clause package tsclient import ( "errors" "fmt" "sync" ) type ( // The Provider type is used to manage multiple Client implementations for different tailnets. Provider struct { defaultClient Client mu sync.RWMutex clients map[string]Client readiness map[string]bool } ) var ( // ErrClientNotFound is the error given when calling Provider.For with a tailnet that has not yet been registered // with the provider. ErrClientNotFound = errors.New("client not found") // ErrNotReady is the error given when calling Provider.For with a tailnet that has not yet been declared as // ready to use by the operator. ErrNotReady = errors.New("tailnet not ready") ) // NewProvider returns a new instance of the Provider type that uses the given Client implementation as the default // client. This client will be given when calling Provider.For with a blank tailnet name. func NewProvider(defaultClient Client) *Provider { return &Provider{ defaultClient: defaultClient, clients: make(map[string]Client), readiness: make(map[string]bool), } } // Add a Client implementation for a given tailnet. func (p *Provider) Add(tailnet string, client Client, ready bool) { p.mu.Lock() defer p.mu.Unlock() p.clients[tailnet] = client p.readiness[tailnet] = ready } // Remove the Client implementation associated with the given tailnet. func (p *Provider) Remove(tailnet string) { p.mu.Lock() defer p.mu.Unlock() delete(p.clients, tailnet) } // For returns a Client implementation associated with the given tailnet. Returns ErrClientNotFound if the given // tailnet does not exist. Use a blank tailnet name to obtain the default Client. func (p *Provider) For(tailnet string) (Client, error) { if tailnet == "" { return p.defaultClient, nil } p.mu.RLock() defer p.mu.RUnlock() if client, ok := p.clients[tailnet]; ok { if ready, _ := p.readiness[tailnet]; !ready { return nil, fmt.Errorf("%w: %s", ErrNotReady, tailnet) } return client, nil } return nil, fmt.Errorf("%w: %s", ErrClientNotFound, tailnet) }