status.go 3.0 KB

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