Skip to content

Commit ce7ec39

Browse files
committed
Merge branch 'main' into dean/schedule-max-ttl
2 parents 69035a6 + 5f6dd0c commit ce7ec39

File tree

124 files changed

+4406
-1438
lines changed

Some content is hidden

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

124 files changed

+4406
-1438
lines changed

.github/workflows/pr-auto-assign.yaml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Filtering pull requests is much easier when we can reliably guarantee
2+
# that the "Assignee" field is populated.
3+
name: PR Auto Assign
4+
5+
on:
6+
pull_request_target:
7+
types: [opened]
8+
9+
permissions:
10+
pull-requests: write
11+
12+
jobs:
13+
assign-author:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: toshimaru/auto-author-assign@v1.6.2

Makefile

+3-3
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ gen: \
424424
provisionerd/proto/provisionerd.pb.go \
425425
site/src/api/typesGenerated.ts \
426426
docs/admin/prometheus.md \
427-
docs/cli/coder.md \
427+
docs/cli.md \
428428
docs/admin/audit-logs.md \
429429
coderd/apidoc/swagger.json \
430430
.prettierignore.include \
@@ -444,7 +444,7 @@ gen/mark-fresh:
444444
provisionerd/proto/provisionerd.pb.go \
445445
site/src/api/typesGenerated.ts \
446446
docs/admin/prometheus.md \
447-
docs/cli/coder.md \
447+
docs/cli.md \
448448
docs/admin/audit-logs.md \
449449
coderd/apidoc/swagger.json \
450450
.prettierignore.include \
@@ -500,7 +500,7 @@ docs/admin/prometheus.md: scripts/metricsdocgen/main.go scripts/metricsdocgen/me
500500
cd site
501501
yarn run format:write:only ../docs/admin/prometheus.md
502502

503-
docs/cli/coder.md: scripts/clidocgen/main.go $(GO_SRC_FILES) docs/manifest.json
503+
docs/cli.md: scripts/clidocgen/main.go $(GO_SRC_FILES) docs/manifest.json
504504
rm -rf ./docs/cli/*.md
505505
BASE_PATH="." go run ./scripts/clidocgen
506506
cd site

agent/agent.go

+18-17
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ func (a *agent) runLoop(ctx context.Context) {
156156
go a.reportLifecycleLoop(ctx)
157157

158158
for retrier := retry.New(100*time.Millisecond, 10*time.Second); retrier.Wait(ctx); {
159-
a.logger.Info(ctx, "running loop")
159+
a.logger.Info(ctx, "connecting to coderd")
160160
err := a.run(ctx)
161161
// Cancel after the run is complete to clean up any leaked resources!
162162
if err == nil {
@@ -169,7 +169,7 @@ func (a *agent) runLoop(ctx context.Context) {
169169
return
170170
}
171171
if errors.Is(err, io.EOF) {
172-
a.logger.Info(ctx, "likely disconnected from coder", slog.Error(err))
172+
a.logger.Info(ctx, "disconnected from coderd")
173173
continue
174174
}
175175
a.logger.Warn(ctx, "run exited with error", slog.Error(err))
@@ -197,7 +197,7 @@ func (a *agent) reportLifecycleLoop(ctx context.Context) {
197197
break
198198
}
199199

200-
a.logger.Debug(ctx, "post lifecycle state", slog.F("state", state))
200+
a.logger.Debug(ctx, "reporting lifecycle state", slog.F("state", state))
201201

202202
err := a.client.PostLifecycle(ctx, agentsdk.PostLifecycleRequest{
203203
State: state,
@@ -242,7 +242,7 @@ func (a *agent) run(ctx context.Context) error {
242242
if err != nil {
243243
return xerrors.Errorf("fetch metadata: %w", err)
244244
}
245-
a.logger.Info(ctx, "fetched metadata")
245+
a.logger.Info(ctx, "fetched metadata", slog.F("metadata", metadata))
246246

247247
// Expand the directory and send it back to coderd so external
248248
// applications that rely on the directory can use it.
@@ -330,13 +330,10 @@ func (a *agent) run(ctx context.Context) error {
330330
go NewWorkspaceAppHealthReporter(
331331
a.logger, metadata.Apps, a.client.PostAppHealth)(appReporterCtx)
332332

333-
a.logger.Debug(ctx, "running tailnet with derpmap", slog.F("derpmap", metadata.DERPMap))
334-
335333
a.closeMutex.Lock()
336334
network := a.network
337335
a.closeMutex.Unlock()
338336
if network == nil {
339-
a.logger.Debug(ctx, "creating tailnet")
340337
network, err = a.createTailnet(ctx, metadata.DERPMap)
341338
if err != nil {
342339
return xerrors.Errorf("create tailnet: %w", err)
@@ -385,10 +382,9 @@ func (a *agent) run(ctx context.Context) error {
385382
network.SetDERPMap(metadata.DERPMap)
386383
}
387384

388-
a.logger.Debug(ctx, "running coordinator")
385+
a.logger.Debug(ctx, "running tailnet connection coordinator")
389386
err = a.runCoordinator(ctx, network)
390387
if err != nil {
391-
a.logger.Debug(ctx, "coordinator exited", slog.Error(err))
392388
return xerrors.Errorf("run coordinator: %w", err)
393389
}
394390
return nil
@@ -474,7 +470,9 @@ func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (_
474470
for {
475471
conn, err := reconnectingPTYListener.Accept()
476472
if err != nil {
477-
logger.Debug(ctx, "accept pty failed", slog.Error(err))
473+
if !a.isClosed() {
474+
logger.Debug(ctx, "accept pty failed", slog.Error(err))
475+
}
478476
break
479477
}
480478
wg.Add(1)
@@ -529,7 +527,9 @@ func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (_
529527
for {
530528
conn, err := speedtestListener.Accept()
531529
if err != nil {
532-
a.logger.Debug(ctx, "speedtest listener failed", slog.Error(err))
530+
if !a.isClosed() {
531+
a.logger.Debug(ctx, "speedtest listener failed", slog.Error(err))
532+
}
533533
break
534534
}
535535
wg.Add(1)
@@ -600,8 +600,10 @@ func (a *agent) runCoordinator(ctx context.Context, network *tailnet.Conn) error
600600
return err
601601
}
602602
defer coordinator.Close()
603-
a.logger.Info(ctx, "connected to coordination server")
604-
sendNodes, errChan := tailnet.ServeCoordinator(coordinator, network.UpdateNodes)
603+
a.logger.Info(ctx, "connected to coordination endpoint")
604+
sendNodes, errChan := tailnet.ServeCoordinator(coordinator, func(nodes []*tailnet.Node) error {
605+
return network.UpdateNodes(nodes, false)
606+
})
605607
network.SetNodeCallback(sendNodes)
606608
select {
607609
case <-ctx.Done():
@@ -644,7 +646,6 @@ func (a *agent) runStartupScript(ctx context.Context, script string) error {
644646
}
645647

646648
func (a *agent) init(ctx context.Context) {
647-
a.logger.Info(ctx, "generating host key")
648649
// Clients' should ignore the host key when connecting.
649650
// The agent needs to authenticate with coderd to SSH,
650651
// so SSH authentication doesn't improve security.
@@ -766,12 +767,12 @@ func (a *agent) init(ctx context.Context) {
766767

767768
func convertAgentStats(counts map[netlogtype.Connection]netlogtype.Counts) *agentsdk.Stats {
768769
stats := &agentsdk.Stats{
769-
ConnsByProto: map[string]int64{},
770-
NumConns: int64(len(counts)),
770+
ConnectionsByProto: map[string]int64{},
771+
ConnectionCount: int64(len(counts)),
771772
}
772773

773774
for conn, count := range counts {
774-
stats.ConnsByProto[conn.Proto.String()]++
775+
stats.ConnectionsByProto[conn.Proto.String()]++
775776
stats.RxPackets += int64(count.RxPackets)
776777
stats.RxBytes += int64(count.RxBytes)
777778
stats.TxPackets += int64(count.TxPackets)

agent/agent_test.go

+14-5
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestAgent_Stats_SSH(t *testing.T) {
7373
require.Eventuallyf(t, func() bool {
7474
var ok bool
7575
s, ok = <-stats
76-
return ok && s.NumConns > 0 && s.RxBytes > 0 && s.TxBytes > 0
76+
return ok && s.ConnectionCount > 0 && s.RxBytes > 0 && s.TxBytes > 0
7777
}, testutil.WaitLong, testutil.IntervalFast,
7878
"never saw stats: %+v", s,
7979
)
@@ -102,7 +102,7 @@ func TestAgent_Stats_ReconnectingPTY(t *testing.T) {
102102
require.Eventuallyf(t, func() bool {
103103
var ok bool
104104
s, ok = <-stats
105-
return ok && s.NumConns > 0 && s.RxBytes > 0 && s.TxBytes > 0
105+
return ok && s.ConnectionCount > 0 && s.RxBytes > 0 && s.TxBytes > 0
106106
}, testutil.WaitLong, testutil.IntervalFast,
107107
"never saw stats: %+v", s,
108108
)
@@ -1179,12 +1179,21 @@ func setupAgent(t *testing.T, metadata agentsdk.Metadata, ptyTimeout time.Durati
11791179
coordinator.ServeClient(serverConn, uuid.New(), agentID)
11801180
}()
11811181
sendNode, _ := tailnet.ServeCoordinator(clientConn, func(node []*tailnet.Node) error {
1182-
return conn.UpdateNodes(node)
1182+
return conn.UpdateNodes(node, false)
11831183
})
11841184
conn.SetNodeCallback(sendNode)
1185-
return &codersdk.WorkspaceAgentConn{
1185+
agentConn := &codersdk.WorkspaceAgentConn{
11861186
Conn: conn,
1187-
}, c, statsCh, fs
1187+
}
1188+
t.Cleanup(func() {
1189+
_ = agentConn.Close()
1190+
})
1191+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
1192+
defer cancel()
1193+
if !agentConn.AwaitReachable(ctx) {
1194+
t.Fatal("agent not reachable")
1195+
}
1196+
return agentConn, c, statsCh, fs
11881197
}
11891198

11901199
var dialTestPayload = []byte("dean-was-here123")

cli/cliui/gitauth.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package cliui
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"time"
8+
9+
"github.com/briandowns/spinner"
10+
11+
"github.com/coder/coder/codersdk"
12+
)
13+
14+
type GitAuthOptions struct {
15+
Fetch func(context.Context) ([]codersdk.TemplateVersionGitAuth, error)
16+
FetchInterval time.Duration
17+
}
18+
19+
func GitAuth(ctx context.Context, writer io.Writer, opts GitAuthOptions) error {
20+
if opts.FetchInterval == 0 {
21+
opts.FetchInterval = 500 * time.Millisecond
22+
}
23+
gitAuth, err := opts.Fetch(ctx)
24+
if err != nil {
25+
return err
26+
}
27+
28+
spin := spinner.New(spinner.CharSets[78], 100*time.Millisecond, spinner.WithColor("fgHiGreen"))
29+
spin.Writer = writer
30+
spin.ForceOutput = true
31+
spin.Suffix = " Waiting for Git authentication..."
32+
defer spin.Stop()
33+
34+
ticker := time.NewTicker(opts.FetchInterval)
35+
defer ticker.Stop()
36+
for _, auth := range gitAuth {
37+
if auth.Authenticated {
38+
return nil
39+
}
40+
41+
_, _ = fmt.Fprintf(writer, "You must authenticate with %s to create a workspace with this template. Visit:\n\n\t%s\n\n", auth.Type.Pretty(), auth.AuthenticateURL)
42+
43+
ticker.Reset(opts.FetchInterval)
44+
spin.Start()
45+
for {
46+
select {
47+
case <-ctx.Done():
48+
return ctx.Err()
49+
case <-ticker.C:
50+
}
51+
gitAuth, err := opts.Fetch(ctx)
52+
if err != nil {
53+
return err
54+
}
55+
var authed bool
56+
for _, a := range gitAuth {
57+
if !a.Authenticated || a.ID != auth.ID {
58+
continue
59+
}
60+
authed = true
61+
break
62+
}
63+
// The user authenticated with the provider!
64+
if authed {
65+
break
66+
}
67+
}
68+
spin.Stop()
69+
_, _ = fmt.Fprintf(writer, "Successfully authenticated with %s!\n\n", auth.Type.Pretty())
70+
}
71+
return nil
72+
}

cli/cliui/gitauth_test.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package cliui_test
2+
3+
import (
4+
"context"
5+
"net/url"
6+
"sync/atomic"
7+
"testing"
8+
"time"
9+
10+
"github.com/spf13/cobra"
11+
"github.com/stretchr/testify/assert"
12+
13+
"github.com/coder/coder/cli/cliui"
14+
"github.com/coder/coder/codersdk"
15+
"github.com/coder/coder/pty/ptytest"
16+
"github.com/coder/coder/testutil"
17+
)
18+
19+
func TestGitAuth(t *testing.T) {
20+
t.Parallel()
21+
22+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
23+
defer cancel()
24+
25+
ptty := ptytest.New(t)
26+
cmd := &cobra.Command{
27+
RunE: func(cmd *cobra.Command, args []string) error {
28+
var fetched atomic.Bool
29+
return cliui.GitAuth(cmd.Context(), cmd.OutOrStdout(), cliui.GitAuthOptions{
30+
Fetch: func(ctx context.Context) ([]codersdk.TemplateVersionGitAuth, error) {
31+
defer fetched.Store(true)
32+
return []codersdk.TemplateVersionGitAuth{{
33+
ID: "github",
34+
Type: codersdk.GitProviderGitHub,
35+
Authenticated: fetched.Load(),
36+
AuthenticateURL: "https://example.com/gitauth/github?redirect=" + url.QueryEscape("/gitauth?notify"),
37+
}}, nil
38+
},
39+
FetchInterval: time.Millisecond,
40+
})
41+
},
42+
}
43+
cmd.SetOutput(ptty.Output())
44+
cmd.SetIn(ptty.Input())
45+
done := make(chan struct{})
46+
go func() {
47+
defer close(done)
48+
err := cmd.Execute()
49+
assert.NoError(t, err)
50+
}()
51+
ptty.ExpectMatchContext(ctx, "You must authenticate with")
52+
ptty.ExpectMatchContext(ctx, "https://example.com/gitauth/github")
53+
ptty.ExpectMatchContext(ctx, "Successfully authenticated with GitHub")
54+
<-done
55+
}

cli/create.go

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cli
22

33
import (
4+
"context"
45
"fmt"
56
"io"
67
"time"
@@ -324,6 +325,15 @@ PromptRichParamLoop:
324325
_, _ = fmt.Fprintln(cmd.OutOrStdout())
325326
}
326327

328+
err = cliui.GitAuth(ctx, cmd.OutOrStdout(), cliui.GitAuthOptions{
329+
Fetch: func(ctx context.Context) ([]codersdk.TemplateVersionGitAuth, error) {
330+
return client.TemplateVersionGitAuth(ctx, templateVersion.ID)
331+
},
332+
})
333+
if err != nil {
334+
return nil, xerrors.Errorf("template version git auth: %w", err)
335+
}
336+
327337
// Run a dry-run with the given parameters to check correctness
328338
dryRun, err := client.CreateTemplateVersionDryRun(cmd.Context(), templateVersion.ID, codersdk.CreateTemplateVersionDryRunRequest{
329339
WorkspaceName: args.NewWorkspaceName,

0 commit comments

Comments
 (0)