Skip to content

Commit 4b8775f

Browse files
committed
Merge remote-tracking branch 'origin/main' into ssncferreira/chore-prebuilt-authz-order
2 parents 2c387ce + 7c40f86 commit 4b8775f

File tree

84 files changed

+2456
-751
lines changed

Some content is hidden

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

84 files changed

+2456
-751
lines changed

.github/.linkspector.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ ignorePatterns:
2424
- pattern: "mutagen.io"
2525
- pattern: "docs.github.com"
2626
- pattern: "claude.ai"
27+
- pattern: "splunk.com"
2728
aliveStatusCodes:
2829
- 200

cli/cliutil/license.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package cliutil
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
8+
"github.com/google/uuid"
9+
"golang.org/x/xerrors"
10+
11+
"github.com/coder/coder/v2/cli/cliui"
12+
"github.com/coder/coder/v2/codersdk"
13+
)
14+
15+
// NewLicenseFormatter returns a new license formatter.
16+
// The formatter will return a table and JSON output.
17+
func NewLicenseFormatter() *cliui.OutputFormatter {
18+
type tableLicense struct {
19+
ID int32 `table:"id,default_sort"`
20+
UUID uuid.UUID `table:"uuid" format:"uuid"`
21+
UploadedAt time.Time `table:"uploaded at" format:"date-time"`
22+
// Features is the formatted string for the license claims.
23+
// Used for the table view.
24+
Features string `table:"features"`
25+
ExpiresAt time.Time `table:"expires at" format:"date-time"`
26+
Trial bool `table:"trial"`
27+
}
28+
29+
return cliui.NewOutputFormatter(
30+
cliui.ChangeFormatterData(
31+
cliui.TableFormat([]tableLicense{}, []string{"ID", "UUID", "Expires At", "Uploaded At", "Features"}),
32+
func(data any) (any, error) {
33+
list, ok := data.([]codersdk.License)
34+
if !ok {
35+
return nil, xerrors.Errorf("invalid data type %T", data)
36+
}
37+
out := make([]tableLicense, 0, len(list))
38+
for _, lic := range list {
39+
var formattedFeatures string
40+
features, err := lic.FeaturesClaims()
41+
if err != nil {
42+
formattedFeatures = xerrors.Errorf("invalid license: %w", err).Error()
43+
} else {
44+
var strs []string
45+
if lic.AllFeaturesClaim() {
46+
// If all features are enabled, just include that
47+
strs = append(strs, "all features")
48+
} else {
49+
for k, v := range features {
50+
if v > 0 {
51+
// Only include claims > 0
52+
strs = append(strs, fmt.Sprintf("%s=%v", k, v))
53+
}
54+
}
55+
}
56+
formattedFeatures = strings.Join(strs, ", ")
57+
}
58+
// If this returns an error, a zero time is returned.
59+
exp, _ := lic.ExpiresAt()
60+
61+
out = append(out, tableLicense{
62+
ID: lic.ID,
63+
UUID: lic.UUID,
64+
UploadedAt: lic.UploadedAt,
65+
Features: formattedFeatures,
66+
ExpiresAt: exp,
67+
Trial: lic.Trial(),
68+
})
69+
}
70+
return out, nil
71+
}),
72+
cliui.ChangeFormatterData(cliui.JSONFormat(), func(data any) (any, error) {
73+
list, ok := data.([]codersdk.License)
74+
if !ok {
75+
return nil, xerrors.Errorf("invalid data type %T", data)
76+
}
77+
for i := range list {
78+
humanExp, err := list[i].ExpiresAt()
79+
if err == nil {
80+
list[i].Claims[codersdk.LicenseExpiryClaim+"_human"] = humanExp.Format(time.RFC3339)
81+
}
82+
}
83+
84+
return list, nil
85+
}),
86+
)
87+
}

cli/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1125,7 +1125,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
11251125
autobuildTicker := time.NewTicker(vals.AutobuildPollInterval.Value())
11261126
defer autobuildTicker.Stop()
11271127
autobuildExecutor := autobuild.NewExecutor(
1128-
ctx, options.Database, options.Pubsub, options.PrometheusRegistry, coderAPI.TemplateScheduleStore, &coderAPI.Auditor, coderAPI.AccessControlStore, logger, autobuildTicker.C, options.NotificationsEnqueuer, coderAPI.Experiments)
1128+
ctx, options.Database, options.Pubsub, coderAPI.FileCache, options.PrometheusRegistry, coderAPI.TemplateScheduleStore, &coderAPI.Auditor, coderAPI.AccessControlStore, logger, autobuildTicker.C, options.NotificationsEnqueuer, coderAPI.Experiments)
11291129
autobuildExecutor.Run()
11301130

11311131
jobReaperTicker := time.NewTicker(vals.JobReaperDetectorInterval.Value())

cli/support.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cli
33
import (
44
"archive/zip"
55
"bytes"
6+
"context"
67
"encoding/base64"
78
"encoding/json"
89
"fmt"
@@ -13,6 +14,8 @@ import (
1314
"text/tabwriter"
1415
"time"
1516

17+
"github.com/coder/coder/v2/cli/cliutil"
18+
1619
"github.com/google/uuid"
1720
"golang.org/x/xerrors"
1821

@@ -48,6 +51,7 @@ var supportBundleBlurb = cliui.Bold("This will collect the following information
4851
- Agent details (with environment variable sanitized)
4952
- Agent network diagnostics
5053
- Agent logs
54+
- License status
5155
` + cliui.Bold("Note: ") +
5256
cliui.Wrap("While we try to sanitize sensitive data from support bundles, we cannot guarantee that they do not contain information that you or your organization may consider sensitive.\n") +
5357
cliui.Bold("Please confirm that you will:\n") +
@@ -302,6 +306,11 @@ func writeBundle(src *support.Bundle, dest *zip.Writer) error {
302306
return xerrors.Errorf("decode template zip from base64")
303307
}
304308

309+
licenseStatus, err := humanizeLicenses(src.Deployment.Licenses)
310+
if err != nil {
311+
return xerrors.Errorf("format license status: %w", err)
312+
}
313+
305314
// The below we just write as we have them:
306315
for k, v := range map[string]string{
307316
"agent/logs.txt": string(src.Agent.Logs),
@@ -315,6 +324,7 @@ func writeBundle(src *support.Bundle, dest *zip.Writer) error {
315324
"network/tailnet_debug.html": src.Network.TailnetDebug,
316325
"workspace/build_logs.txt": humanizeBuildLogs(src.Workspace.BuildLogs),
317326
"workspace/template_file.zip": string(templateVersionBytes),
327+
"license-status.txt": licenseStatus,
318328
} {
319329
f, err := dest.Create(k)
320330
if err != nil {
@@ -359,3 +369,13 @@ func humanizeBuildLogs(ls []codersdk.ProvisionerJobLog) string {
359369
_ = tw.Flush()
360370
return buf.String()
361371
}
372+
373+
func humanizeLicenses(licenses []codersdk.License) (string, error) {
374+
formatter := cliutil.NewLicenseFormatter()
375+
376+
if len(licenses) == 0 {
377+
return "No licenses found", nil
378+
}
379+
380+
return formatter.Format(context.Background(), licenses)
381+
}

cli/support_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,9 @@ func assertBundleContents(t *testing.T, path string, wantWorkspace bool, wantAge
386386
case "cli_logs.txt":
387387
bs := readBytesFromZip(t, f)
388388
require.NotEmpty(t, bs, "CLI logs should not be empty")
389+
case "license-status.txt":
390+
bs := readBytesFromZip(t, f)
391+
require.NotEmpty(t, bs, "license status should not be empty")
389392
default:
390393
require.Failf(t, "unexpected file in bundle", f.Name)
391394
}

coderd/agentapi/subagent.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import (
1212
"golang.org/x/xerrors"
1313

1414
"cdr.dev/slog"
15+
"github.com/coder/quartz"
16+
1517
agentproto "github.com/coder/coder/v2/agent/proto"
1618
"github.com/coder/coder/v2/coderd/database"
1719
"github.com/coder/coder/v2/coderd/database/dbauthz"
1820
"github.com/coder/coder/v2/codersdk"
1921
"github.com/coder/coder/v2/provisioner"
20-
"github.com/coder/quartz"
2122
)
2223

2324
type SubAgentAPI struct {
@@ -164,8 +165,8 @@ func (a *SubAgentAPI) CreateSubAgent(ctx context.Context, req *agentproto.Create
164165
}
165166
}
166167

167-
_, err := a.Database.InsertWorkspaceApp(ctx, database.InsertWorkspaceAppParams{
168-
ID: uuid.New(),
168+
_, err := a.Database.UpsertWorkspaceApp(ctx, database.UpsertWorkspaceAppParams{
169+
ID: uuid.New(), // NOTE: we may need to maintain the app's ID here for stability, but for now we'll leave this as-is.
169170
CreatedAt: createdAt,
170171
AgentID: subAgent.ID,
171172
Slug: app.Slug,

coderd/apidoc/docs.go

Lines changed: 0 additions & 7 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: 0 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/autobuild/lifecycle_executor.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"golang.org/x/xerrors"
1818

1919
"cdr.dev/slog"
20+
"github.com/coder/coder/v2/coderd/files"
2021

2122
"github.com/coder/coder/v2/coderd/audit"
2223
"github.com/coder/coder/v2/coderd/database"
@@ -35,6 +36,7 @@ type Executor struct {
3536
ctx context.Context
3637
db database.Store
3738
ps pubsub.Pubsub
39+
fileCache *files.Cache
3840
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
3941
accessControlStore *atomic.Pointer[dbauthz.AccessControlStore]
4042
auditor *atomic.Pointer[audit.Auditor]
@@ -61,13 +63,14 @@ type Stats struct {
6163
}
6264

6365
// New returns a new wsactions executor.
64-
func NewExecutor(ctx context.Context, db database.Store, ps pubsub.Pubsub, reg prometheus.Registerer, tss *atomic.Pointer[schedule.TemplateScheduleStore], auditor *atomic.Pointer[audit.Auditor], acs *atomic.Pointer[dbauthz.AccessControlStore], log slog.Logger, tick <-chan time.Time, enqueuer notifications.Enqueuer, exp codersdk.Experiments) *Executor {
66+
func NewExecutor(ctx context.Context, db database.Store, ps pubsub.Pubsub, fc *files.Cache, reg prometheus.Registerer, tss *atomic.Pointer[schedule.TemplateScheduleStore], auditor *atomic.Pointer[audit.Auditor], acs *atomic.Pointer[dbauthz.AccessControlStore], log slog.Logger, tick <-chan time.Time, enqueuer notifications.Enqueuer, exp codersdk.Experiments) *Executor {
6567
factory := promauto.With(reg)
6668
le := &Executor{
6769
//nolint:gocritic // Autostart has a limited set of permissions.
6870
ctx: dbauthz.AsAutostart(ctx),
6971
db: db,
7072
ps: ps,
73+
fileCache: fc,
7174
templateScheduleStore: tss,
7275
tick: tick,
7376
log: log.Named("autobuild"),
@@ -276,7 +279,7 @@ func (e *Executor) runOnce(t time.Time) Stats {
276279
}
277280
}
278281

279-
nextBuild, job, _, err = builder.Build(e.ctx, tx, nil, audit.WorkspaceBuildBaggage{IP: "127.0.0.1"})
282+
nextBuild, job, _, err = builder.Build(e.ctx, tx, e.fileCache, nil, audit.WorkspaceBuildBaggage{IP: "127.0.0.1"})
280283
if err != nil {
281284
return xerrors.Errorf("build workspace with transition %q: %w", nextTransition, err)
282285
}

coderd/coderd.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ func New(options *Options) *API {
574574
TemplateScheduleStore: options.TemplateScheduleStore,
575575
UserQuietHoursScheduleStore: options.UserQuietHoursScheduleStore,
576576
AccessControlStore: options.AccessControlStore,
577-
FileCache: files.NewFromStore(options.Database, options.PrometheusRegistry, options.Authorizer),
577+
FileCache: files.New(options.PrometheusRegistry, options.Authorizer),
578578
Experiments: experiments,
579579
WebpushDispatcher: options.WebPushDispatcher,
580580
healthCheckGroup: &singleflight.Group[string, *healthsdk.HealthcheckReport]{},
@@ -972,7 +972,7 @@ func New(options *Options) *API {
972972
})
973973
r.Route("/experiments", func(r chi.Router) {
974974
r.Use(apiKeyMiddleware)
975-
r.Get("/available", handleExperimentsSafe)
975+
r.Get("/available", handleExperimentsAvailable)
976976
r.Get("/", api.handleExperimentsGet)
977977
})
978978
r.Get("/updatecheck", api.updateCheck)
@@ -1895,7 +1895,9 @@ func ReadExperiments(log slog.Logger, raw []string) codersdk.Experiments {
18951895
exps = append(exps, codersdk.ExperimentsSafe...)
18961896
default:
18971897
ex := codersdk.Experiment(strings.ToLower(v))
1898-
if !slice.Contains(codersdk.ExperimentsSafe, ex) {
1898+
if !slice.Contains(codersdk.ExperimentsKnown, ex) {
1899+
log.Warn(context.Background(), "ignoring unknown experiment", slog.F("experiment", ex))
1900+
} else if !slice.Contains(codersdk.ExperimentsSafe, ex) {
18991901
log.Warn(context.Background(), "🐉 HERE BE DRAGONS: opting into hidden experiment", slog.F("experiment", ex))
19001902
}
19011903
exps = append(exps, ex)

0 commit comments

Comments
 (0)