Skip to content

Commit cda50fc

Browse files
committed
Merge branch 'main' of github.com:/coder/coder into dk/template-pull-path
2 parents 85e03e9 + 0d16df9 commit cda50fc

File tree

157 files changed

+2859
-937
lines changed

Some content is hidden

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

157 files changed

+2859
-937
lines changed

cli/login.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,28 @@ func (r *RootCmd) login() *clibase.Cmd {
136136
useTokenForSession bool
137137
)
138138
cmd := &clibase.Cmd{
139-
Use: "login <url>",
139+
Use: "login [<url>]",
140140
Short: "Authenticate with Coder deployment",
141141
Middleware: clibase.RequireRangeArgs(0, 1),
142142
Handler: func(inv *clibase.Invocation) error {
143143
ctx := inv.Context()
144144
rawURL := ""
145+
var urlSource string
146+
145147
if len(inv.Args) == 0 {
146148
rawURL = r.clientURL.String()
149+
urlSource = "flag"
150+
if rawURL != "" && rawURL == inv.Environ.Get(envURL) {
151+
urlSource = "environment"
152+
}
147153
} else {
148154
rawURL = inv.Args[0]
155+
urlSource = "argument"
156+
}
157+
158+
if url, err := r.createConfig().URL().Read(); rawURL == "" && err == nil {
159+
urlSource = "config"
160+
rawURL = url
149161
}
150162

151163
if rawURL == "" {
@@ -187,6 +199,9 @@ func (r *RootCmd) login() *clibase.Cmd {
187199
if err != nil {
188200
return xerrors.Errorf("Failed to check server %q for first user, is the URL correct and is coder accessible from your browser? Error - has initial user: %w", serverURL.String(), err)
189201
}
202+
203+
_, _ = fmt.Fprintf(inv.Stdout, "Attempting to authenticate with %s URL: '%s'\n", urlSource, serverURL)
204+
190205
if !hasFirstUser {
191206
_, _ = fmt.Fprintf(inv.Stdout, Caret+"Your Coder deployment hasn't been set up!\n")
192207

cli/login_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ func TestLogin(t *testing.T) {
116116

117117
clitest.Start(t, inv)
118118

119+
pty.ExpectMatch(fmt.Sprintf("Attempting to authenticate with flag URL: '%s'", client.URL.String()))
119120
matches := []string{
120121
"first user?", "yes",
121122
"username", "testuser",
@@ -205,6 +206,7 @@ func TestLogin(t *testing.T) {
205206
assert.NoError(t, err)
206207
}()
207208

209+
pty.ExpectMatch(fmt.Sprintf("Attempting to authenticate with argument URL: '%s'", client.URL.String()))
208210
pty.ExpectMatch("Paste your token here:")
209211
pty.WriteLine(client.SessionToken())
210212
if runtime.GOOS != "windows" {
@@ -215,6 +217,52 @@ func TestLogin(t *testing.T) {
215217
<-doneChan
216218
})
217219

220+
t.Run("ExistingUserURLSavedInConfig", func(t *testing.T) {
221+
t.Parallel()
222+
client := coderdtest.New(t, nil)
223+
url := client.URL.String()
224+
coderdtest.CreateFirstUser(t, client)
225+
226+
inv, root := clitest.New(t, "login", "--no-open")
227+
clitest.SetupConfig(t, client, root)
228+
229+
doneChan := make(chan struct{})
230+
pty := ptytest.New(t).Attach(inv)
231+
go func() {
232+
defer close(doneChan)
233+
err := inv.Run()
234+
assert.NoError(t, err)
235+
}()
236+
237+
pty.ExpectMatch(fmt.Sprintf("Attempting to authenticate with config URL: '%s'", url))
238+
pty.ExpectMatch("Paste your token here:")
239+
pty.WriteLine(client.SessionToken())
240+
<-doneChan
241+
})
242+
243+
t.Run("ExistingUserURLSavedInEnv", func(t *testing.T) {
244+
t.Parallel()
245+
client := coderdtest.New(t, nil)
246+
url := client.URL.String()
247+
coderdtest.CreateFirstUser(t, client)
248+
249+
inv, _ := clitest.New(t, "login", "--no-open")
250+
inv.Environ.Set("CODER_URL", url)
251+
252+
doneChan := make(chan struct{})
253+
pty := ptytest.New(t).Attach(inv)
254+
go func() {
255+
defer close(doneChan)
256+
err := inv.Run()
257+
assert.NoError(t, err)
258+
}()
259+
260+
pty.ExpectMatch(fmt.Sprintf("Attempting to authenticate with environment URL: '%s'", url))
261+
pty.ExpectMatch("Paste your token here:")
262+
pty.WriteLine(client.SessionToken())
263+
<-doneChan
264+
})
265+
218266
t.Run("ExistingUserInvalidTokenTTY", func(t *testing.T) {
219267
t.Parallel()
220268
client := coderdtest.New(t, nil)

cli/logout_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func TestLogout(t *testing.T) {
119119
go func() {
120120
defer close(logoutChan)
121121
err = logout.Run()
122-
assert.ErrorContains(t, err, "You are not logged in. Try logging in using 'coder login <url>'.")
122+
assert.ErrorContains(t, err, "You are not logged in. Try logging in using 'coder login'.")
123123
}()
124124

125125
<-logoutChan

cli/root.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ const (
6565
varVerbose = "verbose"
6666
varOrganizationSelect = "organization"
6767
varDisableDirect = "disable-direct-connections"
68-
notLoggedInMessage = "You are not logged in. Try logging in using 'coder login <url>'."
68+
69+
notLoggedInMessage = "You are not logged in. Try logging in using 'coder login <url>'."
70+
notLoggedInURLSavedMessage = "You are not logged in. Try logging in using 'coder login'."
6971

7072
envNoVersionCheck = "CODER_NO_VERSION_WARNING"
7173
envNoFeatureWarning = "CODER_NO_FEATURE_WARNING"
@@ -77,7 +79,10 @@ const (
7779
envURL = "CODER_URL"
7880
)
7981

80-
var errUnauthenticated = xerrors.New(notLoggedInMessage)
82+
var (
83+
errUnauthenticated = xerrors.New(notLoggedInMessage)
84+
errUnauthenticatedURLSaved = xerrors.New(notLoggedInURLSavedMessage)
85+
)
8186

8287
func (r *RootCmd) Core() []*clibase.Cmd {
8388
// Please re-sort this list alphabetically if you change it!
@@ -574,7 +579,7 @@ func (r *RootCmd) initClientInternal(client *codersdk.Client, allowTokenMissing
574579
// If the configuration files are absent, the user is logged out
575580
if os.IsNotExist(err) {
576581
if !allowTokenMissing {
577-
return errUnauthenticated
582+
return errUnauthenticatedURLSaved
578583
}
579584
} else if err != nil {
580585
return err

cli/server.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,13 @@ func enablePrometheus(
229229
afterCtx(ctx, closeInsightsMetricsCollector)
230230

231231
if vals.Prometheus.CollectAgentStats {
232-
closeAgentStatsFunc, err := prometheusmetrics.AgentStats(ctx, logger, options.PrometheusRegistry, options.Database, time.Now(), 0)
232+
closeAgentStatsFunc, err := prometheusmetrics.AgentStats(ctx, logger, options.PrometheusRegistry, options.Database, time.Now(), 0, options.DeploymentValues.Prometheus.AggregateAgentStatsBy.Value())
233233
if err != nil {
234234
return nil, xerrors.Errorf("register agent stats prometheus metric: %w", err)
235235
}
236236
afterCtx(ctx, closeAgentStatsFunc)
237237

238-
metricsAggregator, err := prometheusmetrics.NewMetricsAggregator(logger, options.PrometheusRegistry, 0)
238+
metricsAggregator, err := prometheusmetrics.NewMetricsAggregator(logger, options.PrometheusRegistry, 0, options.DeploymentValues.Prometheus.AggregateAgentStatsBy.Value())
239239
if err != nil {
240240
return nil, xerrors.Errorf("can't initialize metrics aggregator: %w", err)
241241
}

cli/testdata/coder_login_--help.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
coder v0.0.0-devel
22

33
USAGE:
4-
coder login [flags] <url>
4+
coder login [flags] [<url>]
55

66
Authenticate with Coder deployment
77

cli/testdata/coder_server_--help.golden

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ INTROSPECTION / PROMETHEUS OPTIONS:
123123
--prometheus-address host:port, $CODER_PROMETHEUS_ADDRESS (default: 127.0.0.1:2112)
124124
The bind address to serve prometheus metrics.
125125

126+
--prometheus-aggregate-agent-stats-by string-array, $CODER_PROMETHEUS_AGGREGATE_AGENT_STATS_BY (default: agent_name,template_name,username,workspace_name)
127+
When collecting agent stats, aggregate metrics by a given set of
128+
comma-separated labels to reduce cardinality. Accepted values are
129+
agent_name, template_name, username, workspace_name.
130+
126131
--prometheus-collect-agent-stats bool, $CODER_PROMETHEUS_COLLECT_AGENT_STATS
127132
Collect agent stats (may increase charges for metrics storage).
128133

cli/testdata/server-config.yaml.golden

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,15 @@ introspection:
188188
# Collect agent stats (may increase charges for metrics storage).
189189
# (default: <unset>, type: bool)
190190
collect_agent_stats: false
191+
# When collecting agent stats, aggregate metrics by a given set of comma-separated
192+
# labels to reduce cardinality. Accepted values are agent_name, template_name,
193+
# username, workspace_name.
194+
# (default: agent_name,template_name,username,workspace_name, type: string-array)
195+
aggregate_agent_stats_by:
196+
- agent_name
197+
- template_name
198+
- username
199+
- workspace_name
191200
# Collect database metrics (may increase charges for metrics storage).
192201
# (default: false, type: bool)
193202
collect_db_metrics: false

coderd/agentmetrics/labels.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package agentmetrics
2+
3+
import (
4+
"strings"
5+
6+
"golang.org/x/xerrors"
7+
)
8+
9+
const (
10+
LabelAgentName = "agent_name"
11+
LabelTemplateName = "template_name"
12+
LabelUsername = "username"
13+
LabelWorkspaceName = "workspace_name"
14+
)
15+
16+
var (
17+
LabelAll = []string{LabelAgentName, LabelTemplateName, LabelUsername, LabelWorkspaceName}
18+
LabelAgentStats = []string{LabelAgentName, LabelUsername, LabelWorkspaceName}
19+
)
20+
21+
// ValidateAggregationLabels ensures a given set of labels are valid aggregation labels.
22+
func ValidateAggregationLabels(labels []string) error {
23+
acceptable := LabelAll
24+
25+
seen := make(map[string]any, len(acceptable))
26+
for _, label := range acceptable {
27+
seen[label] = nil
28+
}
29+
30+
for _, label := range labels {
31+
if _, found := seen[label]; !found {
32+
return xerrors.Errorf("%q is not a valid aggregation label; only one or more of %q are acceptable",
33+
label, strings.Join(acceptable, ", "))
34+
}
35+
}
36+
37+
return nil
38+
}

coderd/agentmetrics/labels_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package agentmetrics_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
8+
"github.com/coder/coder/v2/coderd/agentmetrics"
9+
)
10+
11+
func TestValidateAggregationLabels(t *testing.T) {
12+
t.Parallel()
13+
14+
tests := []struct {
15+
name string
16+
labels []string
17+
expectedErr bool
18+
}{
19+
{
20+
name: "empty list is valid",
21+
},
22+
{
23+
name: "single valid entry",
24+
labels: []string{agentmetrics.LabelTemplateName},
25+
},
26+
{
27+
name: "multiple valid entries",
28+
labels: []string{agentmetrics.LabelTemplateName, agentmetrics.LabelUsername},
29+
},
30+
{
31+
name: "repeated valid entries are not invalid",
32+
labels: []string{agentmetrics.LabelTemplateName, agentmetrics.LabelUsername, agentmetrics.LabelUsername, agentmetrics.LabelUsername},
33+
},
34+
{
35+
name: "empty entry is invalid",
36+
labels: []string{""},
37+
expectedErr: true,
38+
},
39+
{
40+
name: "all valid entries",
41+
labels: agentmetrics.LabelAll,
42+
},
43+
}
44+
45+
for _, tc := range tests {
46+
tc := tc
47+
48+
t.Run(tc.name, func(t *testing.T) {
49+
t.Parallel()
50+
51+
err := agentmetrics.ValidateAggregationLabels(tc.labels)
52+
if tc.expectedErr {
53+
require.Error(t, err)
54+
}
55+
})
56+
}
57+
}

coderd/apidoc/docs.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbfake/dbfake.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ func (b WorkspaceBuildBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) []
9595
Auth: &sdkproto.Agent_Token{
9696
Token: b.agentToken,
9797
},
98+
Env: map[string]string{
99+
"SECRET_TOKEN": "supersecret",
100+
},
98101
}}
99102
for _, m := range mutations {
100103
agents = m(agents)

coderd/database/dbmem/dbmem.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func New() database.Store {
7878
},
7979
}
8080
// Always start with a default org. Matching migration 198.
81-
_, err := q.InsertOrganization(context.Background(), database.InsertOrganizationParams{
81+
defaultOrg, err := q.InsertOrganization(context.Background(), database.InsertOrganizationParams{
8282
ID: uuid.New(),
8383
Name: "first-organization",
8484
Description: "Builtin default organization.",
@@ -88,6 +88,12 @@ func New() database.Store {
8888
if err != nil {
8989
panic(xerrors.Errorf("failed to create default organization: %w", err))
9090
}
91+
92+
_, err = q.InsertAllUsersGroup(context.Background(), defaultOrg.ID)
93+
if err != nil {
94+
panic(fmt.Errorf("failed to create default group: %w", err))
95+
}
96+
9197
q.defaultProxyDisplayName = "Default"
9298
q.defaultProxyIconURL = "/emojis/1f3e1.png"
9399
return q
@@ -8266,6 +8272,19 @@ func (q *FakeQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg database.
82668272
}
82678273
}
82688274

8275+
if len(arg.WorkspaceIds) > 0 {
8276+
match := false
8277+
for _, id := range arg.WorkspaceIds {
8278+
if workspace.ID == id {
8279+
match = true
8280+
break
8281+
}
8282+
}
8283+
if !match {
8284+
continue
8285+
}
8286+
}
8287+
82698288
// If the filter exists, ensure the object is authorized.
82708289
if prepared != nil && prepared.Authorize(ctx, workspace.RBACObject()) != nil {
82718290
continue

0 commit comments

Comments
 (0)