Skip to content

Commit 4b73034

Browse files
committed
Merge branch 'main' into apps
2 parents 866eeed + 1361c13 commit 4b73034

File tree

87 files changed

+3185
-705
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

+3185
-705
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
@@ -61,6 +61,7 @@
6161
"TCGETS",
6262
"tcpip",
6363
"TCSETS",
64+
"templateversions",
6465
"testid",
6566
"tfexec",
6667
"tfjson",

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Discord"](https://img.shields.io/badge/join-us%20on%20Discord-gray.svg?longCache
88
Follow](https://img.shields.io/twitter/follow/CoderHQ?label=%40CoderHQ&style=social)](https://twitter.com/coderhq)
99
[![codecov](https://codecov.io/gh/coder/coder/branch/main/graph/badge.svg?token=TNLW3OAP6G)](https://codecov.io/gh/coder/coder)
1010

11-
Coder turns your cloud into a fleet of remote development servers.
11+
Coder creates remote development machines so you can develop your code from anywhere
1212

1313
<p align="center">
1414
<img src="./docs/images/hero-image.png">
@@ -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: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,10 +359,12 @@ 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("USER=%s", username))
363+
cmd.Env = append(cmd.Env, fmt.Sprintf(`PATH=%s%c%s`, os.Getenv("PATH"), filepath.ListSeparator, filepath.Dir(executablePath)))
362364
// Git on Windows resolves with UNIX-style paths.
363365
// 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))
366+
unixExecutablePath := strings.ReplaceAll(executablePath, "\\", "/")
367+
cmd.Env = append(cmd.Env, fmt.Sprintf(`GIT_SSH_COMMAND=%s gitssh --`, unixExecutablePath))
366368
// These prevent the user from having to specify _anything_ to successfully commit.
367369
// Both author and committer must be set!
368370
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
@@ -143,14 +143,20 @@ func New(options *Options) *API {
143143
r.Get("/{hash}", api.fileByHash)
144144
r.Post("/", api.postFile)
145145
})
146+
r.Route("/provisionerdaemons", func(r chi.Router) {
147+
r.Use(
148+
apiKeyMiddleware,
149+
authRolesMiddleware,
150+
)
151+
r.Get("/", api.provisionerDaemons)
152+
})
146153
r.Route("/organizations/{organization}", func(r chi.Router) {
147154
r.Use(
148155
apiKeyMiddleware,
149156
httpmw.ExtractOrganizationParam(options.Database),
150157
authRolesMiddleware,
151158
)
152159
r.Get("/", api.organization)
153-
r.Get("/provisionerdaemons", api.provisionerDaemonsByOrganization)
154160
r.Post("/templateversions", api.postTemplateVersionsByOrganization)
155161
r.Route("/templates", func(r chi.Router) {
156162
r.Post("/", api.postTemplateByOrganization)
@@ -178,7 +184,7 @@ func New(options *Options) *API {
178184
})
179185
})
180186
r.Route("/parameters/{scope}/{id}", func(r chi.Router) {
181-
r.Use(apiKeyMiddleware)
187+
r.Use(apiKeyMiddleware, authRolesMiddleware)
182188
r.Post("/", api.postParameter)
183189
r.Get("/", api.parameters)
184190
r.Route("/{name}", func(r chi.Router) {
@@ -321,6 +327,7 @@ func New(options *Options) *API {
321327
r.Put("/", api.putWorkspaceTTL)
322328
})
323329
r.Get("/watch", api.watchWorkspace)
330+
r.Put("/extend", api.putExtendWorkspace)
324331
})
325332
})
326333
r.Route("/wildcardauth", func(r chi.Router) {

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