Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5bac6ef

Browse files
committed
feat(agent/agentcontainers): implement ignore customization for devcontainers
Fixescoder/internal#737
1 parent3fb5d0b commit5bac6ef

File tree

3 files changed

+291
-43
lines changed

3 files changed

+291
-43
lines changed

‎agent/agentcontainers/api.go

Lines changed: 123 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ type API struct {
7878
devcontainerNamesmap[string]bool// By devcontainer name.
7979
knownDevcontainersmap[string]codersdk.WorkspaceAgentDevcontainer// By workspace folder.
8080
configFileModifiedTimesmap[string]time.Time// By config file path.
81+
ignoredDevcontainersmap[string]bool// By workspace folder. Tracks three states (true, false and not checked).
8182
recreateSuccessTimesmap[string]time.Time// By workspace folder.
8283
recreateErrorTimesmap[string]time.Time// By workspace folder.
8384
injectedSubAgentProcsmap[string]subAgentProcess// By workspace folder.
@@ -274,6 +275,7 @@ func NewAPI(logger slog.Logger, options ...Option) *API {
274275
devcontainerNames:make(map[string]bool),
275276
knownDevcontainers:make(map[string]codersdk.WorkspaceAgentDevcontainer),
276277
configFileModifiedTimes:make(map[string]time.Time),
278+
ignoredDevcontainers:make(map[string]bool),
277279
recreateSuccessTimes:make(map[string]time.Time),
278280
recreateErrorTimes:make(map[string]time.Time),
279281
scriptLogger:func(uuid.UUID)ScriptLogger {returnnoopScriptLogger{} },
@@ -750,6 +752,10 @@ func (api *API) getContainers() (codersdk.WorkspaceAgentListContainersResponse,
750752
iflen(api.knownDevcontainers)>0 {
751753
devcontainers=make([]codersdk.WorkspaceAgentDevcontainer,0,len(api.knownDevcontainers))
752754
for_,dc:=rangeapi.knownDevcontainers {
755+
ifapi.ignoredDevcontainers[dc.WorkspaceFolder] {
756+
continue
757+
}
758+
753759
// Include the agent if it's running (we're iterating over
754760
// copies, so mutating is fine).
755761
ifproc:=api.injectedSubAgentProcs[dc.WorkspaceFolder];proc.agent.ID!=uuid.Nil {
@@ -982,11 +988,46 @@ func (api *API) markDevcontainerDirty(configPath string, modifiedAt time.Time) {
982988
logger.Info(api.ctx,"marking devcontainer as dirty")
983989
dc.Dirty=true
984990
}
991+
ifapi.ignoredDevcontainers[dc.WorkspaceFolder] {
992+
logger.Debug(api.ctx,"clearing devcontainer ignored state")
993+
delete(api.ignoredDevcontainers,dc.WorkspaceFolder)// Allow re-reading config.
994+
}
985995

986996
api.knownDevcontainers[dc.WorkspaceFolder]=dc
987997
}
988998
}
989999

1000+
// markDevcontainerIgnored marks a devcontainer as ignored. Must not be called
1001+
// with the API lock held.
1002+
func (api*API)markDevcontainerIgnored(ctx context.Context,dc codersdk.WorkspaceAgentDevcontainer,procsubAgentProcess)subAgentProcess {
1003+
logger:=api.logger.With(
1004+
slog.F("devcontainer_id",dc.ID),
1005+
slog.F("devcontainer_name",dc.Name),
1006+
slog.F("workspace_folder",dc.WorkspaceFolder),
1007+
slog.F("config_path",dc.ConfigPath),
1008+
)
1009+
1010+
// We only allow ignore to be set in the root customization layer to
1011+
// prevent weird interactions with devcontainer features.
1012+
logger.Debug(ctx,"marking devcontainer as ignored")
1013+
proc.stop()
1014+
ifproc.agent.ID!=uuid.Nil {
1015+
// If we stop the subagent, we also need to delete it.
1016+
client:=*api.subAgentClient.Load()
1017+
iferr:=client.Delete(ctx,proc.agent.ID);err!=nil {
1018+
api.logger.Error(ctx,"delete subagent failed",slog.Error(err),slog.F("subagent_id",proc.agent.ID))
1019+
}
1020+
}
1021+
// Reset agent and containerID to force config re-reading if ignore is toggled.
1022+
proc.agent=SubAgent{}
1023+
proc.containerID=""
1024+
api.mu.Lock()
1025+
api.ignoredDevcontainers[dc.WorkspaceFolder]=true
1026+
api.mu.Unlock()
1027+
1028+
returnproc
1029+
}
1030+
9901031
// cleanupSubAgents removes subagents that are no longer managed by
9911032
// this agent. This is usually only run at startup to ensure a clean
9921033
// slate. This method has an internal timeout to prevent blocking
@@ -1038,6 +1079,10 @@ func (api *API) cleanupSubAgents(ctx context.Context) error {
10381079
// This method uses an internal timeout to prevent blocking indefinitely
10391080
// if something goes wrong with the injection.
10401081
func (api*API)maybeInjectSubAgentIntoContainerLocked(ctx context.Context,dc codersdk.WorkspaceAgentDevcontainer) (errerror) {
1082+
ifapi.ignoredDevcontainers[dc.WorkspaceFolder] {
1083+
returnnil
1084+
}
1085+
10411086
ctx,cancel:=context.WithTimeout(ctx,defaultOperationTimeout)
10421087
defercancel()
10431088

@@ -1059,6 +1104,29 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
10591104
maybeRecreateSubAgent:=false
10601105
proc,injected:=api.injectedSubAgentProcs[dc.WorkspaceFolder]
10611106
ifinjected {
1107+
if_,ignoreChecked:=api.ignoredDevcontainers[dc.WorkspaceFolder];!ignoreChecked {
1108+
// If ignore status has not yet been checked, or cleared by
1109+
// modifications to the devcontainer.json, we must read it
1110+
// to determine the current status. This can happen while
1111+
// the devcontainer subagent is already running or before
1112+
// we've had a chance to inject it.
1113+
//
1114+
// Note, for simplicity, we do not try to optimize to reduce
1115+
// ReadConfig calls here.
1116+
config,err:=api.dccli.ReadConfig(ctx,dc.WorkspaceFolder,dc.ConfigPath,nil)
1117+
iferr!=nil {
1118+
returnxerrors.Errorf("read devcontainer config: %w",err)
1119+
}
1120+
1121+
ifconfig.Configuration.Customizations.Coder.Ignore {
1122+
api.mu.Unlock()
1123+
proc=api.markDevcontainerIgnored(ctx,dc,proc)
1124+
api.mu.Lock()
1125+
api.injectedSubAgentProcs[dc.WorkspaceFolder]=proc
1126+
returnnil
1127+
}
1128+
}
1129+
10621130
ifproc.containerID==container.ID&&proc.ctx.Err()==nil {
10631131
// Same container and running, no need to reinject.
10641132
returnnil
@@ -1077,7 +1145,8 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
10771145
// Container ID changed or the subagent process is not running,
10781146
// stop the existing subagent context to replace it.
10791147
proc.stop()
1080-
}else {
1148+
}
1149+
ifproc.agent.OperatingSystem=="" {
10811150
// Set SubAgent defaults.
10821151
proc.agent.OperatingSystem="linux"// Assuming Linux for devcontainers.
10831152
}
@@ -1134,48 +1203,6 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
11341203
proc.agent.Architecture=arch
11351204
}
11361205

1137-
agentBinaryPath,err:=os.Executable()
1138-
iferr!=nil {
1139-
returnxerrors.Errorf("get agent binary path: %w",err)
1140-
}
1141-
agentBinaryPath,err=filepath.EvalSymlinks(agentBinaryPath)
1142-
iferr!=nil {
1143-
returnxerrors.Errorf("resolve agent binary path: %w",err)
1144-
}
1145-
1146-
// If we scripted this as a `/bin/sh` script, we could reduce these
1147-
// steps to one instruction, speeding up the injection process.
1148-
//
1149-
// Note: We use `path` instead of `filepath` here because we are
1150-
// working with Unix-style paths inside the container.
1151-
if_,err:=api.ccli.ExecAs(ctx,container.ID,"root","mkdir","-p",path.Dir(coderPathInsideContainer));err!=nil {
1152-
returnxerrors.Errorf("create agent directory in container: %w",err)
1153-
}
1154-
1155-
iferr:=api.ccli.Copy(ctx,container.ID,agentBinaryPath,coderPathInsideContainer);err!=nil {
1156-
returnxerrors.Errorf("copy agent binary: %w",err)
1157-
}
1158-
1159-
logger.Info(ctx,"copied agent binary to container")
1160-
1161-
// Make sure the agent binary is executable so we can run it (the
1162-
// user doesn't matter since we're making it executable for all).
1163-
if_,err:=api.ccli.ExecAs(ctx,container.ID,"root","chmod","0755",path.Dir(coderPathInsideContainer),coderPathInsideContainer);err!=nil {
1164-
returnxerrors.Errorf("set agent binary executable: %w",err)
1165-
}
1166-
1167-
// Attempt to add CAP_NET_ADMIN to the binary to improve network
1168-
// performance (optional, allow to fail). See `bootstrap_linux.sh`.
1169-
// TODO(mafredri): Disable for now until we can figure out why this
1170-
// causes the following error on some images:
1171-
//
1172-
//Image: mcr.microsoft.com/devcontainers/base:ubuntu
1173-
// Error: /.coder-agent/coder: Operation not permitted
1174-
//
1175-
// if _, err := api.ccli.ExecAs(ctx, container.ID, "root", "setcap", "cap_net_admin+ep", coderPathInsideContainer); err != nil {
1176-
// logger.Warn(ctx, "set CAP_NET_ADMIN on agent binary failed", slog.Error(err))
1177-
// }
1178-
11791206
subAgentConfig:=proc.agent.CloneConfig(dc)
11801207
ifproc.agent.ID==uuid.Nil||maybeRecreateSubAgent {
11811208
subAgentConfig.Architecture=arch
@@ -1192,6 +1219,7 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
11921219
}
11931220

11941221
var (
1222+
ignorebool
11951223
appsWithPossibleDuplicates []SubAgentApp
11961224
workspaceFolder=DevcontainerDefaultContainerWorkspaceFolder
11971225
)
@@ -1215,8 +1243,13 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
12151243
returnerr
12161244
}
12171245

1246+
ignore=config.Configuration.Customizations.Coder.Ignore
12181247
workspaceFolder=config.Workspace.WorkspaceFolder
12191248

1249+
ifignore {
1250+
returnnil
1251+
}
1252+
12201253
// NOTE(DanielleMaywood):
12211254
// We only want to take an agent name specified in the root customization layer.
12221255
// This restricts the ability for a feature to specify the agent name. We may revisit
@@ -1262,6 +1295,11 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
12621295
api.logger.Error(ctx,"unable to read devcontainer config",slog.Error(err))
12631296
}
12641297

1298+
ifignore {
1299+
proc=api.markDevcontainerIgnored(ctx,dc,proc)
1300+
returnnil
1301+
}
1302+
12651303
displayApps:=make([]codersdk.DisplayApp,0,len(displayAppsMap))
12661304
forapp,enabled:=rangedisplayAppsMap {
12671305
ifenabled {
@@ -1294,6 +1332,48 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
12941332
subAgentConfig.Directory=workspaceFolder
12951333
}
12961334

1335+
agentBinaryPath,err:=os.Executable()
1336+
iferr!=nil {
1337+
returnxerrors.Errorf("get agent binary path: %w",err)
1338+
}
1339+
agentBinaryPath,err=filepath.EvalSymlinks(agentBinaryPath)
1340+
iferr!=nil {
1341+
returnxerrors.Errorf("resolve agent binary path: %w",err)
1342+
}
1343+
1344+
// If we scripted this as a `/bin/sh` script, we could reduce these
1345+
// steps to one instruction, speeding up the injection process.
1346+
//
1347+
// Note: We use `path` instead of `filepath` here because we are
1348+
// working with Unix-style paths inside the container.
1349+
if_,err:=api.ccli.ExecAs(ctx,container.ID,"root","mkdir","-p",path.Dir(coderPathInsideContainer));err!=nil {
1350+
returnxerrors.Errorf("create agent directory in container: %w",err)
1351+
}
1352+
1353+
iferr:=api.ccli.Copy(ctx,container.ID,agentBinaryPath,coderPathInsideContainer);err!=nil {
1354+
returnxerrors.Errorf("copy agent binary: %w",err)
1355+
}
1356+
1357+
logger.Info(ctx,"copied agent binary to container")
1358+
1359+
// Make sure the agent binary is executable so we can run it (the
1360+
// user doesn't matter since we're making it executable for all).
1361+
if_,err:=api.ccli.ExecAs(ctx,container.ID,"root","chmod","0755",path.Dir(coderPathInsideContainer),coderPathInsideContainer);err!=nil {
1362+
returnxerrors.Errorf("set agent binary executable: %w",err)
1363+
}
1364+
1365+
// Attempt to add CAP_NET_ADMIN to the binary to improve network
1366+
// performance (optional, allow to fail). See `bootstrap_linux.sh`.
1367+
// TODO(mafredri): Disable for now until we can figure out why this
1368+
// causes the following error on some images:
1369+
//
1370+
//Image: mcr.microsoft.com/devcontainers/base:ubuntu
1371+
// Error: /.coder-agent/coder: Operation not permitted
1372+
//
1373+
// if _, err := api.ccli.ExecAs(ctx, container.ID, "root", "setcap", "cap_net_admin+ep", coderPathInsideContainer); err != nil {
1374+
// logger.Warn(ctx, "set CAP_NET_ADMIN on agent binary failed", slog.Error(err))
1375+
// }
1376+
12971377
deleteSubAgent:=proc.agent.ID!=uuid.Nil&&maybeRecreateSubAgent&&!proc.agent.EqualConfig(subAgentConfig)
12981378
ifdeleteSubAgent {
12991379
logger.Debug(ctx,"deleting existing subagent for recreation",slog.F("agent_id",proc.agent.ID))

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp