feat: add is_prebuild_claim to distinguish post-claim provisioning (#17757)

Used in combination with
https://github.com/coder/terraform-provider-coder/pull/396

This is required by both https://github.com/coder/coder/pull/17475 and
https://github.com/coder/coder/pull/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>
This commit is contained in:
Danny Kopping
2025-05-12 14:19:03 +00:00
committed by GitHub
parent 799a0ba573
commit af2941bb92
15 changed files with 478 additions and 339 deletions
@@ -645,7 +645,7 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
WorkspaceBuildId: workspaceBuild.ID.String(),
WorkspaceOwnerLoginType: string(owner.LoginType),
WorkspaceOwnerRbacRoles: ownerRbacRoles,
IsPrebuild: input.IsPrebuild,
PrebuiltWorkspaceBuildStage: input.PrebuiltWorkspaceBuildStage,
},
LogLevel: input.LogLevel,
},
@@ -2471,11 +2471,10 @@ type TemplateVersionImportJob struct {
// WorkspaceProvisionJob is the payload for the "workspace_provision" job type.
type WorkspaceProvisionJob struct {
WorkspaceBuildID uuid.UUID `json:"workspace_build_id"`
DryRun bool `json:"dry_run"`
IsPrebuild bool `json:"is_prebuild,omitempty"`
PrebuildClaimedByUser uuid.UUID `json:"prebuild_claimed_by,omitempty"`
LogLevel string `json:"log_level,omitempty"`
WorkspaceBuildID uuid.UUID `json:"workspace_build_id"`
DryRun bool `json:"dry_run"`
LogLevel string `json:"log_level,omitempty"`
PrebuiltWorkspaceBuildStage sdkproto.PrebuiltWorkspaceBuildStage `json:"prebuilt_workspace_stage,omitempty"`
}
// TemplateVersionDryRunJob is the payload for the "template_version_dry_run" job type.
@@ -23,10 +23,11 @@ import (
"storj.io/drpc"
"cdr.dev/slog/sloggers/slogtest"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/quartz"
"github.com/coder/serpent"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/buildinfo"
"github.com/coder/coder/v2/coderd/audit"
"github.com/coder/coder/v2/coderd/database"
@@ -299,6 +300,10 @@ func TestAcquireJob(t *testing.T) {
Transition: database.WorkspaceTransitionStart,
Reason: database.BuildReasonInitiator,
})
var buildState sdkproto.PrebuiltWorkspaceBuildStage
if prebuiltWorkspace {
buildState = sdkproto.PrebuiltWorkspaceBuildStage_CREATE
}
_ = dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
ID: build.ID,
OrganizationID: pd.OrganizationID,
@@ -308,8 +313,8 @@ func TestAcquireJob(t *testing.T) {
FileID: file.ID,
Type: database.ProvisionerJobTypeWorkspaceBuild,
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
WorkspaceBuildID: build.ID,
IsPrebuild: prebuiltWorkspace,
WorkspaceBuildID: build.ID,
PrebuiltWorkspaceBuildStage: buildState,
})),
})
@@ -380,7 +385,7 @@ func TestAcquireJob(t *testing.T) {
WorkspaceOwnerRbacRoles: []*sdkproto.Role{{Name: rbac.RoleOrgMember(), OrgId: pd.OrganizationID.String()}, {Name: "member", OrgId: ""}, {Name: rbac.RoleOrgAuditor(), OrgId: pd.OrganizationID.String()}},
}
if prebuiltWorkspace {
wantedMetadata.IsPrebuild = true
wantedMetadata.PrebuiltWorkspaceBuildStage = sdkproto.PrebuiltWorkspaceBuildStage_CREATE
}
slices.SortFunc(wantedMetadata.WorkspaceOwnerRbacRoles, func(a, b *sdkproto.Role) int {
+1 -1
View File
@@ -713,7 +713,7 @@ func createWorkspace(
builder = builder.TemplateVersionPresetID(req.TemplateVersionPresetID)
}
if claimedWorkspace != nil {
builder = builder.MarkPrebuildClaimedBy(owner.ID)
builder = builder.MarkPrebuiltWorkspaceClaim()
}
if req.EnableDynamicParameters && api.Experiments.Enabled(codersdk.ExperimentDynamicParameters) {
+10 -9
View File
@@ -16,6 +16,7 @@ import (
"github.com/coder/coder/v2/coderd/rbac/policy"
"github.com/coder/coder/v2/provisioner/terraform/tfparse"
"github.com/coder/coder/v2/provisionersdk"
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
"github.com/google/uuid"
"github.com/sqlc-dev/pqtype"
@@ -76,8 +77,7 @@ type Builder struct {
parameterValues *[]string
templateVersionPresetParameterValues []database.TemplateVersionPresetParameter
prebuild bool
prebuildClaimedBy uuid.UUID
prebuiltWorkspaceBuildStage sdkproto.PrebuiltWorkspaceBuildStage
verifyNoLegacyParametersOnce bool
}
@@ -174,15 +174,17 @@ func (b Builder) RichParameterValues(p []codersdk.WorkspaceBuildParameter) Build
return b
}
// MarkPrebuild indicates that a prebuilt workspace is being built.
func (b Builder) MarkPrebuild() Builder {
// nolint: revive
b.prebuild = true
b.prebuiltWorkspaceBuildStage = sdkproto.PrebuiltWorkspaceBuildStage_CREATE
return b
}
func (b Builder) MarkPrebuildClaimedBy(userID uuid.UUID) Builder {
// MarkPrebuiltWorkspaceClaim indicates that a prebuilt workspace is being claimed.
func (b Builder) MarkPrebuiltWorkspaceClaim() Builder {
// nolint: revive
b.prebuildClaimedBy = userID
b.prebuiltWorkspaceBuildStage = sdkproto.PrebuiltWorkspaceBuildStage_CLAIM
return b
}
@@ -322,10 +324,9 @@ func (b *Builder) buildTx(authFunc func(action policy.Action, object rbac.Object
workspaceBuildID := uuid.New()
input, err := json.Marshal(provisionerdserver.WorkspaceProvisionJob{
WorkspaceBuildID: workspaceBuildID,
LogLevel: b.logLevel,
IsPrebuild: b.prebuild,
PrebuildClaimedByUser: b.prebuildClaimedBy,
WorkspaceBuildID: workspaceBuildID,
LogLevel: b.logLevel,
PrebuiltWorkspaceBuildStage: b.prebuiltWorkspaceBuildStage,
})
if err != nil {
return nil, nil, nil, BuildError{