Skip to content

Commit af8190a

Browse files
committed
Merge branch 'main' into telemetry
2 parents b74d127 + c570501 commit af8190a

31 files changed

+394
-170
lines changed

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,9 @@ test: test-clean
115115
.PHONY: test-postgres
116116
test-postgres: test-clean
117117
DB=ci gotestsum --junitfile="gotests.xml" --packages="./..." -- \
118-
-covermode=atomic -coverprofile="gotests.coverage" -timeout=10m \
118+
-covermode=atomic -coverprofile="gotests.coverage" -timeout=30m \
119119
-coverpkg=./...,github.com/coder/coder/codersdk \
120-
-count=1 -parallel=1 -race -failfast
120+
-count=1 -race -failfast
121121

122122

123123
.PHONY: test-postgres-docker

cli/cliui/prompt_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ func newPrompt(ptty *ptytest.PTY, opts cliui.PromptOptions, cmdOpt func(cmd *cob
165165
}
166166

167167
func TestPasswordTerminalState(t *testing.T) {
168+
// TODO: fix this test so that it runs reliably
169+
t.Skip()
170+
168171
if os.Getenv("TEST_SUBPROCESS") == "1" {
169172
passwordHelper()
170173
return
@@ -205,6 +208,7 @@ func TestPasswordTerminalState(t *testing.T) {
205208
require.True(t, echo, "echo is off after reading password")
206209
}
207210

211+
// nolint:unused
208212
func passwordHelper() {
209213
cmd := &cobra.Command{
210214
Run: func(cmd *cobra.Command, args []string) {

cli/list_test.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"testing"
66
"time"
77

8-
"github.com/stretchr/testify/require"
8+
"github.com/stretchr/testify/assert"
99

1010
"github.com/coder/coder/cli/clitest"
1111
"github.com/coder/coder/coderd/coderdtest"
@@ -30,13 +30,15 @@ func TestList(t *testing.T) {
3030
pty := ptytest.New(t)
3131
cmd.SetIn(pty.Input())
3232
cmd.SetOut(pty.Output())
33-
errC := make(chan error)
33+
done := make(chan any)
3434
go func() {
35-
errC <- cmd.ExecuteContext(ctx)
35+
errC := cmd.ExecuteContext(ctx)
36+
assert.NoError(t, errC)
37+
close(done)
3638
}()
3739
pty.ExpectMatch(workspace.Name)
3840
pty.ExpectMatch("Running")
3941
cancelFunc()
40-
require.NoError(t, <-errC)
42+
<-done
4143
})
4244
}

cli/server.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"github.com/pion/turn/v2"
3434
"github.com/pion/webrtc/v3"
3535
"github.com/prometheus/client_golang/prometheus/promhttp"
36+
"github.com/spf13/afero"
3637
"github.com/spf13/cobra"
3738
sdktrace "go.opentelemetry.io/otel/sdk/trace"
3839
"golang.org/x/oauth2"
@@ -602,7 +603,7 @@ func newProvisionerDaemon(ctx context.Context, coderAPI *coderd.API,
602603
if dev {
603604
echoClient, echoServer := provisionersdk.TransportPipe()
604605
go func() {
605-
err := echo.Serve(ctx, &provisionersdk.ServeOptions{Listener: echoServer})
606+
err := echo.Serve(ctx, afero.NewOsFs(), &provisionersdk.ServeOptions{Listener: echoServer})
606607
if err != nil {
607608
errChan <- err
608609
}

coderd/coderdtest/coderdtest.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
"testing"
2626
"time"
2727

28+
"github.com/spf13/afero"
29+
2830
"github.com/coder/coder/coderd/rbac"
2931
"github.com/coder/coder/coderd/telemetry"
3032
"github.com/coder/coder/coderd/util/ptr"
@@ -192,18 +194,20 @@ func NewProvisionerDaemon(t *testing.T, coderAPI *coderd.API) io.Closer {
192194
_ = echoServer.Close()
193195
cancelFunc()
194196
})
197+
fs := afero.NewMemMapFs()
195198
go func() {
196-
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
199+
err := echo.Serve(ctx, fs, &provisionersdk.ServeOptions{
197200
Listener: echoServer,
198201
})
199202
assert.NoError(t, err)
200203
}()
201204

202205
closer := provisionerd.New(coderAPI.ListenProvisionerDaemon, &provisionerd.Options{
206+
Filesystem: fs,
203207
Logger: slogtest.Make(t, nil).Named("provisionerd").Leveled(slog.LevelDebug),
204-
PollInterval: 50 * time.Millisecond,
205-
UpdateInterval: 250 * time.Millisecond,
206-
ForceCancelInterval: 250 * time.Millisecond,
208+
PollInterval: 10 * time.Millisecond,
209+
UpdateInterval: 25 * time.Millisecond,
210+
ForceCancelInterval: 25 * time.Millisecond,
207211
Provisioners: provisionerd.Provisioners{
208212
string(database.ProvisionerTypeEcho): proto.NewDRPCProvisionerClient(provisionersdk.Conn(echoClient)),
209213
},

coderd/database/migrate.go

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package database
22

33
import (
4+
"context"
45
"database/sql"
56
"embed"
67
"errors"
@@ -17,12 +18,21 @@ import (
1718
var migrations embed.FS
1819

1920
func migrateSetup(db *sql.DB) (source.Driver, *migrate.Migrate, error) {
21+
ctx := context.Background()
2022
sourceDriver, err := iofs.New(migrations, "migrations")
2123
if err != nil {
2224
return nil, nil, xerrors.Errorf("create iofs: %w", err)
2325
}
2426

25-
dbDriver, err := postgres.WithInstance(db, &postgres.Config{})
27+
// there is a postgres.WithInstance() method that takes the DB instance,
28+
// but, when you close the resulting Migrate, it closes the DB, which
29+
// we don't want. Instead, create just a connection that will get closed
30+
// when migration is done.
31+
conn, err := db.Conn(ctx)
32+
if err != nil {
33+
return nil, nil, xerrors.Errorf("postgres connection: %w", err)
34+
}
35+
dbDriver, err := postgres.WithConnection(ctx, conn, &postgres.Config{})
2636
if err != nil {
2737
return nil, nil, xerrors.Errorf("wrap postgres connection: %w", err)
2838
}
@@ -36,11 +46,22 @@ func migrateSetup(db *sql.DB) (source.Driver, *migrate.Migrate, error) {
3646
}
3747

3848
// MigrateUp runs SQL migrations to ensure the database schema is up-to-date.
39-
func MigrateUp(db *sql.DB) error {
49+
func MigrateUp(db *sql.DB) (retErr error) {
4050
_, m, err := migrateSetup(db)
4151
if err != nil {
4252
return xerrors.Errorf("migrate setup: %w", err)
4353
}
54+
defer func() {
55+
srcErr, dbErr := m.Close()
56+
if retErr != nil {
57+
return
58+
}
59+
if dbErr != nil {
60+
retErr = dbErr
61+
return
62+
}
63+
retErr = srcErr
64+
}()
4465

4566
err = m.Up()
4667
if err != nil {
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
DROP TABLE site_config;
1+
DROP TABLE site_configs;

coderd/database/postgres/postgres.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,13 @@ func Open() (string, func(), error) {
4444
return "", nil, xerrors.Errorf("create db: %w", err)
4545
}
4646

47-
return "postgres://postgres:postgres@127.0.0.1:5432/" + dbName + "?sslmode=disable", func() {}, nil
47+
deleteDB := func() {
48+
ddb, _ := sql.Open("postgres", dbURL)
49+
defer ddb.Close()
50+
_, _ = ddb.Exec("DROP DATABASE " + dbName)
51+
}
52+
53+
return "postgres://postgres:postgres@127.0.0.1:5432/" + dbName + "?sslmode=disable", deleteDB, nil
4854
}
4955

5056
pool, err := dockertest.NewPool("")

coderd/telemetry/telemetry.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,8 @@ func (r *remoteReporter) createSnapshot() (*Snapshot, error) {
356356
user := ConvertUser(dbUser)
357357
// If it's the first user, we'll send the email!
358358
if firstUser.ID == dbUser.ID {
359-
user.Email = dbUser.Email
359+
email := dbUser.Email
360+
user.Email = &email
360361
}
361362
snapshot.Users = append(snapshot.Users, user)
362363
}
@@ -620,7 +621,7 @@ type User struct {
620621
ID uuid.UUID `json:"uuid"`
621622
CreatedAt time.Time `json:"created_at"`
622623
// Email is only filled in for the first/admin user!
623-
Email string `json:"email"`
624+
Email *string `json:"email"`
624625
EmailHashed string `json:"email_hashed"`
625626
RBACRoles []string `json:"rbac_roles"`
626627
Status database.UserStatus `json:"status"`
@@ -714,5 +715,5 @@ type ParameterSchema struct {
714715

715716
type noopReporter struct{}
716717

717-
func (n *noopReporter) Report(_ *Snapshot) {}
718-
func (n *noopReporter) Close() {}
718+
func (*noopReporter) Report(_ *Snapshot) {}
719+
func (*noopReporter) Close() {}

coderd/users.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
8989

9090
telemetryUser := telemetry.ConvertUser(user)
9191
// Send the initial users email address!
92-
telemetryUser.Email = user.Email
92+
telemetryUser.Email = &user.Email
9393
api.Telemetry.Report(&telemetry.Snapshot{
9494
Users: []telemetry.User{telemetryUser},
9595
})

docs/ides.md

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# IDEs
2+
3+
The following desktop IDEs have been tested with Coder, though any IDE with SSH
4+
support should work:
5+
6+
- VS Code (with [Remote -
7+
SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh)
8+
extension)
9+
- JetBrains (with
10+
[Gateway](https://www.jetbrains.com/help/idea/remote-development-a.html#launch_gateway)
11+
installed)
12+
- IntelliJ IDEA
13+
- CLion
14+
- GoLand
15+
- PyCharm
16+
- Rider
17+
- RubyMine
18+
- WebStorm
19+
20+
## SSH configuration
21+
22+
> Before proceeding, run `coder login <accessURL>` if you haven't already to
23+
> authenticate the CLI with the web UI and your workspaces.
24+
25+
To access Coder via SSH, run the following in the terminal:
26+
27+
```console
28+
coder config-ssh
29+
```
30+
31+
> Run `coder config-ssh --diff` if you'd like to see the changes that will be
32+
> made before proceeding.
33+
34+
Confirm that you want to continue by typing **yes** and pressing enter. If
35+
successful, you'll see the following message:
36+
37+
```console
38+
You should now be able to ssh into your workspace.
39+
For example, try running:
40+
41+
$ ssh coder.<workspaceName>
42+
```
43+
44+
Your workspace is now accessible via `ssh coder.<workspace_name>` (e.g.,
45+
`ssh coder.myEnv` if your workspace is named `myEnv`).
46+
47+
## VS Code Remote
48+
49+
Once you've configured SSH, you can work on projects from your local copy of VS
50+
Code, connected to your Coder workspace for compute, etc.
51+
52+
1. Open VS Code locally.
53+
54+
1. Install the [Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh)
55+
extension.
56+
57+
1. In VS Code's left-hand nav bar, click **Remote Explorer** and right-click on
58+
a workspace to connect.
59+
60+
## VS Code in the browser
61+
62+
> You must have Docker Desktop running for this template to work.
63+
64+
Coder offers a [sample template that includes
65+
code-server](../examples/templates/docker-code-server/README.md).
66+
67+
To use:
68+
69+
1. Start Coder:
70+
71+
```console
72+
coder server --dev
73+
```
74+
75+
1. Open a new terminal and run:
76+
77+
```console
78+
coder templates init
79+
```
80+
81+
1. Select the **Develop code-server in Docker** option when prompted.
82+
83+
1. Navigate into your new folder and create your sample template:
84+
85+
```console
86+
cd code-server-docker && coder templates create
87+
```
88+
89+
Follow the prompts that appear in the terminal.
90+
91+
1. Create your workspace:
92+
93+
```console
94+
coder create --template="docker-code-server" [workspace name]
95+
```
96+
97+
1. Log into Coder's Web UI, and open your workspace. Then,
98+
click **code-server** to launch VS Code in a new browser window.
99+
100+
## JetBrains Gateway with SSH
101+
102+
If your image
103+
[includes a JetBrains IDE](../admin/workspace-management/installing-jetbrains.md)
104+
and you've [set up SSH access to Coder](./ssh.md), you can use JetBrains Gateway
105+
to run a local JetBrains IDE connected to your Coder workspace.
106+
107+
> See the [Docker sample template](../examples/templates/docker/main.tf) for an
108+
> example of how to refer to images in your template.
109+
110+
Please note that:
111+
112+
- Your Coder workspace must be running, and Gateway needs compute resources, so
113+
monitor your resource usage on the Coder dashboard and adjust accordingly.
114+
- If you use a premium JetBrains IDE (e.g., GoLand, IntelliJ IDEA Ultimate), you
115+
will still need a license to use it remotely with Coder.
116+
117+
1. [Download and install JetBrains Toolbox](https://www.jetbrains.com/toolbox-app/).
118+
Locate JetBrains Gateway in the Toolbox list and click **Install**.
119+
120+
1. Open JetBrains Gateway and click **Connect via SSH** within the **Run the IDE
121+
Remotely** section.
122+
123+
1. Click the small **gear icon** to the right of the **Connection** field, then
124+
the **+** button on the next screen to create a new configuration.
125+
126+
1. Enter your Coder workspace alias target in **Host** (e.g.,
127+
`coder.<yourWorkspaceName>`), `22` in **Port**, `coder` in **User name**, and change
128+
**Authentication Type** to **OpenSSH config and authentication agent**. Leave
129+
the local port field blank. Click **Test Connection**. If the test is
130+
successful, click **Ok** at the bottom to proceed.
131+
132+
1. With your created configuration in the **Connection** chosen in the drop-down
133+
field, click **Check Connection and Continue**.
134+
135+
1. Select a JetBrains IDE from the **IDE version** drop-down (make sure that you
136+
choose the IDE included in your image), then click the folder icon and select the
137+
`/home/coder` directory in your Coder workspace. Click **Download and Start
138+
IDE** to proceed.
139+
140+
1. During this installation step, Gateway downloads the IDE and a JetBrains
141+
client. This may take a couple of minutes.
142+
143+
1. When your IDE download is complete, JetBrains will prompt you for your
144+
license. When done, you'll be able to use your IDE.

0 commit comments

Comments
 (0)