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

Commitc3bc1e7

Browse files
feat(agent/agentcontainers): fall back to workspace folder name (#18466)
This PR changes the logic for how we decide on an agent name.Previously it followed these steps:1. Use a name from `customizations.coder.name`2. Use a name from the terraform resource `coder_devcontainer`3. Use the dev container's friendly nameWith this change it now does:1. Use a name from `customizations.coder.name`2. Use a name from the terraform resource `coder_devcontainer`3. Use a name from the workspace folder4. Use the dev container's friendly nameWe now attempt to construct a valid agent name from the workspacefolder. Should we fail to construct a valid agent name from theworkspace folder, we will fall back to the dev container's friendlyname.---------Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent0a12ec5 commitc3bc1e7

File tree

3 files changed

+255
-18
lines changed

3 files changed

+255
-18
lines changed

‎agent/agentcontainers/api.go

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"os"
99
"path"
1010
"path/filepath"
11+
"regexp"
1112
"runtime"
1213
"slices"
1314
"strings"
@@ -39,6 +40,8 @@ const (
3940
// by tmpfs or other mounts. This assumes the container root filesystem is
4041
// read-write, which seems sensible for devcontainers.
4142
coderPathInsideContainer="/.coder-agent/coder"
43+
44+
maxAgentNameLength=64
4245
)
4346

4447
// API is responsible for container-related operations in the agent.
@@ -583,10 +586,11 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code
583586
ifdc.Container!=nil {
584587
if!api.devcontainerNames[dc.Name] {
585588
// If the devcontainer name wasn't set via terraform, we
586-
// use the containers friendly name as a fallback which
587-
// will keep changing as the devcontainer is recreated.
588-
// TODO(mafredri): Parse the container label (i.e. devcontainer.json) for customization.
589-
dc.Name=safeFriendlyName(dc.Container.FriendlyName)
589+
// will attempt to create an agent name based on the workspace
590+
// folder's name. If it is not possible to generate a valid
591+
// agent name based off of the folder name (i.e. no valid characters),
592+
// we will instead fall back to using the container's friendly name.
593+
dc.Name=safeAgentName(path.Base(filepath.ToSlash(dc.WorkspaceFolder)),dc.Container.FriendlyName)
590594
}
591595
}
592596

@@ -631,6 +635,38 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code
631635
api.containersErr=nil
632636
}
633637

638+
varconsecutiveHyphenRegex=regexp.MustCompile("-+")
639+
640+
// `safeAgentName` returns a safe agent name derived from a folder name,
641+
// falling back to the container’s friendly name if needed.
642+
funcsafeAgentName(namestring,friendlyNamestring)string {
643+
// Keep only ASCII letters and digits, replacing everything
644+
// else with a hyphen.
645+
varsb strings.Builder
646+
for_,r:=rangestrings.ToLower(name) {
647+
if (r>='a'&&r<='z')|| (r>='0'&&r<='9') {
648+
_,_=sb.WriteRune(r)
649+
}else {
650+
_,_=sb.WriteRune('-')
651+
}
652+
}
653+
654+
// Remove any consecutive hyphens, and then trim any leading
655+
// and trailing hyphens.
656+
name=consecutiveHyphenRegex.ReplaceAllString(sb.String(),"-")
657+
name=strings.Trim(name,"-")
658+
659+
// Ensure the name of the agent doesn't exceed the maximum agent
660+
// name length.
661+
name=name[:min(len(name),maxAgentNameLength)]
662+
663+
ifprovisioner.AgentNameRegex.Match([]byte(name)) {
664+
returnname
665+
}
666+
667+
returnsafeFriendlyName(friendlyName)
668+
}
669+
634670
// safeFriendlyName returns a API safe version of the container's
635671
// friendly name.
636672
//
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
package agentcontainers
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
8+
"github.com/coder/coder/v2/provisioner"
9+
)
10+
11+
funcTestSafeAgentName(t*testing.T) {
12+
t.Parallel()
13+
14+
tests:= []struct {
15+
namestring
16+
folderNamestring
17+
expectedstring
18+
}{
19+
// Basic valid names
20+
{
21+
folderName:"simple",
22+
expected:"simple",
23+
},
24+
{
25+
folderName:"with-hyphens",
26+
expected:"with-hyphens",
27+
},
28+
{
29+
folderName:"123numbers",
30+
expected:"123numbers",
31+
},
32+
{
33+
folderName:"mixed123",
34+
expected:"mixed123",
35+
},
36+
37+
// Names that need transformation
38+
{
39+
folderName:"With_Underscores",
40+
expected:"with-underscores",
41+
},
42+
{
43+
folderName:"With Spaces",
44+
expected:"with-spaces",
45+
},
46+
{
47+
folderName:"UPPERCASE",
48+
expected:"uppercase",
49+
},
50+
{
51+
folderName:"Mixed_Case-Name",
52+
expected:"mixed-case-name",
53+
},
54+
55+
// Names with special characters that get replaced
56+
{
57+
folderName:"special@#$chars",
58+
expected:"special-chars",
59+
},
60+
{
61+
folderName:"dots.and.more",
62+
expected:"dots-and-more",
63+
},
64+
{
65+
folderName:"multiple___underscores",
66+
expected:"multiple-underscores",
67+
},
68+
{
69+
folderName:"multiple---hyphens",
70+
expected:"multiple-hyphens",
71+
},
72+
73+
// Edge cases with leading/trailing special chars
74+
{
75+
folderName:"-leading-hyphen",
76+
expected:"leading-hyphen",
77+
},
78+
{
79+
folderName:"trailing-hyphen-",
80+
expected:"trailing-hyphen",
81+
},
82+
{
83+
folderName:"_leading_underscore",
84+
expected:"leading-underscore",
85+
},
86+
{
87+
folderName:"trailing_underscore_",
88+
expected:"trailing-underscore",
89+
},
90+
{
91+
folderName:"---multiple-leading",
92+
expected:"multiple-leading",
93+
},
94+
{
95+
folderName:"trailing-multiple---",
96+
expected:"trailing-multiple",
97+
},
98+
99+
// Complex transformation cases
100+
{
101+
folderName:"___very---complex@@@name___",
102+
expected:"very-complex-name",
103+
},
104+
{
105+
folderName:"my.project-folder_v2",
106+
expected:"my-project-folder-v2",
107+
},
108+
109+
// Empty and fallback cases - now correctly uses friendlyName fallback
110+
{
111+
folderName:"",
112+
expected:"friendly-fallback",
113+
},
114+
{
115+
folderName:"---",
116+
expected:"friendly-fallback",
117+
},
118+
{
119+
folderName:"___",
120+
expected:"friendly-fallback",
121+
},
122+
{
123+
folderName:"@#$",
124+
expected:"friendly-fallback",
125+
},
126+
127+
// Additional edge cases
128+
{
129+
folderName:"a",
130+
expected:"a",
131+
},
132+
{
133+
folderName:"1",
134+
expected:"1",
135+
},
136+
{
137+
folderName:"a1b2c3",
138+
expected:"a1b2c3",
139+
},
140+
{
141+
folderName:"CamelCase",
142+
expected:"camelcase",
143+
},
144+
{
145+
folderName:"snake_case_name",
146+
expected:"snake-case-name",
147+
},
148+
{
149+
folderName:"kebab-case-name",
150+
expected:"kebab-case-name",
151+
},
152+
{
153+
folderName:"mix3d_C4s3-N4m3",
154+
expected:"mix3d-c4s3-n4m3",
155+
},
156+
{
157+
folderName:"123-456-789",
158+
expected:"123-456-789",
159+
},
160+
{
161+
folderName:"abc123def456",
162+
expected:"abc123def456",
163+
},
164+
{
165+
folderName:" spaces everywhere ",
166+
expected:"spaces-everywhere",
167+
},
168+
{
169+
folderName:"unicode-café-naïve",
170+
expected:"unicode-caf-na-ve",
171+
},
172+
{
173+
folderName:"path/with/slashes",
174+
expected:"path-with-slashes",
175+
},
176+
{
177+
folderName:"file.tar.gz",
178+
expected:"file-tar-gz",
179+
},
180+
{
181+
folderName:"version-1.2.3-alpha",
182+
expected:"version-1-2-3-alpha",
183+
},
184+
185+
// Truncation test for names exceeding 64 characters
186+
{
187+
folderName:"this-is-a-very-long-folder-name-that-exceeds-sixty-four-characters-limit-and-should-be-truncated",
188+
expected:"this-is-a-very-long-folder-name-that-exceeds-sixty-four-characte",
189+
},
190+
}
191+
192+
for_,tt:=rangetests {
193+
t.Run(tt.folderName,func(t*testing.T) {
194+
t.Parallel()
195+
name:=safeAgentName(tt.folderName,"friendly-fallback")
196+
197+
assert.Equal(t,tt.expected,name)
198+
assert.True(t,provisioner.AgentNameRegex.Match([]byte(name)))
199+
})
200+
}
201+
}

‎agent/agentcontainers/api_test.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -897,26 +897,26 @@ func TestAPI(t *testing.T) {
897897
FriendlyName:"project1-container",
898898
Running:true,
899899
Labels:map[string]string{
900-
agentcontainers.DevcontainerLocalFolderLabel:"/workspace/project",
901-
agentcontainers.DevcontainerConfigFileLabel:"/workspace/project/.devcontainer/devcontainer.json",
900+
agentcontainers.DevcontainerLocalFolderLabel:"/workspace/project1",
901+
agentcontainers.DevcontainerConfigFileLabel:"/workspace/project1/.devcontainer/devcontainer.json",
902902
},
903903
},
904904
{
905905
ID:"project2-container",
906906
FriendlyName:"project2-container",
907907
Running:true,
908908
Labels:map[string]string{
909-
agentcontainers.DevcontainerLocalFolderLabel:"/home/user/project",
910-
agentcontainers.DevcontainerConfigFileLabel:"/home/user/project/.devcontainer/devcontainer.json",
909+
agentcontainers.DevcontainerLocalFolderLabel:"/home/user/project2",
910+
agentcontainers.DevcontainerConfigFileLabel:"/home/user/project2/.devcontainer/devcontainer.json",
911911
},
912912
},
913913
{
914914
ID:"project3-container",
915915
FriendlyName:"project3-container",
916916
Running:true,
917917
Labels:map[string]string{
918-
agentcontainers.DevcontainerLocalFolderLabel:"/var/lib/project",
919-
agentcontainers.DevcontainerConfigFileLabel:"/var/lib/project/.devcontainer/devcontainer.json",
918+
agentcontainers.DevcontainerLocalFolderLabel:"/var/lib/project3",
919+
agentcontainers.DevcontainerConfigFileLabel:"/var/lib/project3/.devcontainer/devcontainer.json",
920920
},
921921
},
922922
},
@@ -1326,7 +1326,7 @@ func TestAPI(t *testing.T) {
13261326
// Allow initial agent creation and injection to succeed.
13271327
testutil.RequireSend(ctx,t,fakeSAC.createErrC,nil)
13281328
testutil.RequireSend(ctx,t,fakeDCCLI.readConfigErrC,func(envs []string)error {
1329-
assert.Contains(t,envs,"CODER_WORKSPACE_AGENT_NAME=test-container")
1329+
assert.Contains(t,envs,"CODER_WORKSPACE_AGENT_NAME=coder")
13301330
assert.Contains(t,envs,"CODER_WORKSPACE_NAME=test-workspace")
13311331
assert.Contains(t,envs,"CODER_WORKSPACE_OWNER_NAME=test-user")
13321332
assert.Contains(t,envs,"CODER_URL=test-subagent-url")
@@ -1349,7 +1349,7 @@ func TestAPI(t *testing.T) {
13491349

13501350
// Verify agent was created.
13511351
require.Len(t,fakeSAC.created,1)
1352-
assert.Equal(t,"test-container",fakeSAC.created[0].Name)
1352+
assert.Equal(t,"coder",fakeSAC.created[0].Name)
13531353
assert.Equal(t,"/workspaces/coder",fakeSAC.created[0].Directory)
13541354
assert.Len(t,fakeSAC.deleted,0)
13551355

@@ -1405,7 +1405,7 @@ func TestAPI(t *testing.T) {
14051405
WaitStartLoop:
14061406
for {
14071407
// Agent reinjection will succeed and we will not re-create the
1408-
// agent, nor re-probe pwd.
1408+
// agent.
14091409
mCCLI.EXPECT().List(gomock.Any()).Return(codersdk.WorkspaceAgentListContainersResponse{
14101410
Containers: []codersdk.WorkspaceAgentContainer{testContainer},
14111411
},nil).Times(1)// 1 update.
@@ -1468,7 +1468,7 @@ func TestAPI(t *testing.T) {
14681468
// Expect the agent to be recreated.
14691469
testutil.RequireSend(ctx,t,fakeSAC.createErrC,nil)
14701470
testutil.RequireSend(ctx,t,fakeDCCLI.readConfigErrC,func(envs []string)error {
1471-
assert.Contains(t,envs,"CODER_WORKSPACE_AGENT_NAME=test-container")
1471+
assert.Contains(t,envs,"CODER_WORKSPACE_AGENT_NAME=coder")
14721472
assert.Contains(t,envs,"CODER_WORKSPACE_NAME=test-workspace")
14731473
assert.Contains(t,envs,"CODER_WORKSPACE_OWNER_NAME=test-user")
14741474
assert.Contains(t,envs,"CODER_URL=test-subagent-url")
@@ -1910,8 +1910,8 @@ func TestAPI(t *testing.T) {
19101910
Running:true,
19111911
CreatedAt:time.Now(),
19121912
Labels:map[string]string{
1913-
agentcontainers.DevcontainerLocalFolderLabel:"/workspaces",
1914-
agentcontainers.DevcontainerConfigFileLabel:"/workspace/.devcontainer/devcontainer.json",
1913+
agentcontainers.DevcontainerLocalFolderLabel:"/workspaces/coder",
1914+
agentcontainers.DevcontainerConfigFileLabel:"/workspaces/coder/.devcontainer/devcontainer.json",
19151915
},
19161916
}
19171917
)
@@ -1953,13 +1953,13 @@ func TestAPI(t *testing.T) {
19531953
testutil.RequireSend(ctx,t,fSAC.createErrC,nil)
19541954
testutil.RequireSend(ctx,t,fDCCLI.readConfigErrC,func(env []string)error {
19551955
// We expect the wrong workspace agent name passed in first.
1956-
assert.Contains(t,env,"CODER_WORKSPACE_AGENT_NAME=test-container")
1956+
assert.Contains(t,env,"CODER_WORKSPACE_AGENT_NAME=coder")
19571957
returnnil
19581958
})
19591959
testutil.RequireSend(ctx,t,fDCCLI.readConfigErrC,func(env []string)error {
19601960
// We then expect the agent name passed here to have been read from the config.
19611961
assert.Contains(t,env,"CODER_WORKSPACE_AGENT_NAME=custom-name")
1962-
assert.NotContains(t,env,"CODER_WORKSPACE_AGENT_NAME=test-container")
1962+
assert.NotContains(t,env,"CODER_WORKSPACE_AGENT_NAME=coder")
19631963
returnnil
19641964
})
19651965

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp