status.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  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/empty"
  9. "tailscale.com/types/netmap"
  10. "tailscale.com/types/persist"
  11. "tailscale.com/types/structs"
  12. )
  13. // State is the high-level state of the client. It is used only in
  14. // unit tests for proper sequencing, don't depend on it anywhere else.
  15. //
  16. // TODO(apenwarr): eliminate the state, as it's now obsolete.
  17. //
  18. // apenwarr: Historical note: controlclient.Auto was originally
  19. // intended to be the state machine for the whole tailscale client, but that
  20. // turned out to not be the right abstraction layer, and it moved to
  21. // ipn.Backend. Since ipn.Backend now has a state machine, it would be
  22. // much better if controlclient could be a simple stateless API. But the
  23. // current server-side API (two interlocking polling https calls) makes that
  24. // very hard to implement. A server side API change could untangle this and
  25. // remove all the statefulness.
  26. type State int
  27. const (
  28. StateNew = State(iota)
  29. StateNotAuthenticated
  30. StateAuthenticating
  31. StateURLVisitRequired
  32. StateAuthenticated
  33. StateSynchronized // connected and received map update
  34. )
  35. func (s State) MarshalText() ([]byte, error) {
  36. return []byte(s.String()), nil
  37. }
  38. func (s State) String() string {
  39. switch s {
  40. case StateNew:
  41. return "state:new"
  42. case StateNotAuthenticated:
  43. return "state:not-authenticated"
  44. case StateAuthenticating:
  45. return "state:authenticating"
  46. case StateURLVisitRequired:
  47. return "state:url-visit-required"
  48. case StateAuthenticated:
  49. return "state:authenticated"
  50. case StateSynchronized:
  51. return "state:synchronized"
  52. default:
  53. return fmt.Sprintf("state:unknown:%d", int(s))
  54. }
  55. }
  56. type Status struct {
  57. _ structs.Incomparable
  58. LoginFinished *empty.Message // nonempty when login finishes
  59. LogoutFinished *empty.Message // nonempty when logout finishes
  60. Err error
  61. URL string // interactive URL to visit to finish logging in
  62. NetMap *netmap.NetworkMap // server-pushed configuration
  63. // The internal state should not be exposed outside this
  64. // package, but we have some automated tests elsewhere that need to
  65. // use them. Please don't use these fields.
  66. // TODO(apenwarr): Unexport or remove these.
  67. State State
  68. Persist *persist.PersistView // locally persisted configuration
  69. }
  70. // Equal reports whether s and s2 are equal.
  71. func (s *Status) Equal(s2 *Status) bool {
  72. if s == nil && s2 == nil {
  73. return true
  74. }
  75. return s != nil && s2 != nil &&
  76. (s.LoginFinished == nil) == (s2.LoginFinished == nil) &&
  77. (s.LogoutFinished == nil) == (s2.LogoutFinished == nil) &&
  78. s.Err == s2.Err &&
  79. s.URL == s2.URL &&
  80. reflect.DeepEqual(s.Persist, s2.Persist) &&
  81. reflect.DeepEqual(s.NetMap, s2.NetMap) &&
  82. s.State == s2.State
  83. }
  84. func (s Status) String() string {
  85. b, err := json.MarshalIndent(s, "", "\t")
  86. if err != nil {
  87. panic(err)
  88. }
  89. return s.State.String() + " " + string(b)
  90. }