mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
Closes https://github.com/coder/internal/issues/369 We can't know whether a replacement (i.e. drift of terraform state leading to a resource needing to be deleted/recreated) will take place apriori; we can only detect it at `plan` time, because the provider decides whether a resource must be replaced and it cannot be inferred through static analysis of the template. **This is likely to be the most common gotcha with using prebuilds, since it requires a slight template modification to use prebuilds effectively**, so let's head this off before it's an issue for customers. Drift details will now be logged in the workspace build logs:  Plus a notification will be sent to template admins when this situation arises:  A new metric - `coderd_prebuilt_workspaces_resource_replacements_total` - will also increment each time a workspace encounters replacements. We only track _that_ a resource replacement occurred, not how many. Just one is enough to ruin a prebuild, but we can't know apriori which replacement would cause this. For example, say we have 2 replacements: a `docker_container` and a `null_resource`; we don't know which one might cause an issue (or indeed if either would), so we just track the replacement. --------- Signed-off-by: Danny Kopping <dannykopping@gmail.com>
60 lines
2.5 KiB
Go
60 lines
2.5 KiB
Go
package prebuilds
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/google/uuid"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/coder/coder/v2/coderd/database"
|
|
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
|
|
)
|
|
|
|
var (
|
|
ErrNoClaimablePrebuiltWorkspaces = xerrors.New("no claimable prebuilt workspaces found")
|
|
ErrAGPLDoesNotSupportPrebuiltWorkspaces = xerrors.New("prebuilt workspaces functionality is not supported under the AGPL license")
|
|
)
|
|
|
|
// ReconciliationOrchestrator manages the lifecycle of prebuild reconciliation.
|
|
// It runs a continuous loop to check and reconcile prebuild states, and can be stopped gracefully.
|
|
type ReconciliationOrchestrator interface {
|
|
Reconciler
|
|
|
|
// Run starts a continuous reconciliation loop that periodically calls ReconcileAll
|
|
// to ensure all prebuilds are in their desired states. The loop runs until the context
|
|
// is canceled or Stop is called.
|
|
Run(ctx context.Context)
|
|
|
|
// Stop gracefully shuts down the orchestrator with the given cause.
|
|
// The cause is used for logging and error reporting.
|
|
Stop(ctx context.Context, cause error)
|
|
|
|
// TrackResourceReplacement handles a pathological situation whereby a terraform resource is replaced due to drift,
|
|
// which can obviate the whole point of pre-provisioning a prebuilt workspace.
|
|
// See more detail at https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces#preventing-resource-replacement.
|
|
TrackResourceReplacement(ctx context.Context, workspaceID, buildID uuid.UUID, replacements []*sdkproto.ResourceReplacement)
|
|
}
|
|
|
|
type Reconciler interface {
|
|
StateSnapshotter
|
|
|
|
// ReconcileAll orchestrates the reconciliation of all prebuilds across all templates.
|
|
// It takes a global snapshot of the system state and then reconciles each preset
|
|
// in parallel, creating or deleting prebuilds as needed to reach their desired states.
|
|
ReconcileAll(ctx context.Context) error
|
|
}
|
|
|
|
// StateSnapshotter defines the operations necessary to capture workspace prebuilds state.
|
|
type StateSnapshotter interface {
|
|
// SnapshotState captures the current state of all prebuilds across templates.
|
|
// It creates a global database snapshot that can be viewed as a collection of PresetSnapshots,
|
|
// each representing the state of prebuilds for a specific preset.
|
|
// MUST be called inside a repeatable-read transaction.
|
|
SnapshotState(ctx context.Context, store database.Store) (*GlobalSnapshot, error)
|
|
}
|
|
|
|
type Claimer interface {
|
|
Claim(ctx context.Context, userID uuid.UUID, name string, presetID uuid.UUID) (*uuid.UUID, error)
|
|
Initiator() uuid.UUID
|
|
}
|