Run dials the API and uses it with the provided controllers.
(ctx context.Context)
| 1528 | |
| 1529 | // Run dials the API and uses it with the provided controllers. |
| 1530 | func (c *Controller) Run(ctx context.Context) { |
| 1531 | c.ctx = ctx |
| 1532 | c.gracefulCtx, c.cancelGracefulCtx = context.WithCancel(context.Background()) |
| 1533 | go c.manageGracefulTimeout() |
| 1534 | go func() { |
| 1535 | defer close(c.closedCh) |
| 1536 | // Sadly retry doesn't support quartz.Clock yet so this is not |
| 1537 | // influenced by the configured clock. |
| 1538 | for retrier := retry.New(50*time.Millisecond, 10*time.Second); retrier.Wait(c.ctx); { |
| 1539 | // Check the context again before dialing, since `retrier.Wait()` could return true |
| 1540 | // if the delay is 0, even if the context was canceled. This ensures we don't redial |
| 1541 | // after a graceful shutdown. |
| 1542 | if c.ctx.Err() != nil { |
| 1543 | return |
| 1544 | } |
| 1545 | |
| 1546 | tailnetClients, err := c.Dialer.Dial(c.ctx, c.ResumeTokenCtrl) |
| 1547 | if err != nil { |
| 1548 | if c.ctx.Err() != nil { |
| 1549 | return |
| 1550 | } |
| 1551 | |
| 1552 | if errors.Is(err, net.ErrClosed) { |
| 1553 | reason := codersdk.DisconnectReasonNetworkError |
| 1554 | c.logger.Warn(c.ctx, "control plane connection closed, retrying", |
| 1555 | codersdk.ConnectionDirectionServerToAgent.SlogField(), |
| 1556 | reason.SlogField(), |
| 1557 | reason.SlogExpectedField(), |
| 1558 | codersdk.DisconnectInitiatorNetwork.SlogField(), |
| 1559 | slog.Error(err), |
| 1560 | ) |
| 1561 | continue |
| 1562 | } |
| 1563 | |
| 1564 | // If the database is unreachable by the control plane, there's not much we can do, so we'll just retry later. |
| 1565 | if errors.Is(err, codersdk.ErrDatabaseNotReachable) { |
| 1566 | c.logger.Warn(c.ctx, "control plane lost connection to database, retrying", |
| 1567 | slog.Error(err), slog.F("delay", fmt.Sprintf("%vms", retrier.Delay.Milliseconds()))) |
| 1568 | continue |
| 1569 | } |
| 1570 | |
| 1571 | errF := slog.Error(err) |
| 1572 | var sdkErr *codersdk.Error |
| 1573 | if xerrors.As(err, &sdkErr) { |
| 1574 | errF = slog.Error(sdkErr) |
| 1575 | } |
| 1576 | c.logger.Error(c.ctx, "failed to dial tailnet v2+ API", errF) |
| 1577 | continue |
| 1578 | } |
| 1579 | c.logger.Debug(c.ctx, "obtained tailnet API v2+ client") |
| 1580 | err = c.precheckClientsAndControllers(tailnetClients) |
| 1581 | if err != nil { |
| 1582 | c.logger.Critical(c.ctx, "failed precheck", slog.Error(err)) |
| 1583 | _ = tailnetClients.Closer.Close() |
| 1584 | continue |
| 1585 | } |
| 1586 | retrier.Reset() |
| 1587 | c.runControllersOnce(tailnetClients) |