From c769c6e6e33c21ad9c94fb03383c022072d2bf4d Mon Sep 17 00:00:00 2001 From: Sas Swart Date: Thu, 26 Jun 2025 13:20:04 +0000 Subject: [PATCH] feat: graduate prebuilds to general availability --- cli/testdata/coder_server_--help.golden | 6 +++ coderd/apidoc/docs.go | 7 +--- coderd/apidoc/swagger.json | 7 +--- coderd/database/dbauthz/dbauthz_test.go | 3 +- coderd/database/dbmem/dbmem.go | 2 +- coderd/telemetry/telemetry.go | 4 -- coderd/telemetry/telemetry_test.go | 19 +-------- codersdk/deployment.go | 7 +--- .../prebuilt-workspaces.md | 1 - docs/reference/api/schemas.md | 1 - docs/reference/cli/server.md | 11 ++++++ .../cli/testdata/coder_server_--help.golden | 6 +++ enterprise/coderd/coderd.go | 13 ++----- enterprise/coderd/coderd_test.go | 39 +++++-------------- enterprise/coderd/workspaceagents_test.go | 1 - enterprise/coderd/workspaces_test.go | 5 +-- site/src/api/typesGenerated.ts | 2 - 17 files changed, 46 insertions(+), 88 deletions(-) diff --git a/cli/testdata/coder_server_--help.golden b/cli/testdata/coder_server_--help.golden index 19857cf8ebe76..4b1fa1ca4e6c9 100644 --- a/cli/testdata/coder_server_--help.golden +++ b/cli/testdata/coder_server_--help.golden @@ -677,6 +677,12 @@ workspaces stopping during the day due to template scheduling. must be *. Only one hour and minute can be specified (ranges or comma separated values are not supported). +WORKSPACE PREBUILDS OPTIONS: +Configure how workspace prebuilds behave. + + --workspace-prebuilds-reconciliation-interval duration, $CODER_WORKSPACE_PREBUILDS_RECONCILIATION_INTERVAL (default: 15s) + How often to reconcile workspace prebuilds state. + ⚠️ DANGEROUS OPTIONS: --dangerous-allow-path-app-sharing bool, $CODER_DANGEROUS_ALLOW_PATH_APP_SHARING Allow workspace apps that are not served from subdomains to be shared. diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index cbcfe1d72b380..522ba671a9a63 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -12194,15 +12194,13 @@ const docTemplate = `{ "auto-fill-parameters", "notifications", "workspace-usage", - "web-push", - "workspace-prebuilds" + "web-push" ], "x-enum-comments": { "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", "ExperimentExample": "This isn't used for anything.", "ExperimentNotifications": "Sends notifications via SMTP and webhooks following certain events.", "ExperimentWebPush": "Enables web push notifications through the browser.", - "ExperimentWorkspacePrebuilds": "Enables the new workspace prebuilds feature.", "ExperimentWorkspaceUsage": "Enables the new workspace usage tracking." }, "x-enum-varnames": [ @@ -12210,8 +12208,7 @@ const docTemplate = `{ "ExperimentAutoFillParameters", "ExperimentNotifications", "ExperimentWorkspaceUsage", - "ExperimentWebPush", - "ExperimentWorkspacePrebuilds" + "ExperimentWebPush" ] }, "codersdk.ExternalAuth": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index ef723652a0941..abcae550a4ec5 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -10927,15 +10927,13 @@ "auto-fill-parameters", "notifications", "workspace-usage", - "web-push", - "workspace-prebuilds" + "web-push" ], "x-enum-comments": { "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", "ExperimentExample": "This isn't used for anything.", "ExperimentNotifications": "Sends notifications via SMTP and webhooks following certain events.", "ExperimentWebPush": "Enables web push notifications through the browser.", - "ExperimentWorkspacePrebuilds": "Enables the new workspace prebuilds feature.", "ExperimentWorkspaceUsage": "Enables the new workspace usage tracking." }, "x-enum-varnames": [ @@ -10943,8 +10941,7 @@ "ExperimentAutoFillParameters", "ExperimentNotifications", "ExperimentWorkspaceUsage", - "ExperimentWebPush", - "ExperimentWorkspacePrebuilds" + "ExperimentWebPush" ] }, "codersdk.ExternalAuth": { diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 0361bf796cb55..6d1c8c3df601c 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -5059,8 +5059,7 @@ func (s *MethodTestSuite) TestPrebuilds() { })) s.Run("GetPrebuildMetrics", s.Subtest(func(_ database.Store, check *expects) { check.Args(). - Asserts(rbac.ResourceWorkspace.All(), policy.ActionRead). - ErrorsWithInMemDB(dbmem.ErrUnimplemented) + Asserts(rbac.ResourceWorkspace.All(), policy.ActionRead) })) s.Run("CountInProgressPrebuilds", s.Subtest(func(_ database.Store, check *expects) { check.Args(). diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index e5604d440073b..cd1067e61dbb5 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -4270,7 +4270,7 @@ func (q *FakeQuerier) GetParameterSchemasByJobID(_ context.Context, jobID uuid.U } func (*FakeQuerier) GetPrebuildMetrics(_ context.Context) ([]database.GetPrebuildMetricsRow, error) { - return nil, ErrUnimplemented + return make([]database.GetPrebuildMetricsRow, 0), nil } func (q *FakeQuerier) GetPresetByID(ctx context.Context, presetID uuid.UUID) (database.GetPresetByIDRow, error) { diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index dfc27418f4862..747cf2cb47de1 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -687,10 +687,6 @@ func (r *remoteReporter) createSnapshot() (*Snapshot, error) { return nil }) eg.Go(func() error { - if !r.options.Experiments.Enabled(codersdk.ExperimentWorkspacePrebuilds) { - return nil - } - metrics, err := r.options.Database.GetPrebuildMetrics(ctx) if err != nil { return xerrors.Errorf("get prebuild metrics: %w", err) diff --git a/coderd/telemetry/telemetry_test.go b/coderd/telemetry/telemetry_test.go index 9338e87d6d31c..ac836317b680e 100644 --- a/coderd/telemetry/telemetry_test.go +++ b/coderd/telemetry/telemetry_test.go @@ -408,7 +408,6 @@ func TestPrebuiltWorkspacesTelemetry(t *testing.T) { cases := []struct { name string - experimentEnabled bool storeFn func(store database.Store) database.Store expectedSnapshotEntries int expectedCreated int @@ -416,8 +415,7 @@ func TestPrebuiltWorkspacesTelemetry(t *testing.T) { expectedClaimed int }{ { - name: "experiment enabled", - experimentEnabled: true, + name: "prebuilds enabled", storeFn: func(store database.Store) database.Store { return &mockDB{Store: store} }, @@ -427,19 +425,11 @@ func TestPrebuiltWorkspacesTelemetry(t *testing.T) { expectedClaimed: 3, }, { - name: "experiment enabled, prebuilds not used", - experimentEnabled: true, + name: "prebuilds not used", storeFn: func(store database.Store) database.Store { return &emptyMockDB{Store: store} }, }, - { - name: "experiment disabled", - experimentEnabled: false, - storeFn: func(store database.Store) database.Store { - return &mockDB{Store: store} - }, - }, } for _, tc := range cases { @@ -448,11 +438,6 @@ func TestPrebuiltWorkspacesTelemetry(t *testing.T) { deployment, snapshot := collectSnapshot(ctx, t, db, func(opts telemetry.Options) telemetry.Options { opts.Database = tc.storeFn(db) - if tc.experimentEnabled { - opts.Experiments = codersdk.Experiments{ - codersdk.ExperimentWorkspacePrebuilds, - } - } return opts }) diff --git a/codersdk/deployment.go b/codersdk/deployment.go index ef0b4eaa07821..544e98f6f2a72 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -3070,7 +3070,6 @@ Write out the current server config as YAML to stdout.`, Group: &deploymentGroupPrebuilds, YAML: "reconciliation_interval", Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"), - Hidden: ExperimentsSafe.Enabled(ExperimentWorkspacePrebuilds), // Hide setting while this feature is experimental. }, { Name: "Reconciliation Backoff Interval", @@ -3342,7 +3341,6 @@ const ( ExperimentNotifications Experiment = "notifications" // Sends notifications via SMTP and webhooks following certain events. ExperimentWorkspaceUsage Experiment = "workspace-usage" // Enables the new workspace usage tracking. ExperimentWebPush Experiment = "web-push" // Enables web push notifications through the browser. - ExperimentWorkspacePrebuilds Experiment = "workspace-prebuilds" // Enables the new workspace prebuilds feature. ) // ExperimentsKnown should include all experiments defined above. @@ -3352,16 +3350,13 @@ var ExperimentsKnown = Experiments{ ExperimentNotifications, ExperimentWorkspaceUsage, ExperimentWebPush, - ExperimentWorkspacePrebuilds, } // ExperimentsSafe should include all experiments that are safe for // users to opt-in to via --experimental='*'. // Experiments that are not ready for consumption by all users should // not be included here and will be essentially hidden. -var ExperimentsSafe = Experiments{ - ExperimentWorkspacePrebuilds, -} +var ExperimentsSafe = Experiments{} // Experiments is a list of experiments. // Multiple experiments may be enabled at the same time. diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md index 08a404e040159..9d33425019b50 100644 --- a/docs/admin/templates/extending-templates/prebuilt-workspaces.md +++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md @@ -27,7 +27,6 @@ Prebuilt workspaces are tightly integrated with [workspace presets](./parameters - [**Premium license**](../../licensing/index.md) - **Compatible Terraform provider**: Use `coder/coder` Terraform provider `>= 2.4.1`. -- **Feature flag**: Enable the `workspace-prebuilds` [experiment](../../../reference/cli/server.md#--experiments). ## Enable prebuilt workspaces for template presets diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index e19c9d15da413..79c6f817bc776 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -2996,7 +2996,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `notifications` | | `workspace-usage` | | `web-push` | -| `workspace-prebuilds` | ## codersdk.ExternalAuth diff --git a/docs/reference/cli/server.md b/docs/reference/cli/server.md index 644065d35076f..f52b3666a866e 100644 --- a/docs/reference/cli/server.md +++ b/docs/reference/cli/server.md @@ -1615,6 +1615,17 @@ Enable Coder Inbox. The upper limit of attempts to send a notification. +### --workspace-prebuilds-reconciliation-interval + +| | | +|-------------|-----------------------------------------------------------------| +| Type | duration | +| Environment | $CODER_WORKSPACE_PREBUILDS_RECONCILIATION_INTERVAL | +| YAML | workspace_prebuilds.reconciliation_interval | +| Default | 15s | + +How often to reconcile workspace prebuilds state. + ### --hide-ai-tasks | | | diff --git a/enterprise/cli/testdata/coder_server_--help.golden b/enterprise/cli/testdata/coder_server_--help.golden index 3e3868c5ae432..d7c26bc537693 100644 --- a/enterprise/cli/testdata/coder_server_--help.golden +++ b/enterprise/cli/testdata/coder_server_--help.golden @@ -678,6 +678,12 @@ workspaces stopping during the day due to template scheduling. must be *. Only one hour and minute can be specified (ranges or comma separated values are not supported). +WORKSPACE PREBUILDS OPTIONS: +Configure how workspace prebuilds behave. + + --workspace-prebuilds-reconciliation-interval duration, $CODER_WORKSPACE_PREBUILDS_RECONCILIATION_INTERVAL (default: 15s) + How often to reconcile workspace prebuilds state. + ⚠️ DANGEROUS OPTIONS: --dangerous-allow-path-app-sharing bool, $CODER_DANGEROUS_ALLOW_PATH_APP_SHARING Allow workspace apps that are not served from subdomains to be shared. diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 9b168c8e3f366..601700403f326 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -1150,16 +1150,9 @@ func (api *API) Authorize(r *http.Request, action policy.Action, object rbac.Obj // nolint:revive // featureEnabled is a legit control flag. func (api *API) setupPrebuilds(featureEnabled bool) (agplprebuilds.ReconciliationOrchestrator, agplprebuilds.Claimer) { - experimentEnabled := api.AGPL.Experiments.Enabled(codersdk.ExperimentWorkspacePrebuilds) - if !experimentEnabled || !featureEnabled { - levelFn := api.Logger.Debug - // If the experiment is enabled but the license does not entitle the feature, operators should be warned. - if !featureEnabled { - levelFn = api.Logger.Warn - } - - levelFn(context.Background(), "prebuilds not enabled; ensure you have a premium license and the 'workspace-prebuilds' experiment set", - slog.F("experiment_enabled", experimentEnabled), slog.F("feature_enabled", featureEnabled)) + if !featureEnabled { + api.Logger.Warn(context.Background(), "prebuilds not enabled; ensure you have a premium license", + slog.F("feature_enabled", featureEnabled)) return agplprebuilds.DefaultReconciler, agplprebuilds.DefaultClaimer } diff --git a/enterprise/coderd/coderd_test.go b/enterprise/coderd/coderd_test.go index 1c8c863db040b..89a61c657e21a 100644 --- a/enterprise/coderd/coderd_test.go +++ b/enterprise/coderd/coderd_test.go @@ -260,34 +260,19 @@ func TestEntitlements_Prebuilds(t *testing.T) { t.Parallel() cases := []struct { - name string - experimentEnabled bool - featureEnabled bool - expectedEnabled bool + name string + featureEnabled bool + expectedEnabled bool }{ { - name: "Fully enabled", - featureEnabled: true, - experimentEnabled: true, - expectedEnabled: true, + name: "Feature enabled", + featureEnabled: true, + expectedEnabled: true, }, { - name: "Feature disabled", - featureEnabled: false, - experimentEnabled: true, - expectedEnabled: false, - }, - { - name: "Experiment disabled", - featureEnabled: true, - experimentEnabled: false, - expectedEnabled: false, - }, - { - name: "Fully disabled", - featureEnabled: false, - experimentEnabled: false, - expectedEnabled: false, + name: "Feature disabled", + featureEnabled: false, + expectedEnabled: false, }, } @@ -302,11 +287,7 @@ func TestEntitlements_Prebuilds(t *testing.T) { _, _, api, _ := coderdenttest.NewWithAPI(t, &coderdenttest.Options{ Options: &coderdtest.Options{ - DeploymentValues: coderdtest.DeploymentValues(t, func(values *codersdk.DeploymentValues) { - if tc.experimentEnabled { - values.Experiments = serpent.StringArray{string(codersdk.ExperimentWorkspacePrebuilds)} - } - }), + DeploymentValues: coderdtest.DeploymentValues(t), }, EntitlementsUpdateInterval: time.Second, diff --git a/enterprise/coderd/workspaceagents_test.go b/enterprise/coderd/workspaceagents_test.go index 1eea9ecda9ca8..f4f0670cd150e 100644 --- a/enterprise/coderd/workspaceagents_test.go +++ b/enterprise/coderd/workspaceagents_test.go @@ -112,7 +112,6 @@ func TestReinitializeAgent(t *testing.T) { Pubsub: ps, DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { dv.Prebuilds.ReconciliationInterval = serpent.Duration(time.Second) - dv.Experiments.Append(string(codersdk.ExperimentWorkspacePrebuilds)) }), }, LicenseOptions: &coderdenttest.LicenseOptions{ diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index 228b11f485a96..3bed052702637 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -531,10 +531,7 @@ func TestCreateUserWorkspace(t *testing.T) { client, db, user := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ Options: &coderdtest.Options{ - DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { - err := dv.Experiments.Append(string(codersdk.ExperimentWorkspacePrebuilds)) - require.NoError(t, err) - }), + DeploymentValues: coderdtest.DeploymentValues(t), }, LicenseOptions: &coderdenttest.LicenseOptions{ Features: license.Features{ diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index b2117cf15c987..0e6a481406d8b 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -795,7 +795,6 @@ export type Experiment = | "example" | "notifications" | "web-push" - | "workspace-prebuilds" | "workspace-usage"; export const Experiments: Experiment[] = [ @@ -803,7 +802,6 @@ export const Experiments: Experiment[] = [ "example", "notifications", "web-push", - "workspace-prebuilds", "workspace-usage", ];