| 36 | } |
| 37 | |
| 38 | func (a *SubAgentAPI) CreateSubAgent(ctx context.Context, req *agentproto.CreateSubAgentRequest) (*agentproto.CreateSubAgentResponse, error) { |
| 39 | //nolint:gocritic // This gives us only the permissions required to do the job. |
| 40 | ctx = dbauthz.AsSubAgentAPI(ctx, a.OrganizationID, a.OwnerID) |
| 41 | |
| 42 | createdAt := a.Clock.Now() |
| 43 | |
| 44 | displayApps := make([]database.DisplayApp, 0, len(req.DisplayApps)) |
| 45 | for idx, displayApp := range req.DisplayApps { |
| 46 | var app database.DisplayApp |
| 47 | |
| 48 | switch displayApp { |
| 49 | case agentproto.CreateSubAgentRequest_PORT_FORWARDING_HELPER: |
| 50 | app = database.DisplayAppPortForwardingHelper |
| 51 | case agentproto.CreateSubAgentRequest_SSH_HELPER: |
| 52 | app = database.DisplayAppSSHHelper |
| 53 | case agentproto.CreateSubAgentRequest_VSCODE: |
| 54 | app = database.DisplayAppVscode |
| 55 | case agentproto.CreateSubAgentRequest_VSCODE_INSIDERS: |
| 56 | app = database.DisplayAppVscodeInsiders |
| 57 | case agentproto.CreateSubAgentRequest_WEB_TERMINAL: |
| 58 | app = database.DisplayAppWebTerminal |
| 59 | default: |
| 60 | return nil, codersdk.ValidationError{ |
| 61 | Field: fmt.Sprintf("display_apps[%d]", idx), |
| 62 | Detail: fmt.Sprintf("%q is not a valid display app", displayApp), |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | displayApps = append(displayApps, app) |
| 67 | } |
| 68 | |
| 69 | parentAgent, err := a.AgentFn(ctx) |
| 70 | if err != nil { |
| 71 | return nil, xerrors.Errorf("get parent agent: %w", err) |
| 72 | } |
| 73 | |
| 74 | // An ID is only given in the request when it is a terraform-defined devcontainer |
| 75 | // that has attached resources. These subagents are pre-provisioned by terraform |
| 76 | // (the agent record already exists), so we update configurable fields like |
| 77 | // display_apps and directory rather than creating a new agent. |
| 78 | if req.Id != nil { |
| 79 | id, err := uuid.FromBytes(req.Id) |
| 80 | if err != nil { |
| 81 | return nil, xerrors.Errorf("parse agent id: %w", err) |
| 82 | } |
| 83 | |
| 84 | subAgent, err := a.Database.GetWorkspaceAgentByID(ctx, id) |
| 85 | if err != nil { |
| 86 | return nil, xerrors.Errorf("get workspace agent by id: %w", err) |
| 87 | } |
| 88 | |
| 89 | // Validate that the subagent belongs to the current parent agent to |
| 90 | // prevent updating subagents from other agents within the same workspace. |
| 91 | if !subAgent.ParentID.Valid || subAgent.ParentID.UUID != parentAgent.ID { |
| 92 | return nil, xerrors.Errorf("subagent does not belong to this parent agent") |
| 93 | } |
| 94 | |
| 95 | if err := a.Database.UpdateWorkspaceAgentDisplayAppsByID(ctx, database.UpdateWorkspaceAgentDisplayAppsByIDParams{ |