Skip to content

Commit af2941b

Browse files
authored
feat: add is_prebuild_claim to distinguish post-claim provisioning (#17757)
Used in combination with coder/terraform-provider-coder#396 This is required by both #17475 and #17571 Operators may need to conditionalize their templates to perform certain operations once a prebuilt workspace has been claimed. This value will **only** be set once a claim takes place and a subsequent `terraform apply` occurs. Any `terraform apply` runs thereafter will be indistinguishable from a normal run on a workspace. --------- Signed-off-by: Danny Kopping <dannykopping@gmail.com>
1 parent 799a0ba commit af2941b

15 files changed

+478
-339
lines changed

cli/testdata/coder_provisioner_list_--output_json.golden

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"last_seen_at": "====[timestamp]=====",
88
"name": "test",
99
"version": "v0.0.0-devel",
10-
"api_version": "1.4",
10+
"api_version": "1.5",
1111
"provisioners": [
1212
"echo"
1313
],

coderd/provisionerdserver/provisionerdserver.go

+5-6
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
645645
WorkspaceBuildId: workspaceBuild.ID.String(),
646646
WorkspaceOwnerLoginType: string(owner.LoginType),
647647
WorkspaceOwnerRbacRoles: ownerRbacRoles,
648-
IsPrebuild: input.IsPrebuild,
648+
PrebuiltWorkspaceBuildStage: input.PrebuiltWorkspaceBuildStage,
649649
},
650650
LogLevel: input.LogLevel,
651651
},
@@ -2471,11 +2471,10 @@ type TemplateVersionImportJob struct {
24712471

24722472
// WorkspaceProvisionJob is the payload for the "workspace_provision" job type.
24732473
type WorkspaceProvisionJob struct {
2474-
WorkspaceBuildID uuid.UUID `json:"workspace_build_id"`
2475-
DryRun bool `json:"dry_run"`
2476-
IsPrebuild bool `json:"is_prebuild,omitempty"`
2477-
PrebuildClaimedByUser uuid.UUID `json:"prebuild_claimed_by,omitempty"`
2478-
LogLevel string `json:"log_level,omitempty"`
2474+
WorkspaceBuildID uuid.UUID `json:"workspace_build_id"`
2475+
DryRun bool `json:"dry_run"`
2476+
LogLevel string `json:"log_level,omitempty"`
2477+
PrebuiltWorkspaceBuildStage sdkproto.PrebuiltWorkspaceBuildStage `json:"prebuilt_workspace_stage,omitempty"`
24792478
}
24802479

24812480
// TemplateVersionDryRunJob is the payload for the "template_version_dry_run" job type.

coderd/provisionerdserver/provisionerdserver_test.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ import (
2323
"storj.io/drpc"
2424

2525
"cdr.dev/slog/sloggers/slogtest"
26-
"github.com/coder/coder/v2/coderd/rbac"
2726
"github.com/coder/quartz"
2827
"github.com/coder/serpent"
2928

29+
"github.com/coder/coder/v2/coderd/rbac"
30+
3031
"github.com/coder/coder/v2/buildinfo"
3132
"github.com/coder/coder/v2/coderd/audit"
3233
"github.com/coder/coder/v2/coderd/database"
@@ -299,6 +300,10 @@ func TestAcquireJob(t *testing.T) {
299300
Transition: database.WorkspaceTransitionStart,
300301
Reason: database.BuildReasonInitiator,
301302
})
303+
var buildState sdkproto.PrebuiltWorkspaceBuildStage
304+
if prebuiltWorkspace {
305+
buildState = sdkproto.PrebuiltWorkspaceBuildStage_CREATE
306+
}
302307
_ = dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
303308
ID: build.ID,
304309
OrganizationID: pd.OrganizationID,
@@ -308,8 +313,8 @@ func TestAcquireJob(t *testing.T) {
308313
FileID: file.ID,
309314
Type: database.ProvisionerJobTypeWorkspaceBuild,
310315
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
311-
WorkspaceBuildID: build.ID,
312-
IsPrebuild: prebuiltWorkspace,
316+
WorkspaceBuildID: build.ID,
317+
PrebuiltWorkspaceBuildStage: buildState,
313318
})),
314319
})
315320

@@ -380,7 +385,7 @@ func TestAcquireJob(t *testing.T) {
380385
WorkspaceOwnerRbacRoles: []*sdkproto.Role{{Name: rbac.RoleOrgMember(), OrgId: pd.OrganizationID.String()}, {Name: "member", OrgId: ""}, {Name: rbac.RoleOrgAuditor(), OrgId: pd.OrganizationID.String()}},
381386
}
382387
if prebuiltWorkspace {
383-
wantedMetadata.IsPrebuild = true
388+
wantedMetadata.PrebuiltWorkspaceBuildStage = sdkproto.PrebuiltWorkspaceBuildStage_CREATE
384389
}
385390

386391
slices.SortFunc(wantedMetadata.WorkspaceOwnerRbacRoles, func(a, b *sdkproto.Role) int {

coderd/workspaces.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ func createWorkspace(
713713
builder = builder.TemplateVersionPresetID(req.TemplateVersionPresetID)
714714
}
715715
if claimedWorkspace != nil {
716-
builder = builder.MarkPrebuildClaimedBy(owner.ID)
716+
builder = builder.MarkPrebuiltWorkspaceClaim()
717717
}
718718

719719
if req.EnableDynamicParameters && api.Experiments.Enabled(codersdk.ExperimentDynamicParameters) {

coderd/wsbuilder/wsbuilder.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/coder/coder/v2/coderd/rbac/policy"
1717
"github.com/coder/coder/v2/provisioner/terraform/tfparse"
1818
"github.com/coder/coder/v2/provisionersdk"
19+
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
1920

2021
"github.com/google/uuid"
2122
"github.com/sqlc-dev/pqtype"
@@ -76,8 +77,7 @@ type Builder struct {
7677
parameterValues *[]string
7778
templateVersionPresetParameterValues []database.TemplateVersionPresetParameter
7879

79-
prebuild bool
80-
prebuildClaimedBy uuid.UUID
80+
prebuiltWorkspaceBuildStage sdkproto.PrebuiltWorkspaceBuildStage
8181

8282
verifyNoLegacyParametersOnce bool
8383
}
@@ -174,15 +174,17 @@ func (b Builder) RichParameterValues(p []codersdk.WorkspaceBuildParameter) Build
174174
return b
175175
}
176176

177+
// MarkPrebuild indicates that a prebuilt workspace is being built.
177178
func (b Builder) MarkPrebuild() Builder {
178179
// nolint: revive
179-
b.prebuild = true
180+
b.prebuiltWorkspaceBuildStage = sdkproto.PrebuiltWorkspaceBuildStage_CREATE
180181
return b
181182
}
182183

183-
func (b Builder) MarkPrebuildClaimedBy(userID uuid.UUID) Builder {
184+
// MarkPrebuiltWorkspaceClaim indicates that a prebuilt workspace is being claimed.
185+
func (b Builder) MarkPrebuiltWorkspaceClaim() Builder {
184186
// nolint: revive
185-
b.prebuildClaimedBy = userID
187+
b.prebuiltWorkspaceBuildStage = sdkproto.PrebuiltWorkspaceBuildStage_CLAIM
186188
return b
187189
}
188190

@@ -322,10 +324,9 @@ func (b *Builder) buildTx(authFunc func(action policy.Action, object rbac.Object
322324

323325
workspaceBuildID := uuid.New()
324326
input, err := json.Marshal(provisionerdserver.WorkspaceProvisionJob{
325-
WorkspaceBuildID: workspaceBuildID,
326-
LogLevel: b.logLevel,
327-
IsPrebuild: b.prebuild,
328-
PrebuildClaimedByUser: b.prebuildClaimedBy,
327+
WorkspaceBuildID: workspaceBuildID,
328+
LogLevel: b.logLevel,
329+
PrebuiltWorkspaceBuildStage: b.prebuiltWorkspaceBuildStage,
329330
})
330331
if err != nil {
331332
return nil, nil, nil, BuildError{

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ require (
101101
github.com/coder/quartz v0.1.3
102102
github.com/coder/retry v1.5.1
103103
github.com/coder/serpent v0.10.0
104-
github.com/coder/terraform-provider-coder/v2 v2.4.0
104+
github.com/coder/terraform-provider-coder/v2 v2.4.1
105105
github.com/coder/websocket v1.8.13
106106
github.com/coder/wgtunnel v0.1.13-0.20240522110300-ade90dfb2da0
107107
github.com/coreos/go-oidc/v3 v3.14.1

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -925,8 +925,8 @@ github.com/coder/tailscale v1.1.1-0.20250422090654-5090e715905e h1:nope/SZfoLB9M
925925
github.com/coder/tailscale v1.1.1-0.20250422090654-5090e715905e/go.mod h1:1ggFFdHTRjPRu9Yc1yA7nVHBYB50w9Ce7VIXNqcW6Ko=
926926
github.com/coder/terraform-config-inspect v0.0.0-20250107175719-6d06d90c630e h1:JNLPDi2P73laR1oAclY6jWzAbucf70ASAvf5mh2cME0=
927927
github.com/coder/terraform-config-inspect v0.0.0-20250107175719-6d06d90c630e/go.mod h1:Gz/z9Hbn+4KSp8A2FBtNszfLSdT2Tn/uAKGuVqqWmDI=
928-
github.com/coder/terraform-provider-coder/v2 v2.4.0 h1:uuFmF03IyahAZLXEukOdmvV9hGfUMJSESD8+G5wkTcM=
929-
github.com/coder/terraform-provider-coder/v2 v2.4.0/go.mod h1:2kaBpn5k9ZWtgKq5k4JbkVZG9DzEqR4mJSmpdshcO+s=
928+
github.com/coder/terraform-provider-coder/v2 v2.4.1 h1:+HxLJVENJ+kvGhibQ0jbr8Evi6M857d9691ytxNbv90=
929+
github.com/coder/terraform-provider-coder/v2 v2.4.1/go.mod h1:2kaBpn5k9ZWtgKq5k4JbkVZG9DzEqR4mJSmpdshcO+s=
930930
github.com/coder/trivy v0.0.0-20250409153844-e6b004bc465a h1:yryP7e+IQUAArlycH4hQrjXQ64eRNbxsV5/wuVXHgME=
931931
github.com/coder/trivy v0.0.0-20250409153844-e6b004bc465a/go.mod h1:dDvq9axp3kZsT63gY2Znd1iwzfqDq3kXbQnccIrjRYY=
932932
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=

provisioner/terraform/provision.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,12 @@ func provisionEnv(
270270
"CODER_WORKSPACE_TEMPLATE_VERSION="+metadata.GetTemplateVersion(),
271271
"CODER_WORKSPACE_BUILD_ID="+metadata.GetWorkspaceBuildId(),
272272
)
273-
if metadata.GetIsPrebuild() {
273+
if metadata.GetPrebuiltWorkspaceBuildStage().IsPrebuild() {
274274
env = append(env, provider.IsPrebuildEnvironmentVariable()+"=true")
275275
}
276+
if metadata.GetPrebuiltWorkspaceBuildStage().IsPrebuiltWorkspaceClaim() {
277+
env = append(env, provider.IsPrebuildClaimEnvironmentVariable()+"=true")
278+
}
276279

277280
for key, value := range provisionersdk.AgentScriptEnv() {
278281
env = append(env, key+"="+value)

provisioner/terraform/provision_test.go

+41-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"cdr.dev/slog"
2727
"cdr.dev/slog/sloggers/slogtest"
28+
2829
"github.com/coder/coder/v2/codersdk/drpc"
2930
"github.com/coder/coder/v2/provisioner/terraform"
3031
"github.com/coder/coder/v2/provisionersdk"
@@ -977,7 +978,7 @@ func TestProvision(t *testing.T) {
977978
required_providers {
978979
coder = {
979980
source = "coder/coder"
980-
version = "2.3.0-pre2"
981+
version = ">= 2.4.1"
981982
}
982983
}
983984
}
@@ -994,7 +995,7 @@ func TestProvision(t *testing.T) {
994995
},
995996
Request: &proto.PlanRequest{
996997
Metadata: &proto.Metadata{
997-
IsPrebuild: true,
998+
PrebuiltWorkspaceBuildStage: proto.PrebuiltWorkspaceBuildStage_CREATE,
998999
},
9991000
},
10001001
Response: &proto.PlanComplete{
@@ -1008,6 +1009,44 @@ func TestProvision(t *testing.T) {
10081009
}},
10091010
},
10101011
},
1012+
{
1013+
Name: "is-prebuild-claim",
1014+
Files: map[string]string{
1015+
"main.tf": `terraform {
1016+
required_providers {
1017+
coder = {
1018+
source = "coder/coder"
1019+
version = ">= 2.4.1"
1020+
}
1021+
}
1022+
}
1023+
data "coder_workspace" "me" {}
1024+
resource "null_resource" "example" {}
1025+
resource "coder_metadata" "example" {
1026+
resource_id = null_resource.example.id
1027+
item {
1028+
key = "is_prebuild_claim"
1029+
value = data.coder_workspace.me.is_prebuild_claim
1030+
}
1031+
}
1032+
`,
1033+
},
1034+
Request: &proto.PlanRequest{
1035+
Metadata: &proto.Metadata{
1036+
PrebuiltWorkspaceBuildStage: proto.PrebuiltWorkspaceBuildStage_CLAIM,
1037+
},
1038+
},
1039+
Response: &proto.PlanComplete{
1040+
Resources: []*proto.Resource{{
1041+
Name: "example",
1042+
Type: "null_resource",
1043+
Metadata: []*proto.Resource_Metadata{{
1044+
Key: "is_prebuild_claim",
1045+
Value: "true",
1046+
}},
1047+
}},
1048+
},
1049+
},
10111050
}
10121051

10131052
// Remove unused cache dirs before running tests.

provisionerd/proto/version.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,17 @@ import "github.com/coder/coder/v2/apiversion"
1212
//
1313
// API v1.4:
1414
// - Add new field named `devcontainers` in the Agent.
15+
//
16+
// API v1.5:
17+
// - Add new field named `prebuilt_workspace_build_stage` enum in the Metadata message.
1518
const (
1619
CurrentMajor = 1
17-
CurrentMinor = 4
20+
CurrentMinor = 5
1821
)
1922

2023
// CurrentVersion is the current provisionerd API version.
2124
// Breaking changes to the provisionerd API **MUST** increment
2225
// CurrentMajor above.
26+
// Non-breaking changes to the provisionerd API **MUST** increment
27+
// CurrentMinor above.
2328
var CurrentVersion = apiversion.New(CurrentMajor, CurrentMinor)

provisionerd/provisionerd.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ func (p *Server) acquireAndRunOne(client proto.DRPCProvisionerDaemonClient) erro
378378
slog.F("workspace_build_id", build.WorkspaceBuildId),
379379
slog.F("workspace_id", build.Metadata.WorkspaceId),
380380
slog.F("workspace_name", build.WorkspaceName),
381-
slog.F("is_prebuild", build.Metadata.IsPrebuild),
381+
slog.F("prebuilt_workspace_build_stage", build.Metadata.GetPrebuiltWorkspaceBuildStage().String()),
382382
)
383383

384384
span.SetAttributes(
@@ -388,7 +388,7 @@ func (p *Server) acquireAndRunOne(client proto.DRPCProvisionerDaemonClient) erro
388388
attribute.String("workspace_owner_id", build.Metadata.WorkspaceOwnerId),
389389
attribute.String("workspace_owner", build.Metadata.WorkspaceOwner),
390390
attribute.String("workspace_transition", build.Metadata.WorkspaceTransition.String()),
391-
attribute.Bool("is_prebuild", build.Metadata.IsPrebuild),
391+
attribute.String("prebuilt_workspace_build_stage", build.Metadata.GetPrebuiltWorkspaceBuildStage().String()),
392392
)
393393
}
394394

provisionersdk/proto/prebuilt_workspace.go

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)