mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 08:11:32 +01:00 
			
		
		
		
	This eventually allows encoding packages that may respect the proposed encoding.TextAppender interface. The performance gains from this is between 10-30%. Updates tailscale/corp#14379 Signed-off-by: Joe Tsai <joetsai@digital-static.net>
		
			
				
	
	
		
			126 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) Tailscale Inc & AUTHORS
 | |
| // SPDX-License-Identifier: BSD-3-Clause
 | |
| 
 | |
| package controlclient
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 
 | |
| 	"tailscale.com/types/netmap"
 | |
| 	"tailscale.com/types/persist"
 | |
| 	"tailscale.com/types/structs"
 | |
| )
 | |
| 
 | |
| // State is the high-level state of the client. It is used only in
 | |
| // unit tests for proper sequencing, don't depend on it anywhere else.
 | |
| //
 | |
| // TODO(apenwarr): eliminate the state, as it's now obsolete.
 | |
| //
 | |
| // apenwarr: Historical note: controlclient.Auto was originally
 | |
| // intended to be the state machine for the whole tailscale client, but that
 | |
| // turned out to not be the right abstraction layer, and it moved to
 | |
| // ipn.Backend. Since ipn.Backend now has a state machine, it would be
 | |
| // much better if controlclient could be a simple stateless API. But the
 | |
| // current server-side API (two interlocking polling https calls) makes that
 | |
| // very hard to implement. A server side API change could untangle this and
 | |
| // remove all the statefulness.
 | |
| type State int
 | |
| 
 | |
| const (
 | |
| 	StateNew = State(iota)
 | |
| 	StateNotAuthenticated
 | |
| 	StateAuthenticating
 | |
| 	StateURLVisitRequired
 | |
| 	StateAuthenticated
 | |
| 	StateSynchronized // connected and received map update
 | |
| )
 | |
| 
 | |
| func (s State) AppendText(b []byte) ([]byte, error) {
 | |
| 	return append(b, s.String()...), nil
 | |
| }
 | |
| 
 | |
| func (s State) MarshalText() ([]byte, error) {
 | |
| 	return []byte(s.String()), nil
 | |
| }
 | |
| 
 | |
| func (s State) String() string {
 | |
| 	switch s {
 | |
| 	case StateNew:
 | |
| 		return "state:new"
 | |
| 	case StateNotAuthenticated:
 | |
| 		return "state:not-authenticated"
 | |
| 	case StateAuthenticating:
 | |
| 		return "state:authenticating"
 | |
| 	case StateURLVisitRequired:
 | |
| 		return "state:url-visit-required"
 | |
| 	case StateAuthenticated:
 | |
| 		return "state:authenticated"
 | |
| 	case StateSynchronized:
 | |
| 		return "state:synchronized"
 | |
| 	default:
 | |
| 		return fmt.Sprintf("state:unknown:%d", int(s))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type Status struct {
 | |
| 	_ structs.Incomparable
 | |
| 
 | |
| 	// Err, if non-nil, is an error that occurred while logging in.
 | |
| 	//
 | |
| 	// If it's of type UserVisibleError then it's meant to be shown to users in
 | |
| 	// their Tailscale client. Otherwise it's just logged to tailscaled's logs.
 | |
| 	Err error
 | |
| 
 | |
| 	// URL, if non-empty, is the interactive URL to visit to finish logging in.
 | |
| 	URL string
 | |
| 
 | |
| 	// NetMap is the latest server-pushed state of the tailnet network.
 | |
| 	NetMap *netmap.NetworkMap
 | |
| 
 | |
| 	// Persist, when Valid, is the locally persisted configuration.
 | |
| 	//
 | |
| 	// TODO(bradfitz,maisem): clarify this.
 | |
| 	Persist persist.PersistView
 | |
| 
 | |
| 	// state is the internal state. It should not be exposed outside this
 | |
| 	// package, but we have some automated tests elsewhere that need to
 | |
| 	// use it via the StateForTest accessor.
 | |
| 	// TODO(apenwarr): Unexport or remove these.
 | |
| 	state State
 | |
| }
 | |
| 
 | |
| // LoginFinished reports whether the controlclient is in its "StateAuthenticated"
 | |
| // state where it's in a happy register state but not yet in a map poll.
 | |
| //
 | |
| // TODO(bradfitz): delete this and everything around Status.state.
 | |
| func (s *Status) LoginFinished() bool { return s.state == StateAuthenticated }
 | |
| 
 | |
| // StateForTest returns the internal state of s for tests only.
 | |
| func (s *Status) StateForTest() State { return s.state }
 | |
| 
 | |
| // SetStateForTest sets the internal state of s for tests only.
 | |
| func (s *Status) SetStateForTest(state State) { s.state = state }
 | |
| 
 | |
| // Equal reports whether s and s2 are equal.
 | |
| func (s *Status) Equal(s2 *Status) bool {
 | |
| 	if s == nil && s2 == nil {
 | |
| 		return true
 | |
| 	}
 | |
| 	return s != nil && s2 != nil &&
 | |
| 		s.Err == s2.Err &&
 | |
| 		s.URL == s2.URL &&
 | |
| 		s.state == s2.state &&
 | |
| 		reflect.DeepEqual(s.Persist, s2.Persist) &&
 | |
| 		reflect.DeepEqual(s.NetMap, s2.NetMap)
 | |
| }
 | |
| 
 | |
| func (s Status) String() string {
 | |
| 	b, err := json.MarshalIndent(s, "", "\t")
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	return s.state.String() + " " + string(b)
 | |
| }
 |