feat(deployer): configurable per-workload deploy strategy (blue-green for built sources)
Add a deploy_strategy field to each source's config blob — "" (default), "recreate", or "blue-green" — validated in each source's Validate and read on the deploy path. No new DB column, no migration: the field rides inside the existing SourceConfig JSON and every existing workload decodes "" to its historical behavior (image -> blue-green, others -> recreate). The real gap this closes: dockerfile and static stopped the old container before creating the new one on every redeploy — a downtime window image never had. Their blue-green branch now: - names the new "green" container with a unique suffix so it coexists with the still-serving blue (plumbed into both the container name AND the proxy forwardHost); - skips the collision teardown that destroyed blue early; - gates green — an HTTP readiness probe (deps.Health.Check) when a healthcheck is configured, else the existing liveness window; - swaps the route via a pure upsert (no pre-DeleteRoute) so NPM repoints in place with no gap; - persists green into the single runtime-state row BEFORE reaping blue, so a crash mid-swap can never orphan green or leave the row pointing at a removed container (state.go/teardown.go/reconcile.go stay untouched). image honors explicit "recreate" (reap existing containers after pull, before cutover); its default blue-green path is unchanged. compose stays stack-managed and rejects "blue-green" at Validate so the contract is honest. static forces recreate for storage-backed deno sites — blue-green would mount the same RW volume into both containers at once. Shared helper internal/workload/plugin/strategy.go (ValidateStrategy + BuildGreenName). Backend-only (phase 1); the field is usable today via the app's advanced-JSON editor — a friendly toggle + i18n follow in phase 2. Tests: ValidateStrategy matrix, per-source Validate (incl. the empty-key backward-compat lock), and effectiveStrategy defaults + the deno gate. Design + adversarial review: docs/plans/DEPLOY_STRATEGY_PLAN.md.
This commit is contained in:
@@ -64,6 +64,23 @@ type Config struct {
|
||||
// git provider as a commit status (pending/success/failure) on the
|
||||
// built SHA. Best-effort — a reporting failure never fails a deploy.
|
||||
ReportCommitStatus bool `json:"report_commit_status"`
|
||||
|
||||
// DeployStrategy selects how a redeploy cuts over. "" (default) and
|
||||
// "recreate" stop the old container before starting the new one (a brief
|
||||
// downtime window). "blue-green" starts the new build alongside the old,
|
||||
// gates it, swaps the proxy route in place, then reaps the old —
|
||||
// zero-downtime under NPM. Validated via plugin.ValidateStrategy.
|
||||
DeployStrategy string `json:"deploy_strategy,omitempty"`
|
||||
}
|
||||
|
||||
// effectiveStrategy resolves the configured strategy for the dockerfile
|
||||
// source. Empty maps to recreate — the source's historical behavior — so
|
||||
// existing workloads are unchanged.
|
||||
func effectiveStrategy(cfg Config) string {
|
||||
if cfg.DeployStrategy == "" {
|
||||
return plugin.StrategyRecreate
|
||||
}
|
||||
return cfg.DeployStrategy
|
||||
}
|
||||
|
||||
type source struct{}
|
||||
@@ -120,6 +137,9 @@ func (*source) Validate(cfg json.RawMessage) error {
|
||||
return fmt.Errorf("dockerfile source: %q must not contain '..'", p)
|
||||
}
|
||||
}
|
||||
if err := plugin.ValidateStrategy(c.DeployStrategy, true); err != nil {
|
||||
return fmt.Errorf("dockerfile source: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user