auto.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package controlclient
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "net/http"
  9. "sync"
  10. "sync/atomic"
  11. "time"
  12. "tailscale.com/logtail/backoff"
  13. "tailscale.com/net/sockstats"
  14. "tailscale.com/tailcfg"
  15. "tailscale.com/tstime"
  16. "tailscale.com/types/key"
  17. "tailscale.com/types/logger"
  18. "tailscale.com/types/netmap"
  19. "tailscale.com/types/persist"
  20. "tailscale.com/types/structs"
  21. "tailscale.com/util/execqueue"
  22. )
  23. type LoginGoal struct {
  24. _ structs.Incomparable
  25. flags LoginFlags // flags to use when logging in
  26. url string // auth url that needs to be visited
  27. }
  28. var _ Client = (*Auto)(nil)
  29. // waitUnpause waits until either the client is unpaused or the Auto client is
  30. // shut down. It reports whether the client should keep running (i.e. it's not
  31. // closed).
  32. func (c *Auto) waitUnpause(routineLogName string) (keepRunning bool) {
  33. c.mu.Lock()
  34. if !c.paused || c.closed {
  35. defer c.mu.Unlock()
  36. return !c.closed
  37. }
  38. unpaused := c.unpausedChanLocked()
  39. c.mu.Unlock()
  40. c.logf("%s: awaiting unpause", routineLogName)
  41. return <-unpaused
  42. }
  43. // updateRoutine is responsible for informing the server of worthy changes to
  44. // our local state. It runs in its own goroutine.
  45. func (c *Auto) updateRoutine() {
  46. defer close(c.updateDone)
  47. bo := backoff.NewBackoff("updateRoutine", c.logf, 30*time.Second)
  48. // lastUpdateGenInformed is the value of lastUpdateAt that we've successfully
  49. // informed the server of.
  50. var lastUpdateGenInformed updateGen
  51. for {
  52. if !c.waitUnpause("updateRoutine") {
  53. c.logf("updateRoutine: exiting")
  54. return
  55. }
  56. c.mu.Lock()
  57. gen := c.lastUpdateGen
  58. ctx := c.mapCtx
  59. needUpdate := gen > 0 && gen != lastUpdateGenInformed && c.loggedIn
  60. c.mu.Unlock()
  61. if !needUpdate {
  62. // Nothing to do, wait for a signal.
  63. select {
  64. case <-ctx.Done():
  65. continue
  66. case <-c.updateCh:
  67. continue
  68. }
  69. }
  70. t0 := c.clock.Now()
  71. err := c.direct.SendUpdate(ctx)
  72. d := time.Since(t0).Round(time.Millisecond)
  73. if err != nil {
  74. if ctx.Err() == nil {
  75. c.direct.logf("lite map update error after %v: %v", d, err)
  76. }
  77. bo.BackOff(ctx, err)
  78. continue
  79. }
  80. bo.BackOff(ctx, nil)
  81. c.direct.logf("[v1] successful lite map update in %v", d)
  82. lastUpdateGenInformed = gen
  83. }
  84. }
  85. // atomicGen is an atomic int64 generator. It is used to generate monotonically
  86. // increasing numbers for updateGen.
  87. var atomicGen atomic.Int64
  88. func nextUpdateGen() updateGen {
  89. return updateGen(atomicGen.Add(1))
  90. }
  91. // updateGen is a monotonically increasing number that represents a particular
  92. // update to the local state.
  93. type updateGen int64
  94. // Auto connects to a tailcontrol server for a node.
  95. // It's a concrete implementation of the Client interface.
  96. type Auto struct {
  97. direct *Direct // our interface to the server APIs
  98. clock tstime.Clock
  99. logf logger.Logf
  100. closed bool
  101. updateCh chan struct{} // readable when we should inform the server of a change
  102. observer Observer // called to update Client status; always non-nil
  103. observerQueue execqueue.ExecQueue
  104. unregisterHealthWatch func()
  105. mu sync.Mutex // mutex guards the following fields
  106. wantLoggedIn bool // whether the user wants to be logged in per last method call
  107. urlToVisit string // the last url we were told to visit
  108. expiry time.Time
  109. // lastUpdateGen is the gen of last update we had an update worth sending to
  110. // the server.
  111. lastUpdateGen updateGen
  112. paused bool // whether we should stop making HTTP requests
  113. unpauseWaiters []chan bool // chans that gets sent true (once) on wake, or false on Shutdown
  114. loggedIn bool // true if currently logged in
  115. loginGoal *LoginGoal // non-nil if some login activity is desired
  116. inMapPoll bool // true once we get the first MapResponse in a stream; false when HTTP response ends
  117. state State // TODO(bradfitz): delete this, make it computed by method from other state
  118. authCtx context.Context // context used for auth requests
  119. mapCtx context.Context // context used for netmap and update requests
  120. authCancel func() // cancel authCtx
  121. mapCancel func() // cancel mapCtx
  122. authDone chan struct{} // when closed, authRoutine is done
  123. mapDone chan struct{} // when closed, mapRoutine is done
  124. updateDone chan struct{} // when closed, updateRoutine is done
  125. }
  126. // New creates and starts a new Auto.
  127. func New(opts Options) (*Auto, error) {
  128. c, err := NewNoStart(opts)
  129. if c != nil {
  130. c.Start()
  131. }
  132. return c, err
  133. }
  134. // NewNoStart creates a new Auto, but without calling Start on it.
  135. func NewNoStart(opts Options) (_ *Auto, err error) {
  136. direct, err := NewDirect(opts)
  137. if err != nil {
  138. return nil, err
  139. }
  140. defer func() {
  141. if err != nil {
  142. direct.Close()
  143. }
  144. }()
  145. if opts.Observer == nil {
  146. return nil, errors.New("missing required Options.Observer")
  147. }
  148. if opts.Logf == nil {
  149. opts.Logf = func(fmt string, args ...any) {}
  150. }
  151. if opts.Clock == nil {
  152. opts.Clock = tstime.StdClock{}
  153. }
  154. c := &Auto{
  155. direct: direct,
  156. clock: opts.Clock,
  157. logf: opts.Logf,
  158. updateCh: make(chan struct{}, 1),
  159. authDone: make(chan struct{}),
  160. mapDone: make(chan struct{}),
  161. updateDone: make(chan struct{}),
  162. observer: opts.Observer,
  163. }
  164. c.authCtx, c.authCancel = context.WithCancel(context.Background())
  165. c.authCtx = sockstats.WithSockStats(c.authCtx, sockstats.LabelControlClientAuto, opts.Logf)
  166. c.mapCtx, c.mapCancel = context.WithCancel(context.Background())
  167. c.mapCtx = sockstats.WithSockStats(c.mapCtx, sockstats.LabelControlClientAuto, opts.Logf)
  168. c.unregisterHealthWatch = opts.HealthTracker.RegisterWatcher(direct.ReportHealthChange)
  169. return c, nil
  170. }
  171. // SetPaused controls whether HTTP activity should be paused.
  172. //
  173. // The client can be paused and unpaused repeatedly, unlike Start and Shutdown, which can only be used once.
  174. func (c *Auto) SetPaused(paused bool) {
  175. c.mu.Lock()
  176. defer c.mu.Unlock()
  177. if paused == c.paused || c.closed {
  178. return
  179. }
  180. c.logf("setPaused(%v)", paused)
  181. c.paused = paused
  182. if paused {
  183. c.cancelMapCtxLocked()
  184. c.cancelAuthCtxLocked()
  185. return
  186. }
  187. for _, ch := range c.unpauseWaiters {
  188. ch <- true
  189. }
  190. c.unpauseWaiters = nil
  191. }
  192. // Start starts the client's goroutines.
  193. //
  194. // It should only be called for clients created by NewNoStart.
  195. func (c *Auto) Start() {
  196. go c.authRoutine()
  197. go c.mapRoutine()
  198. go c.updateRoutine()
  199. }
  200. // updateControl sends a new OmitPeers, non-streaming map request (to just send
  201. // Hostinfo/Netinfo/Endpoints info, while keeping an existing streaming response
  202. // open).
  203. //
  204. // It should be called whenever there's something new to tell the server.
  205. func (c *Auto) updateControl() {
  206. gen := nextUpdateGen()
  207. c.mu.Lock()
  208. if gen < c.lastUpdateGen {
  209. // This update is out of date.
  210. c.mu.Unlock()
  211. return
  212. }
  213. c.lastUpdateGen = gen
  214. c.mu.Unlock()
  215. select {
  216. case c.updateCh <- struct{}{}:
  217. default:
  218. }
  219. }
  220. // cancelAuthCtxLocked is like cancelAuthCtx, but assumes the caller holds c.mu.
  221. func (c *Auto) cancelAuthCtxLocked() {
  222. if c.authCancel != nil {
  223. c.authCancel()
  224. }
  225. if !c.closed {
  226. c.authCtx, c.authCancel = context.WithCancel(context.Background())
  227. c.authCtx = sockstats.WithSockStats(c.authCtx, sockstats.LabelControlClientAuto, c.logf)
  228. }
  229. }
  230. // cancelMapCtxLocked is like cancelMapCtx, but assumes the caller holds c.mu.
  231. func (c *Auto) cancelMapCtxLocked() {
  232. if c.mapCancel != nil {
  233. c.mapCancel()
  234. }
  235. if !c.closed {
  236. c.mapCtx, c.mapCancel = context.WithCancel(context.Background())
  237. c.mapCtx = sockstats.WithSockStats(c.mapCtx, sockstats.LabelControlClientAuto, c.logf)
  238. }
  239. }
  240. // restartMap cancels the existing mapPoll and liteUpdates, and then starts a
  241. // new one.
  242. func (c *Auto) restartMap() {
  243. c.mu.Lock()
  244. c.cancelMapCtxLocked()
  245. synced := c.inMapPoll
  246. c.mu.Unlock()
  247. c.logf("[v1] restartMap: synced=%v", synced)
  248. c.updateControl()
  249. }
  250. func (c *Auto) authRoutine() {
  251. defer close(c.authDone)
  252. bo := backoff.NewBackoff("authRoutine", c.logf, 30*time.Second)
  253. for {
  254. if !c.waitUnpause("authRoutine") {
  255. c.logf("authRoutine: exiting")
  256. return
  257. }
  258. c.mu.Lock()
  259. goal := c.loginGoal
  260. ctx := c.authCtx
  261. if goal != nil {
  262. c.logf("[v1] authRoutine: %s; wantLoggedIn=%v", c.state, true)
  263. } else {
  264. c.logf("[v1] authRoutine: %s; goal=nil paused=%v", c.state, c.paused)
  265. }
  266. c.mu.Unlock()
  267. report := func(err error, msg string) {
  268. c.logf("[v1] %s: %v", msg, err)
  269. // don't send status updates for context errors,
  270. // since context cancelation is always on purpose.
  271. if ctx.Err() == nil {
  272. c.sendStatus("authRoutine-report", err, "", nil)
  273. }
  274. }
  275. if goal == nil {
  276. c.direct.health.SetAuthRoutineInError(nil)
  277. // Wait for user to Login or Logout.
  278. <-ctx.Done()
  279. c.logf("[v1] authRoutine: context done.")
  280. continue
  281. }
  282. c.mu.Lock()
  283. c.urlToVisit = goal.url
  284. if goal.url != "" {
  285. c.state = StateURLVisitRequired
  286. } else {
  287. c.state = StateAuthenticating
  288. }
  289. c.mu.Unlock()
  290. var url string
  291. var err error
  292. var f string
  293. if goal.url != "" {
  294. url, err = c.direct.WaitLoginURL(ctx, goal.url)
  295. f = "WaitLoginURL"
  296. } else {
  297. url, err = c.direct.TryLogin(ctx, goal.flags)
  298. f = "TryLogin"
  299. }
  300. if err != nil {
  301. c.direct.health.SetAuthRoutineInError(err)
  302. report(err, f)
  303. bo.BackOff(ctx, err)
  304. continue
  305. }
  306. if url != "" {
  307. // goal.url ought to be empty here. However, not all control servers
  308. // get this right, and logging about it here just generates noise.
  309. //
  310. // TODO(bradfitz): I don't follow that comment. Our own testcontrol
  311. // used by tstest/integration hits this path, in fact.
  312. if c.direct.panicOnUse {
  313. panic("tainted client")
  314. }
  315. c.mu.Lock()
  316. c.urlToVisit = url
  317. c.loginGoal = &LoginGoal{
  318. flags: LoginDefault,
  319. url: url,
  320. }
  321. c.state = StateURLVisitRequired
  322. c.mu.Unlock()
  323. c.sendStatus("authRoutine-url", err, url, nil)
  324. if goal.url == url {
  325. // The server sent us the same URL we already tried,
  326. // backoff to avoid a busy loop.
  327. bo.BackOff(ctx, errors.New("login URL not changing"))
  328. } else {
  329. bo.BackOff(ctx, nil)
  330. }
  331. continue
  332. }
  333. // success
  334. c.direct.health.SetAuthRoutineInError(nil)
  335. c.mu.Lock()
  336. c.urlToVisit = ""
  337. c.loggedIn = true
  338. c.loginGoal = nil
  339. c.state = StateAuthenticated
  340. c.mu.Unlock()
  341. c.sendStatus("authRoutine-success", nil, "", nil)
  342. c.restartMap()
  343. bo.BackOff(ctx, nil)
  344. }
  345. }
  346. // ExpiryForTests returns the credential expiration time, or the zero value if
  347. // the expiration time isn't known. It's used in tests only.
  348. func (c *Auto) ExpiryForTests() time.Time {
  349. c.mu.Lock()
  350. defer c.mu.Unlock()
  351. return c.expiry
  352. }
  353. // DirectForTest returns the underlying direct client object.
  354. // It's used in tests only.
  355. func (c *Auto) DirectForTest() *Direct {
  356. return c.direct
  357. }
  358. // unpausedChanLocked returns a new channel that gets sent
  359. // either a true when unpaused or false on Auto.Shutdown.
  360. //
  361. // c.mu must be held
  362. func (c *Auto) unpausedChanLocked() <-chan bool {
  363. unpaused := make(chan bool, 1)
  364. c.unpauseWaiters = append(c.unpauseWaiters, unpaused)
  365. return unpaused
  366. }
  367. // mapRoutineState is the state of Auto.mapRoutine while it's running.
  368. type mapRoutineState struct {
  369. c *Auto
  370. bo *backoff.Backoff
  371. }
  372. var _ NetmapDeltaUpdater = mapRoutineState{}
  373. func (mrs mapRoutineState) UpdateFullNetmap(nm *netmap.NetworkMap) {
  374. c := mrs.c
  375. c.mu.Lock()
  376. ctx := c.mapCtx
  377. c.inMapPoll = true
  378. if c.loggedIn {
  379. c.state = StateSynchronized
  380. }
  381. c.expiry = nm.Expiry
  382. stillAuthed := c.loggedIn
  383. c.logf("[v1] mapRoutine: netmap received: %s", c.state)
  384. c.mu.Unlock()
  385. if stillAuthed {
  386. c.sendStatus("mapRoutine-got-netmap", nil, "", nm)
  387. }
  388. // Reset the backoff timer if we got a netmap.
  389. mrs.bo.BackOff(ctx, nil)
  390. }
  391. func (mrs mapRoutineState) UpdateNetmapDelta(muts []netmap.NodeMutation) bool {
  392. c := mrs.c
  393. c.mu.Lock()
  394. goodState := c.loggedIn && c.inMapPoll
  395. ndu, canDelta := c.observer.(NetmapDeltaUpdater)
  396. c.mu.Unlock()
  397. if !goodState || !canDelta {
  398. return false
  399. }
  400. ctx, cancel := context.WithTimeout(c.mapCtx, 2*time.Second)
  401. defer cancel()
  402. var ok bool
  403. err := c.observerQueue.RunSync(ctx, func() {
  404. ok = ndu.UpdateNetmapDelta(muts)
  405. })
  406. return err == nil && ok
  407. }
  408. // mapRoutine is responsible for keeping a read-only streaming connection to the
  409. // control server, and keeping the netmap up to date.
  410. func (c *Auto) mapRoutine() {
  411. defer close(c.mapDone)
  412. mrs := mapRoutineState{
  413. c: c,
  414. bo: backoff.NewBackoff("mapRoutine", c.logf, 30*time.Second),
  415. }
  416. for {
  417. if !c.waitUnpause("mapRoutine") {
  418. c.logf("mapRoutine: exiting")
  419. return
  420. }
  421. c.mu.Lock()
  422. c.logf("[v1] mapRoutine: %s", c.state)
  423. loggedIn := c.loggedIn
  424. ctx := c.mapCtx
  425. c.mu.Unlock()
  426. report := func(err error, msg string) {
  427. c.logf("[v1] %s: %v", msg, err)
  428. err = fmt.Errorf("%s: %w", msg, err)
  429. // don't send status updates for context errors,
  430. // since context cancelation is always on purpose.
  431. if ctx.Err() == nil {
  432. c.sendStatus("mapRoutine1", err, "", nil)
  433. }
  434. }
  435. if !loggedIn {
  436. // Wait for something interesting to happen
  437. c.mu.Lock()
  438. c.inMapPoll = false
  439. c.mu.Unlock()
  440. <-ctx.Done()
  441. c.logf("[v1] mapRoutine: context done.")
  442. continue
  443. }
  444. c.direct.health.SetOutOfPollNetMap()
  445. err := c.direct.PollNetMap(ctx, mrs)
  446. c.direct.health.SetOutOfPollNetMap()
  447. c.mu.Lock()
  448. c.inMapPoll = false
  449. if c.state == StateSynchronized {
  450. c.state = StateAuthenticated
  451. }
  452. paused := c.paused
  453. c.mu.Unlock()
  454. if paused {
  455. mrs.bo.BackOff(ctx, nil)
  456. c.logf("mapRoutine: paused")
  457. } else {
  458. mrs.bo.BackOff(ctx, err)
  459. report(err, "PollNetMap")
  460. }
  461. }
  462. }
  463. func (c *Auto) AuthCantContinue() bool {
  464. if c == nil {
  465. return true
  466. }
  467. c.mu.Lock()
  468. defer c.mu.Unlock()
  469. return !c.loggedIn && (c.loginGoal == nil || c.loginGoal.url != "")
  470. }
  471. func (c *Auto) SetHostinfo(hi *tailcfg.Hostinfo) {
  472. if hi == nil {
  473. panic("nil Hostinfo")
  474. }
  475. if !c.direct.SetHostinfo(hi) {
  476. // No changes. Don't log.
  477. return
  478. }
  479. // Send new Hostinfo to server
  480. c.updateControl()
  481. }
  482. func (c *Auto) SetNetInfo(ni *tailcfg.NetInfo) {
  483. if ni == nil {
  484. panic("nil NetInfo")
  485. }
  486. if !c.direct.SetNetInfo(ni) {
  487. return
  488. }
  489. // Send new NetInfo to server
  490. c.updateControl()
  491. }
  492. // SetTKAHead updates the TKA head hash that map-request infrastructure sends.
  493. func (c *Auto) SetTKAHead(headHash string) {
  494. if !c.direct.SetTKAHead(headHash) {
  495. return
  496. }
  497. // Send new TKAHead to server
  498. c.updateControl()
  499. }
  500. // sendStatus can not be called with the c.mu held.
  501. func (c *Auto) sendStatus(who string, err error, url string, nm *netmap.NetworkMap) {
  502. c.mu.Lock()
  503. if c.closed {
  504. c.mu.Unlock()
  505. return
  506. }
  507. state := c.state
  508. loggedIn := c.loggedIn
  509. inMapPoll := c.inMapPoll
  510. c.mu.Unlock()
  511. c.logf("[v1] sendStatus: %s: %v", who, state)
  512. var p persist.PersistView
  513. if nm != nil && loggedIn && inMapPoll {
  514. p = c.direct.GetPersist()
  515. } else {
  516. // don't send netmap status, as it's misleading when we're
  517. // not logged in.
  518. nm = nil
  519. }
  520. new := Status{
  521. URL: url,
  522. Persist: p,
  523. NetMap: nm,
  524. Err: err,
  525. state: state,
  526. }
  527. // Launch a new goroutine to avoid blocking the caller while the observer
  528. // does its thing, which may result in a call back into the client.
  529. c.observerQueue.Add(func() {
  530. c.observer.SetControlClientStatus(c, new)
  531. })
  532. }
  533. func (c *Auto) Login(flags LoginFlags) {
  534. c.logf("client.Login(%v)", flags)
  535. c.mu.Lock()
  536. defer c.mu.Unlock()
  537. if c.closed {
  538. return
  539. }
  540. if c.direct != nil && c.direct.panicOnUse {
  541. panic("tainted client")
  542. }
  543. c.wantLoggedIn = true
  544. c.loginGoal = &LoginGoal{
  545. flags: flags,
  546. }
  547. c.cancelMapCtxLocked()
  548. c.cancelAuthCtxLocked()
  549. }
  550. var ErrClientClosed = errors.New("client closed")
  551. func (c *Auto) Logout(ctx context.Context) error {
  552. c.logf("client.Logout()")
  553. c.mu.Lock()
  554. c.wantLoggedIn = false
  555. c.loginGoal = nil
  556. closed := c.closed
  557. if c.direct != nil && c.direct.panicOnUse {
  558. panic("tainted client")
  559. }
  560. c.mu.Unlock()
  561. if closed {
  562. return ErrClientClosed
  563. }
  564. if err := c.direct.TryLogout(ctx); err != nil {
  565. return err
  566. }
  567. c.mu.Lock()
  568. c.loggedIn = false
  569. c.state = StateNotAuthenticated
  570. c.cancelAuthCtxLocked()
  571. c.cancelMapCtxLocked()
  572. c.mu.Unlock()
  573. c.sendStatus("authRoutine-wantout", nil, "", nil)
  574. return nil
  575. }
  576. func (c *Auto) SetExpirySooner(ctx context.Context, expiry time.Time) error {
  577. return c.direct.SetExpirySooner(ctx, expiry)
  578. }
  579. // UpdateEndpoints sets the client's discovered endpoints and sends
  580. // them to the control server if they've changed.
  581. //
  582. // It does not retain the provided slice.
  583. func (c *Auto) UpdateEndpoints(endpoints []tailcfg.Endpoint) {
  584. changed := c.direct.SetEndpoints(endpoints)
  585. if changed {
  586. c.updateControl()
  587. }
  588. }
  589. func (c *Auto) Shutdown() {
  590. c.mu.Lock()
  591. if c.closed {
  592. c.mu.Unlock()
  593. return
  594. }
  595. c.logf("client.Shutdown ...")
  596. direct := c.direct
  597. c.closed = true
  598. c.observerQueue.Shutdown()
  599. c.cancelAuthCtxLocked()
  600. c.cancelMapCtxLocked()
  601. for _, w := range c.unpauseWaiters {
  602. w <- false
  603. }
  604. c.unpauseWaiters = nil
  605. c.mu.Unlock()
  606. c.unregisterHealthWatch()
  607. <-c.authDone
  608. <-c.mapDone
  609. <-c.updateDone
  610. if direct != nil {
  611. direct.Close()
  612. }
  613. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  614. defer cancel()
  615. c.observerQueue.Wait(ctx)
  616. c.logf("Client.Shutdown done.")
  617. }
  618. // NodePublicKey returns the node public key currently in use. This is
  619. // used exclusively in tests.
  620. func (c *Auto) TestOnlyNodePublicKey() key.NodePublic {
  621. priv := c.direct.GetPersist()
  622. return priv.PrivateNodeKey().Public()
  623. }
  624. func (c *Auto) TestOnlySetAuthKey(authkey string) {
  625. c.direct.mu.Lock()
  626. defer c.direct.mu.Unlock()
  627. c.direct.authKey = authkey
  628. }
  629. func (c *Auto) TestOnlyTimeNow() time.Time {
  630. return c.clock.Now()
  631. }
  632. // SetDNS sends the SetDNSRequest request to the control plane server,
  633. // requesting a DNS record be created or updated.
  634. func (c *Auto) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) error {
  635. return c.direct.SetDNS(ctx, req)
  636. }
  637. func (c *Auto) DoNoiseRequest(req *http.Request) (*http.Response, error) {
  638. return c.direct.DoNoiseRequest(req)
  639. }
  640. // GetSingleUseNoiseRoundTripper returns a RoundTripper that can be only be used
  641. // once (and must be used once) to make a single HTTP request over the noise
  642. // channel to the coordination server.
  643. //
  644. // In addition to the RoundTripper, it returns the HTTP/2 channel's early noise
  645. // payload, if any.
  646. func (c *Auto) GetSingleUseNoiseRoundTripper(ctx context.Context) (http.RoundTripper, *tailcfg.EarlyNoise, error) {
  647. return c.direct.GetSingleUseNoiseRoundTripper(ctx)
  648. }