status.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package controlclient
  4. import (
  5. "encoding/json"
  6. "fmt"
  7. "reflect"
  8. "tailscale.com/types/netmap"
  9. "tailscale.com/types/persist"
  10. "tailscale.com/types/structs"
  11. )
  12. // State is the high-level state of the client. It is used only in
  13. // unit tests for proper sequencing, don't depend on it anywhere else.
  14. //
  15. // TODO(apenwarr): eliminate the state, as it's now obsolete.
  16. //
  17. // apenwarr: Historical note: controlclient.Auto was originally
  18. // intended to be the state machine for the whole tailscale client, but that
  19. // turned out to not be the right abstraction layer, and it moved to
  20. // ipn.Backend. Since ipn.Backend now has a state machine, it would be
  21. // much better if controlclient could be a simple stateless API. But the
  22. // current server-side API (two interlocking polling https calls) makes that
  23. // very hard to implement. A server side API change could untangle this and
  24. // remove all the statefulness.
  25. type State int
  26. const (
  27. StateNew = State(iota)
  28. StateNotAuthenticated
  29. StateAuthenticating
  30. StateURLVisitRequired
  31. StateAuthenticated
  32. StateSynchronized // connected and received map update
  33. )
  34. func (s State) AppendText(b []byte) ([]byte, error) {
  35. return append(b, s.String()...), nil
  36. }
  37. func (s State) MarshalText() ([]byte, error) {
  38. return []byte(s.String()), nil
  39. }
  40. func (s State) String() string {
  41. switch s {
  42. case StateNew:
  43. return "state:new"
  44. case StateNotAuthenticated:
  45. return "state:not-authenticated"
  46. case StateAuthenticating:
  47. return "state:authenticating"
  48. case StateURLVisitRequired:
  49. return "state:url-visit-required"
  50. case StateAuthenticated:
  51. return "state:authenticated"
  52. case StateSynchronized:
  53. return "state:synchronized"
  54. default:
  55. return fmt.Sprintf("state:unknown:%d", int(s))
  56. }
  57. }
  58. type Status struct {
  59. _ structs.Incomparable
  60. // Err, if non-nil, is an error that occurred while logging in.
  61. //
  62. // If it's of type UserVisibleError then it's meant to be shown to users in
  63. // their Tailscale client. Otherwise it's just logged to tailscaled's logs.
  64. Err error
  65. // URL, if non-empty, is the interactive URL to visit to finish logging in.
  66. URL string
  67. // NetMap is the latest server-pushed state of the tailnet network.
  68. NetMap *netmap.NetworkMap
  69. // Persist, when Valid, is the locally persisted configuration.
  70. //
  71. // TODO(bradfitz,maisem): clarify this.
  72. Persist persist.PersistView
  73. // state is the internal state. It should not be exposed outside this
  74. // package, but we have some automated tests elsewhere that need to
  75. // use it via the StateForTest accessor.
  76. // TODO(apenwarr): Unexport or remove these.
  77. state State
  78. }
  79. // LoginFinished reports whether the controlclient is in its "StateAuthenticated"
  80. // state where it's in a happy register state but not yet in a map poll.
  81. //
  82. // TODO(bradfitz): delete this and everything around Status.state.
  83. func (s *Status) LoginFinished() bool { return s.state == StateAuthenticated }
  84. // StateForTest returns the internal state of s for tests only.
  85. func (s *Status) StateForTest() State { return s.state }
  86. // SetStateForTest sets the internal state of s for tests only.
  87. func (s *Status) SetStateForTest(state State) { s.state = state }
  88. // Equal reports whether s and s2 are equal.
  89. func (s *Status) Equal(s2 *Status) bool {
  90. if s == nil && s2 == nil {
  91. return true
  92. }
  93. return s != nil && s2 != nil &&
  94. s.Err == s2.Err &&
  95. s.URL == s2.URL &&
  96. s.state == s2.state &&
  97. reflect.DeepEqual(s.Persist, s2.Persist) &&
  98. reflect.DeepEqual(s.NetMap, s2.NetMap)
  99. }
  100. func (s Status) String() string {
  101. b, err := json.MarshalIndent(s, "", "\t")
  102. if err != nil {
  103. panic(err)
  104. }
  105. return s.state.String() + " " + string(b)
  106. }