Skip to content

Commit d496771

Browse files
committed
fix(agent/agentcontainers): reduce need to recreate sub agents
Updates #18332
1 parent c033618 commit d496771

File tree

2 files changed

+59
-34
lines changed

2 files changed

+59
-34
lines changed

agent/agentcontainers/api.go

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
977977
)
978978

979979
// Check if subagent already exists for this devcontainer.
980-
recreateSubAgent := false
980+
maybeRecreateSubAgent := false
981981
proc, injected := api.injectedSubAgentProcs[dc.WorkspaceFolder]
982982
if injected {
983983
if proc.containerID == container.ID && proc.ctx.Err() == nil {
@@ -992,12 +992,15 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
992992
logger.Debug(ctx, "container ID changed, injecting subagent into new container",
993993
slog.F("old_container_id", proc.containerID),
994994
)
995-
recreateSubAgent = true
995+
maybeRecreateSubAgent = true
996996
}
997997

998998
// Container ID changed or the subagent process is not running,
999999
// stop the existing subagent context to replace it.
10001000
proc.stop()
1001+
} else {
1002+
// Set SubAgent defaults.
1003+
proc.agent.OperatingSystem = "linux" // Assuming Linux for devcontainers.
10011004
}
10021005

10031006
// Prepare the subAgentProcess to be used when running the subagent.
@@ -1090,36 +1093,33 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
10901093
// logger.Warn(ctx, "set CAP_NET_ADMIN on agent binary failed", slog.Error(err))
10911094
// }
10921095

1096+
updatedSubAgent := proc.agent
1097+
10931098
// Detect workspace folder by executing `pwd` in the container.
10941099
// NOTE(mafredri): This is a quick and dirty way to detect the
10951100
// workspace folder inside the container. In the future we will
10961101
// rely more on `devcontainer read-configuration`.
1097-
var pwdBuf bytes.Buffer
1098-
err = api.dccli.Exec(ctx, dc.WorkspaceFolder, dc.ConfigPath, "pwd", []string{},
1099-
WithExecOutput(&pwdBuf, io.Discard),
1100-
WithExecContainerID(container.ID),
1101-
)
1102-
if err != nil {
1103-
return xerrors.Errorf("check workspace folder in container: %w", err)
1104-
}
1105-
directory := strings.TrimSpace(pwdBuf.String())
1106-
if directory == "" {
1107-
logger.Warn(ctx, "detected workspace folder is empty, using default workspace folder",
1108-
slog.F("default_workspace_folder", DevcontainerDefaultContainerWorkspaceFolder),
1102+
if proc.agent.Directory == "" || maybeRecreateSubAgent {
1103+
var pwdBuf bytes.Buffer
1104+
err = api.dccli.Exec(ctx, dc.WorkspaceFolder, dc.ConfigPath, "pwd", []string{},
1105+
WithExecOutput(&pwdBuf, io.Discard),
1106+
WithExecContainerID(container.ID),
11091107
)
1110-
directory = DevcontainerDefaultContainerWorkspaceFolder
1111-
}
1112-
1113-
if proc.agent.ID != uuid.Nil && recreateSubAgent {
1114-
logger.Debug(ctx, "deleting existing subagent for recreation", slog.F("agent_id", proc.agent.ID))
1115-
client := *api.subAgentClient.Load()
1116-
err = client.Delete(ctx, proc.agent.ID)
11171108
if err != nil {
1118-
return xerrors.Errorf("delete existing subagent failed: %w", err)
1109+
return xerrors.Errorf("check workspace folder in container: %w", err)
1110+
}
1111+
directory := strings.TrimSpace(pwdBuf.String())
1112+
if directory == "" {
1113+
logger.Warn(ctx, "detected workspace folder is empty, using default workspace folder",
1114+
slog.F("default_workspace_folder", DevcontainerDefaultContainerWorkspaceFolder),
1115+
)
1116+
directory = DevcontainerDefaultContainerWorkspaceFolder
11191117
}
1120-
proc.agent = SubAgent{}
1118+
1119+
updatedSubAgent.Directory = directory
11211120
}
1122-
if proc.agent.ID == uuid.Nil {
1121+
1122+
if maybeRecreateSubAgent {
11231123
displayAppsMap := map[codersdk.DisplayApp]bool{
11241124
// NOTE(DanielleMaywood):
11251125
// We use the same defaults here as set in terraform-provider-coder.
@@ -1138,6 +1138,9 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
11381138

11391139
for _, customization := range coderCustomization {
11401140
for app, enabled := range customization.DisplayApps {
1141+
if _, ok := displayAppsMap[app]; !ok {
1142+
continue // Ignore unknown display apps.
1143+
}
11411144
displayAppsMap[app] = enabled
11421145
}
11431146
}
@@ -1149,24 +1152,37 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
11491152
displayApps = append(displayApps, app)
11501153
}
11511154
}
1155+
slices.Sort(displayApps)
1156+
1157+
updatedSubAgent.Name = dc.Name
1158+
updatedSubAgent.DisplayApps = displayApps
1159+
}
1160+
1161+
recreateSubAgent := maybeRecreateSubAgent && !proc.agent.Equal(updatedSubAgent)
1162+
if recreateSubAgent {
1163+
logger.Debug(ctx, "deleting existing subagent for recreation", slog.F("agent_id", proc.agent.ID))
1164+
client := *api.subAgentClient.Load()
1165+
err = client.Delete(ctx, proc.agent.ID)
1166+
if err != nil {
1167+
return xerrors.Errorf("delete existing subagent failed: %w", err)
1168+
}
1169+
proc.agent = SubAgent{} // In case of error, make sure this is cleared.
1170+
1171+
// Reset fields that are not part of the request.
1172+
updatedSubAgent.ID = uuid.Nil
1173+
updatedSubAgent.AuthToken = uuid.Nil
11521174

11531175
logger.Debug(ctx, "creating new subagent",
1154-
slog.F("directory", directory),
1155-
slog.F("display_apps", displayApps),
1176+
slog.F("directory", updatedSubAgent.Directory),
1177+
slog.F("display_apps", updatedSubAgent.DisplayApps),
11561178
)
11571179

11581180
// Create new subagent record in the database to receive the auth token.
1159-
client := *api.subAgentClient.Load()
1160-
proc.agent, err = client.Create(ctx, SubAgent{
1161-
Name: dc.Name,
1162-
Directory: directory,
1163-
OperatingSystem: "linux", // Assuming Linux for devcontainers.
1164-
Architecture: arch,
1165-
DisplayApps: displayApps,
1166-
})
1181+
newSubAgent, err := client.Create(ctx, updatedSubAgent)
11671182
if err != nil {
11681183
return xerrors.Errorf("create subagent failed: %w", err)
11691184
}
1185+
proc.agent = newSubAgent
11701186

11711187
logger.Info(ctx, "created new subagent", slog.F("agent_id", proc.agent.ID))
11721188
}

agent/agentcontainers/subagent.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package agentcontainers
22

33
import (
44
"context"
5+
"slices"
56

67
"github.com/google/uuid"
78
"golang.org/x/xerrors"
@@ -23,6 +24,14 @@ type SubAgent struct {
2324
DisplayApps []codersdk.DisplayApp
2425
}
2526

27+
func (s SubAgent) Equal(other SubAgent) bool {
28+
return s.Name == other.Name &&
29+
s.Directory == other.Directory &&
30+
s.Architecture == other.Architecture &&
31+
s.OperatingSystem == other.OperatingSystem &&
32+
slices.Equal(s.DisplayApps, other.DisplayApps)
33+
}
34+
2635
// SubAgentClient is an interface for managing sub agents and allows
2736
// changing the implementation without having to deal with the
2837
// agentproto package directly.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy