Skip to content

Commit 332d435

Browse files
committed
Merge branch 'main' into replica
2 parents ae0aa5f + e456799 commit 332d435

Some content is hidden

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

47 files changed

+1130
-228
lines changed

cli/deployment/flags.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func Flags() *codersdk.DeploymentFlags {
3232
Name: "Wildcard Address URL",
3333
Flag: "wildcard-access-url",
3434
EnvVar: "CODER_WILDCARD_ACCESS_URL",
35-
Description: `Specifies the wildcard hostname to use for workspace applications in the form "*.example.com".`,
35+
Description: `Specifies the wildcard hostname to use for workspace applications in the form "*.example.com" or "*-suffix.example.com". Ports or schemes should not be included. The scheme will be copied from the access URL.`,
3636
},
3737
Address: &codersdk.StringFlag{
3838
Name: "Bind Address",

cli/server.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"os/signal"
1818
"os/user"
1919
"path/filepath"
20+
"regexp"
2021
"strconv"
2122
"strings"
2223
"sync"
@@ -53,6 +54,7 @@ import (
5354
"github.com/coder/coder/coderd/database/migrations"
5455
"github.com/coder/coder/coderd/devtunnel"
5556
"github.com/coder/coder/coderd/gitsshkey"
57+
"github.com/coder/coder/coderd/httpapi"
5658
"github.com/coder/coder/coderd/prometheusmetrics"
5759
"github.com/coder/coder/coderd/telemetry"
5860
"github.com/coder/coder/coderd/tracing"
@@ -299,13 +301,19 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code
299301
return xerrors.Errorf("create derp map: %w", err)
300302
}
301303

302-
appHostname := strings.TrimPrefix(dflags.WildcardAccessURL.Value, "http://")
303-
appHostname = strings.TrimPrefix(appHostname, "https://")
304-
appHostname = strings.TrimPrefix(appHostname, "*.")
304+
appHostname := strings.TrimSpace(dflags.WildcardAccessURL.Value)
305+
var appHostnameRegex *regexp.Regexp
306+
if appHostname != "" {
307+
appHostnameRegex, err = httpapi.CompileHostnamePattern(appHostname)
308+
if err != nil {
309+
return xerrors.Errorf("parse wildcard access URL %q: %w", appHostname, err)
310+
}
311+
}
305312

306313
options := &coderd.Options{
307314
AccessURL: accessURLParsed,
308315
AppHostname: appHostname,
316+
AppHostnameRegex: appHostnameRegex,
309317
Logger: logger.Named("coderd"),
310318
Database: databasefake.New(),
311319
DERPMap: derpMap,

coderd/activitybump_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@ func TestWorkspaceActivityBump(t *testing.T) {
2323
setupActivityTest := func(t *testing.T) (client *codersdk.Client, workspace codersdk.Workspace, assertBumped func(want bool)) {
2424
var ttlMillis int64 = 60 * 1000
2525

26-
client, _, workspace, _ = setupProxyTest(t, func(cwr *codersdk.CreateWorkspaceRequest) {
26+
client = coderdtest.New(t, &coderdtest.Options{
27+
AppHostname: proxyTestSubdomainRaw,
28+
IncludeProvisionerDaemon: true,
29+
AgentStatsRefreshInterval: time.Millisecond * 100,
30+
MetricsCacheRefreshInterval: time.Millisecond * 100,
31+
})
32+
user := coderdtest.CreateFirstUser(t, client)
33+
34+
workspace = createWorkspaceWithApps(t, client, user.OrganizationID, 1234, func(cwr *codersdk.CreateWorkspaceRequest) {
2735
cwr.TTLMillis = &ttlMillis
2836
})
2937

coderd/audit.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,18 @@ func convertAuditLog(dblog database.GetAuditLogsOffsetRow) codersdk.AuditLog {
221221
}
222222

223223
func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
224-
return fmt.Sprintf("{user} %s %s {target}",
224+
str := fmt.Sprintf("{user} %s %s",
225225
codersdk.AuditAction(alog.Action).FriendlyString(),
226226
codersdk.ResourceType(alog.ResourceType).FriendlyString(),
227227
)
228+
229+
// We don't display the name for git ssh keys. It's fairly long and doesn't
230+
// make too much sense to display.
231+
if alog.ResourceType != database.ResourceTypeGitSshKey {
232+
str += " {target}"
233+
}
234+
235+
return str
228236
}
229237

230238
// auditSearchQuery takes a query string and returns the auditLog filter.

coderd/audit/audit.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ func (nop) Export(context.Context, database.AuditLog) error {
2121
return nil
2222
}
2323

24-
func (nop) diff(any, any) Map { return Map{} }
24+
func (nop) diff(any, any) Map {
25+
return Map{}
26+
}
2527

2628
func NewMock() *MockAuditor {
2729
return &MockAuditor{}
@@ -36,4 +38,6 @@ func (a *MockAuditor) Export(_ context.Context, alog database.AuditLog) error {
3638
return nil
3739
}
3840

39-
func (*MockAuditor) diff(any, any) Map { return Map{} }
41+
func (*MockAuditor) diff(any, any) Map {
42+
return Map{}
43+
}

coderd/coderd.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88
"net/url"
99
"path/filepath"
10+
"regexp"
1011
"sync"
1112
"sync/atomic"
1213
"time"
@@ -47,11 +48,16 @@ import (
4748
type Options struct {
4849
AccessURL *url.URL
4950
// AppHostname should be the wildcard hostname to use for workspace
50-
// applications without the asterisk or leading dot. E.g. "apps.coder.com".
51+
// applications INCLUDING the asterisk, (optional) suffix and leading dot.
52+
// E.g. "*.apps.coder.com" or "*-apps.coder.com".
5153
AppHostname string
52-
Logger slog.Logger
53-
Database database.Store
54-
Pubsub database.Pubsub
54+
// AppHostnameRegex contains the regex version of options.AppHostname as
55+
// generated by httpapi.CompileHostnamePattern(). It MUST be set if
56+
// options.AppHostname is set.
57+
AppHostnameRegex *regexp.Regexp
58+
Logger slog.Logger
59+
Database database.Store
60+
Pubsub database.Pubsub
5561

5662
// CacheDir is used for caching files served by the API.
5763
CacheDir string
@@ -94,6 +100,9 @@ func New(options *Options) *API {
94100
if options == nil {
95101
options = &Options{}
96102
}
103+
if options.AppHostname != "" && options.AppHostnameRegex == nil || options.AppHostname == "" && options.AppHostnameRegex != nil {
104+
panic("coderd: both AppHostname and AppHostnameRegex must be set or unset")
105+
}
97106
if options.AgentConnectionUpdateFrequency == 0 {
98107
options.AgentConnectionUpdateFrequency = 3 * time.Second
99108
}

coderd/coderdtest/authorize_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
1111
t.Parallel()
1212
client, _, api := coderdtest.NewWithAPI(t, &coderdtest.Options{
1313
// Required for any subdomain-based proxy tests to pass.
14-
AppHostname: "test.coder.com",
14+
AppHostname: "*.test.coder.com",
1515
Authorizer: &coderdtest.RecordingAuthorizer{},
1616
IncludeProvisionerDaemon: true,
1717
})

coderd/coderdtest/coderdtest.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"net/http"
2222
"net/http/httptest"
2323
"net/url"
24+
"regexp"
2425
"strconv"
2526
"strings"
2627
"testing"
@@ -52,6 +53,7 @@ import (
5253
"github.com/coder/coder/coderd/database"
5354
"github.com/coder/coder/coderd/database/dbtestutil"
5455
"github.com/coder/coder/coderd/gitsshkey"
56+
"github.com/coder/coder/coderd/httpapi"
5557
"github.com/coder/coder/coderd/rbac"
5658
"github.com/coder/coder/coderd/telemetry"
5759
"github.com/coder/coder/coderd/util/ptr"
@@ -195,13 +197,21 @@ func NewOptions(t *testing.T, options *Options) (*httptest.Server, context.Cance
195197
options.SSHKeygenAlgorithm = gitsshkey.AlgorithmEd25519
196198
}
197199

200+
var appHostnameRegex *regexp.Regexp
201+
if options.AppHostname != "" {
202+
var err error
203+
appHostnameRegex, err = httpapi.CompileHostnamePattern(options.AppHostname)
204+
require.NoError(t, err)
205+
}
206+
198207
return srv, cancelFunc, &coderd.Options{
199208
AgentConnectionUpdateFrequency: 150 * time.Millisecond,
200209
// Force a long disconnection timeout to ensure
201210
// agents are not marked as disconnected during slow tests.
202211
AgentInactiveDisconnectTimeout: testutil.WaitShort,
203212
AccessURL: serverURL,
204213
AppHostname: options.AppHostname,
214+
AppHostnameRegex: appHostnameRegex,
205215
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
206216
CacheDir: t.TempDir(),
207217
Database: options.Database,

coderd/database/databasefake/databasefake.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,35 @@ func (q *fakeQuerier) GetTemplateDAUs(_ context.Context, templateID uuid.UUID) (
241241
return rs, nil
242242
}
243243

244+
func (q *fakeQuerier) GetTemplateAverageBuildTime(ctx context.Context, arg database.GetTemplateAverageBuildTimeParams) (float64, error) {
245+
var times []float64
246+
for _, wb := range q.workspaceBuilds {
247+
if wb.Transition != database.WorkspaceTransitionStart {
248+
continue
249+
}
250+
version, err := q.GetTemplateVersionByID(ctx, wb.TemplateVersionID)
251+
if err != nil {
252+
return -1, err
253+
}
254+
if version.TemplateID != arg.TemplateID {
255+
continue
256+
}
257+
258+
job, err := q.GetProvisionerJobByID(ctx, wb.JobID)
259+
if err != nil {
260+
return -1, err
261+
}
262+
if job.CompletedAt.Valid {
263+
times = append(times, job.CompletedAt.Time.Sub(job.StartedAt.Time).Seconds())
264+
}
265+
}
266+
sort.Float64s(times)
267+
if len(times) == 0 {
268+
return -1, nil
269+
}
270+
return times[len(times)/2], nil
271+
}
272+
244273
func (q *fakeQuerier) ParameterValue(_ context.Context, id uuid.UUID) (database.ParameterValue, error) {
245274
q.mutex.Lock()
246275
defer q.mutex.Unlock()
@@ -2682,7 +2711,7 @@ func (q *fakeQuerier) GetGitSSHKey(_ context.Context, userID uuid.UUID) (databas
26822711
return database.GitSSHKey{}, sql.ErrNoRows
26832712
}
26842713

2685-
func (q *fakeQuerier) UpdateGitSSHKey(_ context.Context, arg database.UpdateGitSSHKeyParams) error {
2714+
func (q *fakeQuerier) UpdateGitSSHKey(_ context.Context, arg database.UpdateGitSSHKeyParams) (database.GitSSHKey, error) {
26862715
q.mutex.Lock()
26872716
defer q.mutex.Unlock()
26882717

@@ -2694,9 +2723,9 @@ func (q *fakeQuerier) UpdateGitSSHKey(_ context.Context, arg database.UpdateGitS
26942723
key.PrivateKey = arg.PrivateKey
26952724
key.PublicKey = arg.PublicKey
26962725
q.gitSSHKey[index] = key
2697-
return nil
2726+
return key, nil
26982727
}
2699-
return sql.ErrNoRows
2728+
return database.GitSSHKey{}, sql.ErrNoRows
27002729
}
27012730

27022731
func (q *fakeQuerier) InsertGroupMember(_ context.Context, arg database.InsertGroupMemberParams) error {

coderd/database/querier.go

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

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

coderd/database/queries/gitsshkeys.sql

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,17 @@ FROM
1818
WHERE
1919
user_id = $1;
2020

21-
-- name: UpdateGitSSHKey :exec
21+
-- name: UpdateGitSSHKey :one
2222
UPDATE
2323
gitsshkeys
2424
SET
2525
updated_at = $2,
2626
private_key = $3,
2727
public_key = $4
2828
WHERE
29-
user_id = $1;
29+
user_id = $1
30+
RETURNING
31+
*;
3032

3133
-- name: DeleteGitSSHKey :exec
3234
DELETE FROM

coderd/database/queries/templates.sql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,27 @@ WHERE
105105
id = $1
106106
RETURNING
107107
*;
108+
109+
-- name: GetTemplateAverageBuildTime :one
110+
WITH build_times AS (
111+
SELECT
112+
EXTRACT(EPOCH FROM (pj.completed_at - pj.started_at))::FLOAT AS exec_time_sec
113+
FROM
114+
workspace_builds
115+
JOIN template_versions ON
116+
workspace_builds.template_version_id = template_versions.id
117+
JOIN provisioner_jobs pj ON
118+
workspace_builds.job_id = pj.id
119+
WHERE
120+
template_versions.template_id = @template_id AND
121+
(workspace_builds.transition = 'start') AND
122+
(pj.completed_at IS NOT NULL) AND (pj.started_at IS NOT NULL) AND
123+
(pj.started_at > @start_time) AND
124+
(pj.canceled_at IS NULL) AND
125+
((pj.error IS NULL) OR (pj.error = ''))
126+
ORDER BY
127+
workspace_builds.created_at DESC
128+
)
129+
SELECT coalesce((PERCENTILE_DISC(0.5) WITHIN GROUP(ORDER BY exec_time_sec)), -1)::FLOAT
130+
FROM build_times
131+
;

0 commit comments

Comments
 (0)