chore: add experiment toggle for terraform workspace caching (#20559)

Experiments passed to provisioners to determine behavior. This adds
`--experiments` flag to provisioner daemons. Prior to this, provisioners
had no method to turn on/off experiments.
This commit is contained in:
Steven Masley
2025-11-12 14:26:15 -06:00
committed by GitHub
parent ac2c161636
commit 04727c06e8
15 changed files with 65 additions and 15 deletions
+1
View File
@@ -1476,6 +1476,7 @@ func newProvisionerDaemon(
Listener: terraformServer, Listener: terraformServer,
Logger: provisionerLogger, Logger: provisionerLogger,
WorkDirectory: workDir, WorkDirectory: workDir,
Experiments: coderAPI.Experiments,
}, },
CachePath: tfDir, CachePath: tfDir,
Tracer: tracer, Tracer: tracer,
+5 -2
View File
@@ -14322,7 +14322,8 @@ const docTemplate = `{
"web-push", "web-push",
"oauth2", "oauth2",
"mcp-server-http", "mcp-server-http",
"workspace-sharing" "workspace-sharing",
"terraform-directory-reuse"
], ],
"x-enum-comments": { "x-enum-comments": {
"ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.",
@@ -14330,6 +14331,7 @@ const docTemplate = `{
"ExperimentMCPServerHTTP": "Enables the MCP HTTP server functionality.", "ExperimentMCPServerHTTP": "Enables the MCP HTTP server functionality.",
"ExperimentNotifications": "Sends notifications via SMTP and webhooks following certain events.", "ExperimentNotifications": "Sends notifications via SMTP and webhooks following certain events.",
"ExperimentOAuth2": "Enables OAuth2 provider functionality.", "ExperimentOAuth2": "Enables OAuth2 provider functionality.",
"ExperimentTerraformWorkspace": "Enables reuse of existing terraform directory for builds",
"ExperimentWebPush": "Enables web push notifications through the browser.", "ExperimentWebPush": "Enables web push notifications through the browser.",
"ExperimentWorkspaceSharing": "Enables updating workspace ACLs for sharing with users and groups.", "ExperimentWorkspaceSharing": "Enables updating workspace ACLs for sharing with users and groups.",
"ExperimentWorkspaceUsage": "Enables the new workspace usage tracking." "ExperimentWorkspaceUsage": "Enables the new workspace usage tracking."
@@ -14342,7 +14344,8 @@ const docTemplate = `{
"ExperimentWebPush", "ExperimentWebPush",
"ExperimentOAuth2", "ExperimentOAuth2",
"ExperimentMCPServerHTTP", "ExperimentMCPServerHTTP",
"ExperimentWorkspaceSharing" "ExperimentWorkspaceSharing",
"ExperimentTerraformWorkspace"
] ]
}, },
"codersdk.ExternalAPIKeyScopes": { "codersdk.ExternalAPIKeyScopes": {
+5 -2
View File
@@ -12929,7 +12929,8 @@
"web-push", "web-push",
"oauth2", "oauth2",
"mcp-server-http", "mcp-server-http",
"workspace-sharing" "workspace-sharing",
"terraform-directory-reuse"
], ],
"x-enum-comments": { "x-enum-comments": {
"ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.",
@@ -12937,6 +12938,7 @@
"ExperimentMCPServerHTTP": "Enables the MCP HTTP server functionality.", "ExperimentMCPServerHTTP": "Enables the MCP HTTP server functionality.",
"ExperimentNotifications": "Sends notifications via SMTP and webhooks following certain events.", "ExperimentNotifications": "Sends notifications via SMTP and webhooks following certain events.",
"ExperimentOAuth2": "Enables OAuth2 provider functionality.", "ExperimentOAuth2": "Enables OAuth2 provider functionality.",
"ExperimentTerraformWorkspace": "Enables reuse of existing terraform directory for builds",
"ExperimentWebPush": "Enables web push notifications through the browser.", "ExperimentWebPush": "Enables web push notifications through the browser.",
"ExperimentWorkspaceSharing": "Enables updating workspace ACLs for sharing with users and groups.", "ExperimentWorkspaceSharing": "Enables updating workspace ACLs for sharing with users and groups.",
"ExperimentWorkspaceUsage": "Enables the new workspace usage tracking." "ExperimentWorkspaceUsage": "Enables the new workspace usage tracking."
@@ -12949,7 +12951,8 @@
"ExperimentWebPush", "ExperimentWebPush",
"ExperimentOAuth2", "ExperimentOAuth2",
"ExperimentMCPServerHTTP", "ExperimentMCPServerHTTP",
"ExperimentWorkspaceSharing" "ExperimentWorkspaceSharing",
"ExperimentTerraformWorkspace"
] ]
}, },
"codersdk.ExternalAPIKeyScopes": { "codersdk.ExternalAPIKeyScopes": {
+1
View File
@@ -1999,6 +1999,7 @@ func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, n
api.NotificationsEnqueuer, api.NotificationsEnqueuer,
&api.PrebuildsReconciler, &api.PrebuildsReconciler,
api.ProvisionerdServerMetrics, api.ProvisionerdServerMetrics,
api.Experiments,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@@ -121,6 +121,7 @@ type server struct {
NotificationsEnqueuer notifications.Enqueuer NotificationsEnqueuer notifications.Enqueuer
PrebuildsOrchestrator *atomic.Pointer[prebuilds.ReconciliationOrchestrator] PrebuildsOrchestrator *atomic.Pointer[prebuilds.ReconciliationOrchestrator]
UsageInserter *atomic.Pointer[usage.Inserter] UsageInserter *atomic.Pointer[usage.Inserter]
Experiments codersdk.Experiments
OIDCConfig promoauth.OAuth2Config OIDCConfig promoauth.OAuth2Config
@@ -182,6 +183,7 @@ func NewServer(
enqueuer notifications.Enqueuer, enqueuer notifications.Enqueuer,
prebuildsOrchestrator *atomic.Pointer[prebuilds.ReconciliationOrchestrator], prebuildsOrchestrator *atomic.Pointer[prebuilds.ReconciliationOrchestrator],
metrics *Metrics, metrics *Metrics,
experiments codersdk.Experiments,
) (proto.DRPCProvisionerDaemonServer, error) { ) (proto.DRPCProvisionerDaemonServer, error) {
// Fail-fast if pointers are nil // Fail-fast if pointers are nil
if lifecycleCtx == nil { if lifecycleCtx == nil {
@@ -253,6 +255,7 @@ func NewServer(
PrebuildsOrchestrator: prebuildsOrchestrator, PrebuildsOrchestrator: prebuildsOrchestrator,
UsageInserter: usageInserter, UsageInserter: usageInserter,
metrics: metrics, metrics: metrics,
Experiments: experiments,
} }
if s.heartbeatFn == nil { if s.heartbeatFn == nil {
@@ -26,6 +26,7 @@ import (
"storj.io/drpc" "storj.io/drpc"
"cdr.dev/slog/sloggers/slogtest" "cdr.dev/slog/sloggers/slogtest"
"github.com/coder/coder/v2/coderd"
"github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/quartz" "github.com/coder/quartz"
"github.com/coder/serpent" "github.com/coder/serpent"
@@ -4162,7 +4163,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
defOrg, err := db.GetDefaultOrganization(context.Background()) defOrg, err := db.GetDefaultOrganization(context.Background())
require.NoError(t, err, "default org not found") require.NoError(t, err, "default org not found")
deploymentValues := &codersdk.DeploymentValues{} deploymentValues := coderdtest.DeploymentValues(t)
var externalAuthConfigs []*externalauth.Config var externalAuthConfigs []*externalauth.Config
tss := testTemplateScheduleStore() tss := testTemplateScheduleStore()
uqhss := testUserQuietHoursScheduleStore() uqhss := testUserQuietHoursScheduleStore()
@@ -4285,6 +4286,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
notifEnq, notifEnq,
&op, &op,
provisionerdserver.NewMetrics(logger), provisionerdserver.NewMetrics(logger),
coderd.ReadExperiments(logger, deploymentValues.Experiments),
) )
require.NoError(t, err) require.NoError(t, err)
return srv, db, ps, daemon return srv, db, ps, daemon
+4
View File
@@ -3646,6 +3646,8 @@ const (
ExperimentOAuth2 Experiment = "oauth2" // Enables OAuth2 provider functionality. ExperimentOAuth2 Experiment = "oauth2" // Enables OAuth2 provider functionality.
ExperimentMCPServerHTTP Experiment = "mcp-server-http" // Enables the MCP HTTP server functionality. ExperimentMCPServerHTTP Experiment = "mcp-server-http" // Enables the MCP HTTP server functionality.
ExperimentWorkspaceSharing Experiment = "workspace-sharing" // Enables updating workspace ACLs for sharing with users and groups. ExperimentWorkspaceSharing Experiment = "workspace-sharing" // Enables updating workspace ACLs for sharing with users and groups.
// ExperimentTerraformWorkspace uses the "Terraform Workspaces" feature, not to be confused with Coder Workspaces.
ExperimentTerraformWorkspace Experiment = "terraform-directory-reuse" // Enables reuse of existing terraform directory for builds
) )
func (e Experiment) DisplayName() string { func (e Experiment) DisplayName() string {
@@ -3666,6 +3668,8 @@ func (e Experiment) DisplayName() string {
return "MCP HTTP Server Functionality" return "MCP HTTP Server Functionality"
case ExperimentWorkspaceSharing: case ExperimentWorkspaceSharing:
return "Workspace Sharing" return "Workspace Sharing"
case ExperimentTerraformWorkspace:
return "Terraform Directory Reuse"
default: default:
// Split on hyphen and convert to title case // Split on hyphen and convert to title case
// e.g. "web-push" -> "Web Push", "mcp-server-http" -> "Mcp Server Http" // e.g. "web-push" -> "Web Push", "mcp-server-http" -> "Mcp Server Http"
+11 -10
View File
@@ -4057,16 +4057,17 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
#### Enumerated Values #### Enumerated Values
| Value | | Value |
|------------------------| |-----------------------------|
| `example` | | `example` |
| `auto-fill-parameters` | | `auto-fill-parameters` |
| `notifications` | | `notifications` |
| `workspace-usage` | | `workspace-usage` |
| `web-push` | | `web-push` |
| `oauth2` | | `oauth2` |
| `mcp-server-http` | | `mcp-server-http` |
| `workspace-sharing` | | `workspace-sharing` |
| `terraform-directory-reuse` |
## codersdk.ExternalAPIKeyScopes ## codersdk.ExternalAPIKeyScopes
+10
View File
@@ -144,6 +144,16 @@ Serve prometheus metrics on the address defined by prometheus address.
The bind address to serve prometheus metrics. The bind address to serve prometheus metrics.
### --experiments
| | |
|-------------|---------------------------------|
| Type | <code>string-array</code> |
| Environment | <code>$CODER_EXPERIMENTS</code> |
| YAML | <code>experiments</code> |
Enable one or more experiments. These are not ready for production. Separate multiple experiments with commas, or enter '*' to opt-in to all available experiments.
### -O, --org ### -O, --org
| | | | | |
+11
View File
@@ -23,6 +23,7 @@ import (
"github.com/coder/coder/v2/cli/clilog" "github.com/coder/coder/v2/cli/clilog"
"github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/cli/cliutil" "github.com/coder/coder/v2/cli/cliutil"
"github.com/coder/coder/v2/coderd"
"github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/drpcsdk" "github.com/coder/coder/v2/codersdk/drpcsdk"
@@ -48,6 +49,7 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
preSharedKey string preSharedKey string
provisionerKey string provisionerKey string
verbose bool verbose bool
experiments []string
prometheusEnable bool prometheusEnable bool
prometheusAddress string prometheusAddress string
@@ -186,6 +188,7 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
Listener: terraformServer, Listener: terraformServer,
Logger: logger.Named("terraform"), Logger: logger.Named("terraform"),
WorkDirectory: tempDir, WorkDirectory: tempDir,
Experiments: coderd.ReadExperiments(logger, experiments),
}, },
CachePath: cacheDir, CachePath: cacheDir,
}) })
@@ -378,6 +381,14 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
Value: serpent.StringOf(&prometheusAddress), Value: serpent.StringOf(&prometheusAddress),
Default: "127.0.0.1:2112", Default: "127.0.0.1:2112",
}, },
{
Name: "Experiments",
Description: "Enable one or more experiments. These are not ready for production. Separate multiple experiments with commas, or enter '*' to opt-in to all available experiments.",
Flag: "experiments",
Env: "CODER_EXPERIMENTS",
Value: serpent.StringArrayOf(&experiments),
YAML: "experiments",
},
} }
orgContext.AttachOptions(cmd) orgContext.AttachOptions(cmd)
@@ -6,6 +6,11 @@ USAGE:
Run a provisioner daemon Run a provisioner daemon
OPTIONS: OPTIONS:
--experiments string-array, $CODER_EXPERIMENTS
Enable one or more experiments. These are not ready for production.
Separate multiple experiments with commas, or enter '*' to opt-in to
all available experiments.
-O, --org string, $CODER_ORGANIZATION -O, --org string, $CODER_ORGANIZATION
Select which organization (uuid or name) to use. Select which organization (uuid or name) to use.
@@ -414,6 +414,7 @@ func newExternalProvisionerDaemon(t testing.TB, client *codersdk.Client, org uui
ServeOptions: &provisionersdk.ServeOptions{ ServeOptions: &provisionersdk.ServeOptions{
Listener: provisionerSrv, Listener: provisionerSrv,
WorkDirectory: t.TempDir(), WorkDirectory: t.TempDir(),
Experiments: codersdk.Experiments{},
}, },
})) }))
}() }()
+1
View File
@@ -362,6 +362,7 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
api.NotificationsEnqueuer, api.NotificationsEnqueuer,
&api.AGPL.PrebuildsReconciler, &api.AGPL.PrebuildsReconciler,
api.ProvisionerdServerMetrics, api.ProvisionerdServerMetrics,
api.AGPL.Experiments,
) )
if err != nil { if err != nil {
if !xerrors.Is(err, context.Canceled) { if !xerrors.Is(err, context.Canceled) {
+2
View File
@@ -15,6 +15,7 @@ import (
"storj.io/drpc/drpcserver" "storj.io/drpc/drpcserver"
"cdr.dev/slog" "cdr.dev/slog"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/drpcsdk" "github.com/coder/coder/v2/codersdk/drpcsdk"
"github.com/coder/coder/v2/coderd/tracing" "github.com/coder/coder/v2/coderd/tracing"
@@ -30,6 +31,7 @@ type ServeOptions struct {
Logger slog.Logger Logger slog.Logger
WorkDirectory string WorkDirectory string
ExternalProvisioner bool ExternalProvisioner bool
Experiments codersdk.Experiments
} }
type Server interface { type Server interface {
+2
View File
@@ -1895,6 +1895,7 @@ export type Experiment =
| "mcp-server-http" | "mcp-server-http"
| "notifications" | "notifications"
| "oauth2" | "oauth2"
| "terraform-directory-reuse"
| "web-push" | "web-push"
| "workspace-sharing" | "workspace-sharing"
| "workspace-usage"; | "workspace-usage";
@@ -1905,6 +1906,7 @@ export const Experiments: Experiment[] = [
"mcp-server-http", "mcp-server-http",
"notifications", "notifications",
"oauth2", "oauth2",
"terraform-directory-reuse",
"web-push", "web-push",
"workspace-sharing", "workspace-sharing",
"workspace-usage", "workspace-usage",