Skip to content

Commit 5385c2c

Browse files
committed
Merge branch 'main' into negotiatemutex
2 parents a7a1ec5 + e2030bb commit 5385c2c

File tree

101 files changed

+3184
-846
lines changed

Some content is hidden

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

101 files changed

+3184
-846
lines changed

.github/workflows/chromatic.yaml

+2-2
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

+6-3
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

+1
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",

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ INSTALL_DIR=$(shell go env GOPATH)/bin
44
GOOS=$(shell go env GOOS)
55
GOARCH=$(shell go env GOARCH)
66

7-
bin: $(shell find . -not -path './vendor/*' -type f -name '*.go') go.mod go.sum
7+
bin: $(shell find . -not -path './vendor/*' -type f -name '*.go') go.mod go.sum $(shell find ./examples/templates)
88
@echo "== This builds binaries for command-line usage."
99
@echo "== Use \"make build\" to embed the site."
1010
goreleaser build --snapshot --rm-dist --single-target
@@ -24,7 +24,7 @@ dev:
2424
./scripts/develop.sh
2525
.PHONY: dev
2626

27-
dist/artifacts.json: site/out/index.html $(shell find . -not -path './vendor/*' -type f -name '*.go') go.mod go.sum
27+
dist/artifacts.json: site/out/index.html $(shell find . -not -path './vendor/*' -type f -name '*.go') go.mod go.sum $(shell find ./examples/templates)
2828
goreleaser release --snapshot --rm-dist --skip-sign
2929

3030
fmt/prettier:

README.md

+20-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ 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.
12+
13+
> **Note**:
14+
> Coder is in an alpha state, but any serious bugs are P1 for us so please report them.
1215
1316
<p align="center">
1417
<img src="./docs/images/hero-image.png">
@@ -57,7 +60,7 @@ We recommend installing [the latest
5760
release](https://github.com/coder/coder/releases) on a system with at least 1
5861
CPU core and 2 GB RAM:
5962

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

@@ -147,10 +150,23 @@ coder templates update gcp-linux
147150
- [Workspace lifecycle](./docs/workspaces.md#workspace-lifecycle)
148151
- [Updating workspaces](./docs/workspaces.md#updating-workspaces)
149152

153+
## Comparison
154+
155+
Please file [an issue](https://github.com/coder/coder/issues/new) if any information is out of date. Also refer to: [What Coder is not](./docs/about.md#what-coder-is-not).
156+
157+
| Tool | Type | Delivery Model | Cost | Environments |
158+
| :---------------------------------------------------------- | :------- | :----------------- | :---------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- |
159+
| [Coder](https://github.com/coder/coder) | Platform | OSS + Self-Managed | Pay your cloud | All [Terraform](https://www.terraform.io/registry/providers) resources, all clouds, multi-architecture: Linux, Mac, Windows, containers, VMs, amd64, arm64 |
160+
| [code-server](https://github.com/cdr/code-server) | Web IDE | OSS + Self-Managed | Pay your cloud | Linux, Mac, Windows, containers, VMs, amd64, arm64 |
161+
| [Coder (Classic)](https://coder.com/docs) | Platform | Self-Managed | Pay your cloud + license fees | Kubernetes Linux Containers |
162+
| [GitHub Codespaces](https://github.com/features/codespaces) | Platform | SaaS | 2x Azure Compute | Linux containers |
163+
164+
---
165+
166+
_As of 5/27/22_
167+
150168
## Contributing
151169

152170
Read the [contributing docs](./docs/CONTRIBUTING.md).
153171

154-
## Contributors
155-
156172
Find our list of contributors [here](./docs/CONTRIBUTORS.md).

agent/agent.go

+4-2
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

+15
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

+4-2
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

+17-8
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

+88-8
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`)

0 commit comments

Comments
 (0)