Skip to content

Commit 5fa278d

Browse files
committed
Merge branch 'main' into abhineetjain/delete-session-token-api
2 parents 492db6a + d01a687 commit 5fa278d

File tree

87 files changed

+2703
-709
lines changed

Some content is hidden

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

87 files changed

+2703
-709
lines changed

.github/workflows/chromatic.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
# storybook snapshots will require manual approval/review in order for
4343
# the check to pass. This is desired in PRs, but not in mainline.
4444
- name: Publish to Chromatic (non-mainline)
45-
if: github.ref != 'refs/heads/main'
45+
if: github.ref != 'refs/heads/main' && github.repository_owner == 'coder'
4646
uses: chromaui/action@v1
4747
with:
4848
buildScriptName: "storybook:build"
@@ -59,7 +59,7 @@ jobs:
5959
# commits, but it's good to be defensive in case, otherwise CI remains
6060
# infinitely "in progress" in mainline unless we re-review each build.
6161
- name: Publish to Chromatic (mainline)
62-
if: github.ref == 'refs/heads/main'
62+
if: github.ref == 'refs/heads/main' && github.repository_owner == 'coder'
6363
uses: chromaui/action@v1
6464
with:
6565
autoAcceptChanges: true

.github/workflows/coder.yaml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ jobs:
208208
token: ${{ secrets.CODECOV_TOKEN }}
209209
files: ./gotests.coverage
210210
flags: unittest-go-${{ matrix.os }}
211-
fail_ci_if_error: true
211+
# this flakes and sometimes fails the build
212+
fail_ci_if_error: false
212213

213214
test-go-postgres:
214215
name: "test/go/postgres"
@@ -291,7 +292,8 @@ jobs:
291292
token: ${{ secrets.CODECOV_TOKEN }}
292293
files: ./gotests.coverage
293294
flags: unittest-go-postgres-${{ matrix.os }}
294-
fail_ci_if_error: true
295+
# this flakes and sometimes fails the build
296+
fail_ci_if_error: false
295297

296298
deploy:
297299
name: "deploy"
@@ -421,7 +423,8 @@ jobs:
421423
token: ${{ secrets.CODECOV_TOKEN }}
422424
files: ./site/coverage/lcov.info
423425
flags: unittest-js
424-
fail_ci_if_error: true
426+
# this flakes and sometimes fails the build
427+
fail_ci_if_error: false
425428

426429
- name: Upload DataDog Trace
427430
if: always() && github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"TCGETS",
6161
"tcpip",
6262
"TCSETS",
63+
"templateversions",
6364
"testid",
6465
"tfexec",
6566
"tfjson",

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ We recommend installing [the latest
5757
release](https://github.com/coder/coder/releases) on a system with at least 1
5858
CPU core and 2 GB RAM:
5959

60-
1. Download the release appropriate for your operating system
60+
1. Download the [release asset](https://github.com/coder/coder/releases) appropriate for your operating system
6161
1. Unzip the folder you just downloaded, and move the `coder` executable to a
6262
location that's on your `PATH`
6363

agent/agent.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,10 +359,11 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
359359
if err != nil {
360360
return nil, xerrors.Errorf("getting os executable: %w", err)
361361
}
362+
cmd.Env = append(cmd.Env, fmt.Sprintf(`PATH=%s%c%s`, os.Getenv("PATH"), filepath.ListSeparator, filepath.Dir(executablePath)))
362363
// Git on Windows resolves with UNIX-style paths.
363364
// If using backslashes, it's unable to find the executable.
364-
executablePath = strings.ReplaceAll(executablePath, "\\", "/")
365-
cmd.Env = append(cmd.Env, fmt.Sprintf(`GIT_SSH_COMMAND=%s gitssh --`, executablePath))
365+
unixExecutablePath := strings.ReplaceAll(executablePath, "\\", "/")
366+
cmd.Env = append(cmd.Env, fmt.Sprintf(`GIT_SSH_COMMAND=%s gitssh --`, unixExecutablePath))
366367
// These prevent the user from having to specify _anything_ to successfully commit.
367368
// Both author and committer must be set!
368369
cmd.Env = append(cmd.Env, fmt.Sprintf(`GIT_AUTHOR_EMAIL=%s`, metadata.OwnerEmail))

agent/agent_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,21 @@ func TestAgent(t *testing.T) {
6868
require.True(t, strings.HasSuffix(strings.TrimSpace(string(output)), "gitssh --"))
6969
})
7070

71+
t.Run("PATHHasCoder", func(t *testing.T) {
72+
t.Parallel()
73+
session := setupSSHSession(t, agent.Metadata{})
74+
command := "sh -c 'echo $PATH'"
75+
if runtime.GOOS == "windows" {
76+
command = "cmd.exe /c echo %PATH%"
77+
}
78+
output, err := session.Output(command)
79+
require.NoError(t, err)
80+
ex, err := os.Executable()
81+
t.Log(ex)
82+
require.NoError(t, err)
83+
require.True(t, strings.Contains(strings.TrimSpace(string(output)), filepath.Dir(ex)))
84+
})
85+
7186
t.Run("SessionTTY", func(t *testing.T) {
7287
t.Parallel()
7388
if runtime.GOOS == "windows" {

cli/server_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,19 @@ func TestServer(t *testing.T) {
9191
require.Eventually(t, func() bool {
9292
var err error
9393
token, err = cfg.Session().Read()
94-
return err == nil
94+
return err == nil && token != ""
9595
}, 15*time.Second, 25*time.Millisecond)
96+
9697
// Verify that authentication was properly set in dev-mode.
9798
accessURL, err := cfg.URL().Read()
9899
require.NoError(t, err)
99100
parsed, err := url.Parse(accessURL)
100101
require.NoError(t, err)
102+
101103
client := codersdk.New(parsed)
102104
client.SessionToken = token
103105
_, err = client.User(ctx, codersdk.Me)
104-
require.NoError(t, err)
106+
require.NoError(t, err, "token:", token)
105107

106108
cancelFunc()
107109
require.ErrorIs(t, <-errC, context.Canceled)

coderd/autobuild/executor/lifecycle_executor.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ func (e *Executor) Run() {
5050
func (e *Executor) runOnce(t time.Time) error {
5151
currentTick := t.Truncate(time.Minute)
5252
return e.db.InTx(func(db database.Store) error {
53+
// TTL is set at the workspace level, and deadline at the workspace build level.
54+
// When a workspace build is created, its deadline initially starts at zero.
55+
// When provisionerd successfully completes a provision job, the deadline is
56+
// set to now + TTL if the associated workspace has a TTL set. This deadline
57+
// is what we compare against when performing autostop operations, rounded down
58+
// to the minute.
59+
//
60+
// NOTE: Currently, if a workspace build is created with a given TTL and then
61+
// the user either changes or unsets the TTL, the deadline for the workspace
62+
// build will not have changed. So, autostop will still happen at the
63+
// original TTL value from when the workspace build was created.
64+
// Whether this is expected behavior from a user's perspective is not yet known.
5365
eligibleWorkspaces, err := db.GetWorkspacesAutostart(e.ctx)
5466
if err != nil {
5567
return xerrors.Errorf("get eligible workspaces for autostart or autostop: %w", err)
@@ -88,18 +100,15 @@ func (e *Executor) runOnce(t time.Time) error {
88100
switch priorHistory.Transition {
89101
case database.WorkspaceTransitionStart:
90102
validTransition = database.WorkspaceTransitionStop
91-
if !ws.Ttl.Valid || ws.Ttl.Int64 == 0 {
92-
e.log.Debug(e.ctx, "invalid or zero ws ttl, skipping",
103+
if priorHistory.Deadline.IsZero() {
104+
e.log.Debug(e.ctx, "latest workspace build has zero deadline, skipping",
93105
slog.F("workspace_id", ws.ID),
94-
slog.F("ttl", time.Duration(ws.Ttl.Int64)),
106+
slog.F("workspace_build_id", priorHistory.ID),
95107
)
96108
continue
97109
}
98-
ttl := time.Duration(ws.Ttl.Int64)
99-
// Measure TTL from the time the workspace finished building.
100-
// Truncate to nearest minute for consistency with autostart
101-
// behavior, and add one minute for padding.
102-
nextTransition = priorHistory.UpdatedAt.Truncate(time.Minute).Add(ttl + time.Minute)
110+
// Truncate to nearest minute for consistency with autostart behavior
111+
nextTransition = priorHistory.Deadline.Truncate(time.Minute)
103112
case database.WorkspaceTransitionStop:
104113
validTransition = database.WorkspaceTransitionStart
105114
sched, err := schedule.Weekly(ws.AutostartSchedule.String)

coderd/autobuild/executor/lifecycle_executor_test.go

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,14 @@ func TestExecutorAutostopOK(t *testing.T) {
190190
})
191191
// Given: we have a user with a workspace
192192
workspace = mustProvisionWorkspace(t, client)
193-
ttl = *workspace.TTL
194193
)
195194
// Given: workspace is running
196195
require.Equal(t, codersdk.WorkspaceTransitionStart, workspace.LatestBuild.Transition)
196+
require.NotZero(t, workspace.LatestBuild.Deadline)
197197

198-
// When: the autobuild executor ticks *after* the TTL:
198+
// When: the autobuild executor ticks *after* the deadline:
199199
go func() {
200-
tickCh <- time.Now().UTC().Add(ttl + time.Minute)
200+
tickCh <- workspace.LatestBuild.Deadline.Add(time.Minute)
201201
close(tickCh)
202202
}()
203203

@@ -209,6 +209,55 @@ func TestExecutorAutostopOK(t *testing.T) {
209209
require.Equal(t, codersdk.WorkspaceTransitionStop, ws.LatestBuild.Transition, "expected workspace not to be running")
210210
}
211211

212+
func TestExecutorAutostopExtend(t *testing.T) {
213+
t.Parallel()
214+
215+
var (
216+
ctx = context.Background()
217+
tickCh = make(chan time.Time)
218+
client = coderdtest.New(t, &coderdtest.Options{
219+
AutobuildTicker: tickCh,
220+
IncludeProvisionerD: true,
221+
})
222+
// Given: we have a user with a workspace
223+
workspace = mustProvisionWorkspace(t, client)
224+
originalDeadline = workspace.LatestBuild.Deadline
225+
)
226+
// Given: workspace is running
227+
require.Equal(t, codersdk.WorkspaceTransitionStart, workspace.LatestBuild.Transition)
228+
require.NotZero(t, originalDeadline)
229+
230+
// Given: we extend the workspace deadline
231+
err := client.PutExtendWorkspace(ctx, workspace.ID, codersdk.PutExtendWorkspaceRequest{
232+
Deadline: originalDeadline.Add(30 * time.Minute),
233+
})
234+
require.NoError(t, err, "extend workspace deadline")
235+
236+
// When: the autobuild executor ticks *after* the original deadline:
237+
go func() {
238+
tickCh <- originalDeadline.Add(time.Minute)
239+
}()
240+
241+
// Then: nothing should happen
242+
<-time.After(5 * time.Second)
243+
ws := mustWorkspace(t, client, workspace.ID)
244+
require.Equal(t, workspace.LatestBuild.ID, ws.LatestBuild.ID, "expected no further workspace builds to occur")
245+
require.Equal(t, codersdk.WorkspaceTransitionStart, ws.LatestBuild.Transition, "expected workspace to be running")
246+
247+
// When: the autobuild executor ticks after the *new* deadline:
248+
go func() {
249+
tickCh <- ws.LatestBuild.Deadline.Add(time.Minute)
250+
close(tickCh)
251+
}()
252+
253+
// Then: the workspace should be stopped
254+
<-time.After(5 * time.Second)
255+
ws = mustWorkspace(t, client, workspace.ID)
256+
require.NotEqual(t, workspace.LatestBuild.ID, ws.LatestBuild.ID, "expected a workspace build to occur")
257+
require.Equal(t, codersdk.ProvisionerJobSucceeded, ws.LatestBuild.Job.Status, "expected provisioner job to have succeeded")
258+
require.Equal(t, codersdk.WorkspaceTransitionStop, ws.LatestBuild.Transition, "expected workspace not to be running")
259+
}
260+
212261
func TestExecutorAutostopAlreadyStopped(t *testing.T) {
213262
t.Parallel()
214263

@@ -222,15 +271,14 @@ func TestExecutorAutostopAlreadyStopped(t *testing.T) {
222271
workspace = mustProvisionWorkspace(t, client, func(cwr *codersdk.CreateWorkspaceRequest) {
223272
cwr.AutostartSchedule = nil
224273
})
225-
ttl = *workspace.TTL
226274
)
227275

228276
// Given: workspace is stopped
229277
workspace = mustTransitionWorkspace(t, client, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
230278

231279
// When: the autobuild executor ticks past the TTL
232280
go func() {
233-
tickCh <- time.Now().UTC().Add(ttl + time.Minute)
281+
tickCh <- workspace.LatestBuild.Deadline.Add(time.Minute)
234282
close(tickCh)
235283
}()
236284

@@ -264,7 +312,7 @@ func TestExecutorAutostopNotEnabled(t *testing.T) {
264312

265313
// When: the autobuild executor ticks past the TTL
266314
go func() {
267-
tickCh <- time.Now().UTC().Add(time.Minute)
315+
tickCh <- workspace.LatestBuild.Deadline.Add(time.Minute)
268316
close(tickCh)
269317
}()
270318

@@ -352,7 +400,7 @@ func TestExecutorWorkspaceAutostartTooEarly(t *testing.T) {
352400
require.Equal(t, codersdk.WorkspaceTransitionStart, ws.LatestBuild.Transition, "expected workspace to be running")
353401
}
354402

355-
func TestExecutorWorkspaceTTLTooEarly(t *testing.T) {
403+
func TestExecutorWorkspaceAutostopBeforeDeadline(t *testing.T) {
356404
t.Parallel()
357405

358406
var (
@@ -367,7 +415,7 @@ func TestExecutorWorkspaceTTLTooEarly(t *testing.T) {
367415

368416
// When: the autobuild executor ticks before the TTL
369417
go func() {
370-
tickCh <- time.Now().UTC()
418+
tickCh <- workspace.LatestBuild.Deadline.Add(-1 * time.Minute)
371419
close(tickCh)
372420
}()
373421

@@ -378,6 +426,38 @@ func TestExecutorWorkspaceTTLTooEarly(t *testing.T) {
378426
require.Equal(t, codersdk.WorkspaceTransitionStart, ws.LatestBuild.Transition, "expected workspace to be running")
379427
}
380428

429+
func TestExecutorWorkspaceAutostopNoWaitChangedMyMind(t *testing.T) {
430+
t.Parallel()
431+
432+
var (
433+
ctx = context.Background()
434+
tickCh = make(chan time.Time)
435+
client = coderdtest.New(t, &coderdtest.Options{
436+
AutobuildTicker: tickCh,
437+
IncludeProvisionerD: true,
438+
})
439+
// Given: we have a user with a workspace
440+
workspace = mustProvisionWorkspace(t, client)
441+
)
442+
443+
// Given: the user changes their mind and decides their workspace should not auto-stop
444+
err := client.UpdateWorkspaceTTL(ctx, workspace.ID, codersdk.UpdateWorkspaceTTLRequest{TTL: nil})
445+
require.NoError(t, err)
446+
447+
// When: the autobuild executor ticks after the deadline
448+
go func() {
449+
tickCh <- workspace.LatestBuild.Deadline.Add(time.Minute)
450+
close(tickCh)
451+
}()
452+
453+
// Then: the workspace should still stop - sorry!
454+
<-time.After(5 * time.Second)
455+
ws := mustWorkspace(t, client, workspace.ID)
456+
require.NotEqual(t, workspace.LatestBuild.ID, ws.LatestBuild.ID, "expected a workspace build to occur")
457+
require.Equal(t, codersdk.ProvisionerJobSucceeded, ws.LatestBuild.Job.Status, "expected provisioner job to have succeeded")
458+
require.Equal(t, codersdk.WorkspaceTransitionStop, ws.LatestBuild.Transition, "expected workspace not to be running")
459+
}
460+
381461
func TestExecutorAutostartMultipleOK(t *testing.T) {
382462
if os.Getenv("DB") == "" {
383463
t.Skip(`This test only really works when using a "real" database, similar to a HA setup`)

coderd/coderd.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,20 @@ func New(options *Options) *API {
131131
r.Get("/{hash}", api.fileByHash)
132132
r.Post("/", api.postFile)
133133
})
134+
r.Route("/provisionerdaemons", func(r chi.Router) {
135+
r.Use(
136+
apiKeyMiddleware,
137+
authRolesMiddleware,
138+
)
139+
r.Get("/", api.provisionerDaemons)
140+
})
134141
r.Route("/organizations/{organization}", func(r chi.Router) {
135142
r.Use(
136143
apiKeyMiddleware,
137144
httpmw.ExtractOrganizationParam(options.Database),
138145
authRolesMiddleware,
139146
)
140147
r.Get("/", api.organization)
141-
r.Get("/provisionerdaemons", api.provisionerDaemonsByOrganization)
142148
r.Post("/templateversions", api.postTemplateVersionsByOrganization)
143149
r.Route("/templates", func(r chi.Router) {
144150
r.Post("/", api.postTemplateByOrganization)
@@ -166,7 +172,7 @@ func New(options *Options) *API {
166172
})
167173
})
168174
r.Route("/parameters/{scope}/{id}", func(r chi.Router) {
169-
r.Use(apiKeyMiddleware)
175+
r.Use(apiKeyMiddleware, authRolesMiddleware)
170176
r.Post("/", api.postParameter)
171177
r.Get("/", api.parameters)
172178
r.Route("/{name}", func(r chi.Router) {
@@ -309,6 +315,7 @@ func New(options *Options) *API {
309315
r.Put("/", api.putWorkspaceTTL)
310316
})
311317
r.Get("/watch", api.watchWorkspace)
318+
r.Put("/extend", api.putExtendWorkspace)
312319
})
313320
})
314321
r.Route("/workspacebuilds/{workspacebuild}", func(r chi.Router) {

0 commit comments

Comments
 (0)