status.go 3.1 KB

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