Skip to content

Commit d7da927

Browse files
committed
Merge branch 'main' into lilac/local-cli-install
2 parents 430f807 + 08dd2ab commit d7da927

File tree

109 files changed

+3288
-997
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+3288
-997
lines changed

.github/workflows/ci.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,10 @@ jobs:
445445
# C: drive is extremely slow: https://github.com/actions/runner-images/issues/8755
446446
mkdir -p "R:/temp/embedded-pg"
447447
go run scripts/embedded-pg/main.go -path "R:/temp/embedded-pg"
448-
DB=ci gotestsum --format standard-quiet -- -v -short -count=1 ./...
448+
# Reduce test parallelism, mirroring what we do for race tests.
449+
# We'd been encountering issues with timing related flakes, and
450+
# this seems to help.
451+
DB=ci gotestsum --format standard-quiet -- -v -short -count=1 -parallel 4 -p 4 ./...
449452
else
450453
go run scripts/embedded-pg/main.go
451454
DB=ci gotestsum --format standard-quiet -- -v -short -count=1 ./...

agent/agent_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import (
5858
)
5959

6060
func TestMain(m *testing.M) {
61-
goleak.VerifyTestMain(m)
61+
goleak.VerifyTestMain(m, testutil.GoleakOptions...)
6262
}
6363

6464
// NOTE: These tests only work when your default shell is bash for some reason.

agent/agentscripts/agentscripts_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
)
2525

2626
func TestMain(m *testing.M) {
27-
goleak.VerifyTestMain(m)
27+
goleak.VerifyTestMain(m, testutil.GoleakOptions...)
2828
}
2929

3030
func TestExecuteBasic(t *testing.T) {

agent/agentssh/agentssh_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
)
3030

3131
func TestMain(m *testing.M) {
32-
goleak.VerifyTestMain(m)
32+
goleak.VerifyTestMain(m, testutil.GoleakOptions...)
3333
}
3434

3535
func TestNewServer_ServeClient(t *testing.T) {

cli/clitest/clitest_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import (
88
"github.com/coder/coder/v2/cli/clitest"
99
"github.com/coder/coder/v2/coderd/coderdtest"
1010
"github.com/coder/coder/v2/pty/ptytest"
11+
"github.com/coder/coder/v2/testutil"
1112
)
1213

1314
func TestMain(m *testing.M) {
14-
goleak.VerifyTestMain(m)
15+
goleak.VerifyTestMain(m, testutil.GoleakOptions...)
1516
}
1617

1718
func TestCli(t *testing.T) {

cli/root_internal_test.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/coder/coder/v2/cli/cliui"
2020
"github.com/coder/coder/v2/cli/telemetry"
2121
"github.com/coder/coder/v2/codersdk"
22+
"github.com/coder/coder/v2/testutil"
2223
"github.com/coder/pretty"
2324
"github.com/coder/serpent"
2425
)
@@ -29,15 +30,7 @@ func TestMain(m *testing.M) {
2930
// See: https://github.com/coder/coder/issues/8954
3031
os.Exit(m.Run())
3132
}
32-
goleak.VerifyTestMain(m,
33-
// The lumberjack library is used by by agent and seems to leave
34-
// goroutines after Close(), fails TestGitSSH tests.
35-
// https://github.com/natefinch/lumberjack/pull/100
36-
goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"),
37-
goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).mill.func1"),
38-
// The pq library appears to leave around a goroutine after Close().
39-
goleak.IgnoreTopFunction("github.com/lib/pq.NewDialListener"),
40-
)
33+
goleak.VerifyTestMain(m, testutil.GoleakOptions...)
4134
}
4235

4336
func Test_formatExamples(t *testing.T) {

cli/ssh.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -657,12 +657,19 @@ func getWorkspaceAndAgent(ctx context.Context, inv *serpent.Invocation, client *
657657
// workspaces with the active version.
658658
_, _ = fmt.Fprintf(inv.Stderr, "Workspace was stopped, starting workspace to allow connecting to %q...\n", workspace.Name)
659659
_, err = startWorkspace(inv, client, workspace, workspaceParameterFlags{}, buildFlags{}, WorkspaceStart)
660-
if cerr, ok := codersdk.AsError(err); ok && cerr.StatusCode() == http.StatusForbidden {
661-
_, err = startWorkspace(inv, client, workspace, workspaceParameterFlags{}, buildFlags{}, WorkspaceUpdate)
662-
if err != nil {
663-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("start workspace with active template version: %w", err)
660+
if cerr, ok := codersdk.AsError(err); ok {
661+
switch cerr.StatusCode() {
662+
case http.StatusConflict:
663+
_, _ = fmt.Fprintln(inv.Stderr, "Unable to start the workspace due to conflict, the workspace may be starting, retrying without autostart...")
664+
return getWorkspaceAndAgent(ctx, inv, client, false, input)
665+
666+
case http.StatusForbidden:
667+
_, err = startWorkspace(inv, client, workspace, workspaceParameterFlags{}, buildFlags{}, WorkspaceUpdate)
668+
if err != nil {
669+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("start workspace with active template version: %w", err)
670+
}
671+
_, _ = fmt.Fprintln(inv.Stdout, "Unable to start the workspace with template version from last build. Your workspace has been updated to the current active template version.")
664672
}
665-
_, _ = fmt.Fprintln(inv.Stdout, "Unable to start the workspace with template version from last build. Your workspace has been updated to the current active template version.")
666673
} else if err != nil {
667674
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("start workspace with current template version: %w", err)
668675
}

cli/ssh_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"os/exec"
1818
"path"
1919
"path/filepath"
20+
"regexp"
2021
"runtime"
2122
"strings"
2223
"testing"
@@ -145,6 +146,101 @@ func TestSSH(t *testing.T) {
145146
pty.WriteLine("exit")
146147
<-cmdDone
147148
})
149+
t.Run("StartStoppedWorkspaceConflict", func(t *testing.T) {
150+
t.Parallel()
151+
152+
// Intercept builds to synchronize execution of the SSH command.
153+
// The purpose here is to make sure all commands try to trigger
154+
// a start build of the workspace.
155+
isFirstBuild := true
156+
buildURL := regexp.MustCompile("/api/v2/workspaces/.*/builds")
157+
buildPause := make(chan bool)
158+
buildDone := make(chan struct{})
159+
buildSyncMW := func(next http.Handler) http.Handler {
160+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
161+
if r.Method == http.MethodPost && buildURL.MatchString(r.URL.Path) {
162+
if !isFirstBuild {
163+
t.Log("buildSyncMW: pausing build")
164+
if shouldContinue := <-buildPause; !shouldContinue {
165+
// We can't force the API to trigger a build conflict (racy) so we fake it.
166+
t.Log("buildSyncMW: return conflict")
167+
w.WriteHeader(http.StatusConflict)
168+
return
169+
}
170+
t.Log("buildSyncMW: resuming build")
171+
defer func() {
172+
t.Log("buildSyncMW: sending build done")
173+
buildDone <- struct{}{}
174+
t.Log("buildSyncMW: done")
175+
}()
176+
} else {
177+
isFirstBuild = false
178+
}
179+
}
180+
next.ServeHTTP(w, r)
181+
})
182+
}
183+
184+
authToken := uuid.NewString()
185+
ownerClient := coderdtest.New(t, &coderdtest.Options{
186+
IncludeProvisionerDaemon: true,
187+
APIMiddleware: buildSyncMW,
188+
})
189+
owner := coderdtest.CreateFirstUser(t, ownerClient)
190+
client, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleTemplateAdmin())
191+
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{
192+
Parse: echo.ParseComplete,
193+
ProvisionPlan: echo.PlanComplete,
194+
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
195+
})
196+
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
197+
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
198+
workspace := coderdtest.CreateWorkspace(t, client, template.ID)
199+
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
200+
// Stop the workspace
201+
workspaceBuild := coderdtest.CreateWorkspaceBuild(t, client, workspace, database.WorkspaceTransitionStop)
202+
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspaceBuild.ID)
203+
204+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
205+
defer cancel()
206+
207+
var ptys []*ptytest.PTY
208+
for i := 0; i < 3; i++ {
209+
// SSH to the workspace which should autostart it
210+
inv, root := clitest.New(t, "ssh", workspace.Name)
211+
212+
pty := ptytest.New(t).Attach(inv)
213+
ptys = append(ptys, pty)
214+
clitest.SetupConfig(t, client, root)
215+
testutil.Go(t, func() {
216+
_ = inv.WithContext(ctx).Run()
217+
})
218+
}
219+
220+
for _, pty := range ptys {
221+
pty.ExpectMatchContext(ctx, "Workspace was stopped, starting workspace to allow connecting to")
222+
}
223+
224+
// Allow one build to complete.
225+
testutil.RequireSendCtx(ctx, t, buildPause, true)
226+
testutil.RequireRecvCtx(ctx, t, buildDone)
227+
228+
// Allow the remaining builds to continue.
229+
for i := 0; i < len(ptys)-1; i++ {
230+
testutil.RequireSendCtx(ctx, t, buildPause, false)
231+
}
232+
233+
var foundConflict int
234+
for _, pty := range ptys {
235+
// Either allow the command to start the workspace or fail
236+
// due to conflict (race), in which case it retries.
237+
match := pty.ExpectRegexMatchContext(ctx, "Waiting for the workspace agent to connect")
238+
if strings.Contains(match, "Unable to start the workspace due to conflict, the workspace may be starting, retrying without autostart...") {
239+
foundConflict++
240+
}
241+
}
242+
require.Equal(t, 2, foundConflict, "expected 2 conflicts")
243+
})
148244
t.Run("RequireActiveVersion", func(t *testing.T) {
149245
t.Parallel()
150246

coderd/apidoc/docs.go

Lines changed: 94 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)