maybeInjectSubAgentIntoContainerLocked injects a subagent into a dev container and starts the subagent process. This method assumes that api.mu is held. This method is idempotent and will not re-inject the subagent if it is already/still running in the container. This method uses an internal timeou
(ctx context.Context, dc codersdk.WorkspaceAgentDevcontainer)
| 1668 | // This method uses an internal timeout to prevent blocking indefinitely |
| 1669 | // if something goes wrong with the injection. |
| 1670 | func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc codersdk.WorkspaceAgentDevcontainer) (err error) { |
| 1671 | if api.ignoredDevcontainers[dc.WorkspaceFolder] { |
| 1672 | return nil |
| 1673 | } |
| 1674 | |
| 1675 | ctx, cancel := context.WithTimeout(ctx, defaultOperationTimeout) |
| 1676 | defer cancel() |
| 1677 | |
| 1678 | container := dc.Container |
| 1679 | if container == nil { |
| 1680 | return xerrors.New("container is nil, cannot inject subagent") |
| 1681 | } |
| 1682 | |
| 1683 | logger := api.logger.With( |
| 1684 | slog.F("devcontainer_id", dc.ID), |
| 1685 | slog.F("devcontainer_name", dc.Name), |
| 1686 | slog.F("workspace_folder", dc.WorkspaceFolder), |
| 1687 | slog.F("config_path", dc.ConfigPath), |
| 1688 | slog.F("container_id", container.ID), |
| 1689 | slog.F("container_name", container.FriendlyName), |
| 1690 | ) |
| 1691 | |
| 1692 | // Check if subagent already exists for this devcontainer. |
| 1693 | maybeRecreateSubAgent := false |
| 1694 | proc, injected := api.injectedSubAgentProcs[dc.WorkspaceFolder] |
| 1695 | if injected { |
| 1696 | if _, ignoreChecked := api.ignoredDevcontainers[dc.WorkspaceFolder]; !ignoreChecked { |
| 1697 | // If ignore status has not yet been checked, or cleared by |
| 1698 | // modifications to the devcontainer.json, we must read it |
| 1699 | // to determine the current status. This can happen while |
| 1700 | // the devcontainer subagent is already running or before |
| 1701 | // we've had a chance to inject it. |
| 1702 | // |
| 1703 | // Note, for simplicity, we do not try to optimize to reduce |
| 1704 | // ReadConfig calls here. |
| 1705 | config, err := api.dccli.ReadConfig(ctx, dc.WorkspaceFolder, dc.ConfigPath, nil) |
| 1706 | if err != nil { |
| 1707 | return xerrors.Errorf("read devcontainer config: %w", err) |
| 1708 | } |
| 1709 | |
| 1710 | dcIgnored := config.Configuration.Customizations.Coder.Ignore |
| 1711 | if dcIgnored { |
| 1712 | proc.stop() |
| 1713 | if proc.agent.ID != uuid.Nil { |
| 1714 | // Unlock while doing the delete operation. |
| 1715 | api.mu.Unlock() |
| 1716 | client := *api.subAgentClient.Load() |
| 1717 | if err := client.Delete(ctx, proc.agent.ID); err != nil { |
| 1718 | api.mu.Lock() |
| 1719 | return xerrors.Errorf("delete subagent: %w", err) |
| 1720 | } |
| 1721 | api.mu.Lock() |
| 1722 | } |
| 1723 | // Reset agent and containerID to force config re-reading if ignore is toggled. |
| 1724 | proc.agent = SubAgent{} |
| 1725 | proc.containerID = "" |
| 1726 | api.injectedSubAgentProcs[dc.WorkspaceFolder] = proc |
| 1727 | api.ignoredDevcontainers[dc.WorkspaceFolder] = dcIgnored |
no test coverage detected