updaterLoop is responsible for periodically updating the container list and handling manual refresh requests.
()
| 617 | // updaterLoop is responsible for periodically updating the container |
| 618 | // list and handling manual refresh requests. |
| 619 | func (api *API) updaterLoop() { |
| 620 | defer close(api.updaterDone) |
| 621 | defer api.logger.Debug(api.ctx, "updater loop stopped") |
| 622 | api.logger.Debug(api.ctx, "updater loop started") |
| 623 | |
| 624 | // Make sure we clean up any subagents not tracked by this process |
| 625 | // before starting the update loop and creating new ones. |
| 626 | api.logger.Debug(api.ctx, "cleaning up subagents") |
| 627 | if err := api.cleanupSubAgents(api.ctx); err != nil { |
| 628 | api.logger.Error(api.ctx, "cleanup subagents failed", slog.Error(err)) |
| 629 | } else { |
| 630 | api.logger.Debug(api.ctx, "cleanup subagents complete") |
| 631 | } |
| 632 | |
| 633 | // Perform an initial update to populate the container list, this |
| 634 | // gives us a guarantee that the API has loaded the initial state |
| 635 | // before returning any responses. This is useful for both tests |
| 636 | // and anyone looking to interact with the API. |
| 637 | api.logger.Debug(api.ctx, "performing initial containers update") |
| 638 | if err := api.updateContainers(api.ctx); err != nil { |
| 639 | if errors.Is(err, context.Canceled) { |
| 640 | api.logger.Warn(api.ctx, "initial containers update canceled", slog.Error(err)) |
| 641 | } else { |
| 642 | api.logger.Error(api.ctx, "initial containers update failed", slog.Error(err)) |
| 643 | } |
| 644 | } else { |
| 645 | api.logger.Debug(api.ctx, "initial containers update complete") |
| 646 | } |
| 647 | close(api.initialUpdateDone) |
| 648 | |
| 649 | // We utilize a TickerFunc here instead of a regular Ticker so that |
| 650 | // we can guarantee execution of the updateContainers method after |
| 651 | // advancing the clock. |
| 652 | var prevErr error |
| 653 | ticker := api.clock.TickerFunc(api.ctx, api.updateInterval, func() error { |
| 654 | done := make(chan error, 1) |
| 655 | var sent bool |
| 656 | defer func() { |
| 657 | if !sent { |
| 658 | close(done) |
| 659 | } |
| 660 | }() |
| 661 | select { |
| 662 | case <-api.ctx.Done(): |
| 663 | return api.ctx.Err() |
| 664 | case api.updateTrigger <- done: |
| 665 | sent = true |
| 666 | err := <-done |
| 667 | if err != nil { |
| 668 | if errors.Is(err, context.Canceled) { |
| 669 | api.logger.Warn(api.ctx, "updater loop ticker canceled", slog.Error(err)) |
| 670 | return nil |
| 671 | } |
| 672 | // Avoid excessive logging of the same error. |
| 673 | if prevErr == nil || prevErr.Error() != err.Error() { |
| 674 | api.logger.Error(api.ctx, "updater loop ticker failed", slog.Error(err)) |
| 675 | } |
| 676 | prevErr = err |
no test coverage detected