Documentation
¶
Overview ¶
Package release orchestrates the gate-check → compose-up → record pipeline.
Index ¶
- Constants
- func AllPassed(outcomes []GateOutcome) bool
- func DefaultSnapshotDir() string
- func PolicyFor(step FailedStep) bool
- func RunSmoke(ctx context.Context, cfg config.SmokeConfig, targetEnv string, ...) error
- func WriteOverrideYAML(path string, images map[string]string) error
- type AggregateGateChecker
- type BrokerClient
- type ComposeDeployer
- type DeployBroker
- type DeployDeps
- type DeployReport
- type FailedStep
- type GateChecker
- type GateOutcome
- type Plan
- type RollbackDeps
- type RollbackHint
- type RollbackMode
- type RollbackPlan
- type RollbackReport
- type ServiceDelta
- type Snapshot
- type UI
- type VerifyDeps
- type VerifyReport
Constants ¶
const ParallelismDefault = 4
ParallelismDefault controls the service fan-out for gate checks.
Variables ¶
This section is empty.
Functions ¶
func AllPassed ¶
func AllPassed(outcomes []GateOutcome) bool
AllPassed reports whether every outcome was both error-free and deployable.
func DefaultSnapshotDir ¶
func DefaultSnapshotDir() string
ComposeFilesHelper makes "snapshot dir with env name in it" conveniently.
func PolicyFor ¶ added in v0.4.0
func PolicyFor(step FailedStep) bool
PolicyFor reports whether auto-rollback should run for a given failed step.
- StepGate: no state changed upstream, broker view untouched → skip.
- StepComposeUp / StepSmoke: possibly partial Compose-side state → rollback.
- StepRecord: compose & smoke both succeeded; only broker bookkeeping is inconsistent. Auto-rolling back here would revert a running healthy deployment — operator-only territory. See ADR-0006.
Types ¶
type AggregateGateChecker ¶ added in v0.4.6
type AggregateGateChecker interface {
GateChecker
CanIDeployMany(ctx context.Context, env string, selectors []broker.CanIDeploySelector) (*broker.CanIDeploySetResult, error)
}
AggregateGateChecker extends GateChecker with the multi-selector matrix query used for all_or_nothing environments. Pipelines may type-assert on this to decide whether to fan out or batch into one request. Declared as a separate interface so legacy test fakes keep compiling.
type BrokerClient ¶
type BrokerClient = GateChecker
BrokerClient is kept as an alias of GateChecker for backwards compatibility with existing tests; prefer GateChecker in new code.
type ComposeDeployer ¶
type ComposeDeployer interface {
Pull(ctx context.Context, services []string, progress io.Writer) error
Up(ctx context.Context, opts composeadapter.UpOptions, progress io.Writer) error
PsJSON(ctx context.Context) ([]composeadapter.ContainerStatus, error)
RenderConfigJSON(ctx context.Context) (*composeadapter.RenderedConfig, error)
}
ComposeDeployer is the subset of the Compose adapter used during deploy.
RenderConfigJSON is required so pre-deploy snapshots can capture the resolved {service → image} map, which the rollback flow uses to pin services back to their previous images.
type DeployBroker ¶
type DeployBroker interface {
AggregateGateChecker
RecordDeployment(ctx context.Context, in broker.RecordDeploymentInput) error
HasRelation(rel string) bool
APICallCount() int
}
DeployBroker extends GateChecker with what the deploy pipeline needs.
type DeployDeps ¶
type DeployDeps struct {
Broker DeployBroker
Compose ComposeDeployer
Strategy versioning.Strategy
Logger *slog.Logger
UI UI
Progress io.Writer
Stderr io.Writer
SnapshotDir string
// RollbackMode controls auto-rollback on compose-up / smoke failures.
// Zero value defaults to RollbackOn (opt-out). See ADR-0006.
RollbackMode RollbackMode
// RollbackTimeout bounds the rollback's own `compose up --wait`. Defaults
// to 2× Deploy.WaitTimeout, or 3 minutes if neither is set.
RollbackTimeout time.Duration
// ForceRecreate passes `--force-recreate` through to compose up. Debug
// escape hatch for the fresh-build-same-tag case. See ADR 0011.
ForceRecreate bool
}
DeployDeps collects the collaborators Deploy needs. The CLI layer builds these from config; tests can substitute fakes.
type DeployReport ¶
type DeployReport struct {
Plan *Plan
Outcomes []GateOutcome
Pre *Snapshot
Post *Snapshot
PreSnapshotFile string
PostSnapshotFile string
FailedAtStep FailedStep
FailedCause error
Rollback *RollbackReport
StartedAt time.Time
FinishedAt time.Time
}
DeployReport captures what happened, successful or not. On failure the caller uses FailedAtStep to decide the exit code and whether to emit a rollback hint.
func Deploy ¶
func Deploy(ctx context.Context, cfg *config.Config, envName, onlyService string, dryRun bool, deps DeployDeps) (*DeployReport, error)
Deploy runs the full lock-held pipeline. The caller MUST hold an environment lock for the duration of this call. See internal/lock.
Ordering is strict:
(1) pre-snapshot (2) gate check (3) compose up (4) smoke (optional) (5) record-deployment ← always last (6) post-snapshot
record-deployment is never called before step 5. A failure in (1)-(4) leaves the broker's view unchanged, which is what ADR 0004 requires.
type FailedStep ¶
type FailedStep string
FailedStep is the broad category of what went wrong during deploy.
const ( StepGate FailedStep = "gate" StepComposeUp FailedStep = "compose-up" StepSmoke FailedStep = "smoke" StepRecord FailedStep = "record-deployment" )
type GateChecker ¶
type GateChecker interface {
CanIDeploy(ctx context.Context, in broker.CanIDeployInput) (*broker.CanIDeployResult, error)
}
GateChecker is what gate-check logic needs from a Pact Broker client. Defined here (not in broker/) so release/ depends only on behaviour, per ISP and the "accept interfaces" idiom.
type GateOutcome ¶
type GateOutcome struct {
Service string
Pacticipant string
Release versioning.Release
Deployable bool
Reason string
BrokerURL string
VerifyURL string
Err error
}
GateOutcome is the per-service result of a can-i-deploy check.
func CheckService ¶
func CheckService(ctx context.Context, client GateChecker, svc, pacticipant, envName string, rel versioning.Release) GateOutcome
CheckService runs one gate check.
func GateAll ¶
func GateAll(ctx context.Context, client GateChecker, plan *Plan) []GateOutcome
GateAll runs gate checks for every service in plan.
Two shapes are supported:
- Per-service fan-out (default): each service is checked independently against the broker. This is correct when deployments are independent and matches the behaviour of the legacy can-i-deploy endpoint.
- Aggregate matrix query (all_or_nothing, 2+ services): the candidate set is sent in a single matrix request so the broker evaluates the services together. This is the fix for monolithic rollouts where every candidate is deployed with the same version: per-service queries falsely assume "everything else stays on current prod" and gate the rollout on verifications that are already known to pass within the candidate set.
Single-service plans always use the per-service path even with AllOrNothing=true, because the aggregate has no advantage for one selector and it keeps `--service foo` usable for probing.
The outcome slice is returned in plan.Services order regardless of completion order so output is deterministic.
type Plan ¶
type Plan struct {
Env string
Services []string // order matters (output stability)
Releases map[string]versioning.Release // service -> release
Mapping map[string]config.ServiceMapping // service -> pacticipant mapping
// AllOrNothing mirrors the environment's all_or_nothing setting. When
// true, GateAll routes the check through a single matrix query so the
// broker evaluates the candidate set together. False keeps the
// per-service fan-out.
AllOrNothing bool
}
Plan is the resolved versions for every service being gated.
type RollbackDeps ¶ added in v0.4.0
type RollbackDeps struct {
Compose ComposeDeployer
Logger *slog.Logger
UI UI
Progress io.Writer
SnapshotDir string // root under which snapshots & rollback artefacts are written
WaitTimeout time.Duration
}
RollbackDeps bundles what ExecuteRollback needs. Mirrors DeployDeps in spirit: the CLI constructs concrete implementations; tests supply fakes.
type RollbackHint ¶
type RollbackHint struct {
Env string
FailedAt FailedStep
Cause error
PreSnapshot *Snapshot
PreSnapshotFile string
PostSnapshot *Snapshot
Rollback *RollbackReport
}
func (RollbackHint) Write ¶
func (h RollbackHint) Write(w io.Writer)
Write emits the human-readable hint to w.
type RollbackMode ¶ added in v0.4.0
type RollbackMode string
RollbackMode controls whether the deploy pipeline auto-executes a rollback on compose-up or smoke failure.
const ( // RollbackOn is the default: on applicable failures, restore the // pre-deploy images via an override file and re-run `compose up`. RollbackOn RollbackMode = "on" // RollbackOff preserves the pre-v0.4 behaviour: hint-only, operator // drives the recovery by hand. RollbackOff RollbackMode = "off" // RollbackDryRun prints the plan to stderr but never invokes the // compose adapter. RollbackDryRun RollbackMode = "dry-run" )
func ParseRollbackMode ¶ added in v0.4.0
func ParseRollbackMode(s string) (RollbackMode, error)
ParseRollbackMode accepts the string form used by the CLI flag and returns the canonical value, or an error for anything unrecognised.
func ResolveRollbackMode ¶ added in v0.4.0
func ResolveRollbackMode(m RollbackMode) RollbackMode
ResolveRollbackMode normalises the zero value to RollbackOn so callers that omit the field get the documented default behaviour.
type RollbackPlan ¶ added in v0.4.0
type RollbackPlan struct {
Env string `json:"env"`
FromSnapshotFile string `json:"from_snapshot_file,omitempty"`
Images map[string]string `json:"images"` // service → previous image ref
Services []string `json:"services"` // sorted; stable output
}
RollbackPlan is the concrete action the executor will take: services to re-apply and the image reference each must land on.
func BuildRollbackPlan ¶ added in v0.4.0
BuildRollbackPlan compares pre-snapshot images against the currently-rendered compose config. Returns (plan, true) when rollback is meaningful, or (nil, false, reason) when it should be skipped.
current may be nil when the caller could not render the current config; in that case we fall back to "restore every service recorded in pre".
type RollbackReport ¶ added in v0.4.0
type RollbackReport struct {
Mode RollbackMode `json:"mode"`
Attempted bool `json:"attempted"`
Succeeded bool `json:"succeeded"`
Skipped bool `json:"skipped,omitempty"`
SkipReason string `json:"skip_reason,omitempty"`
Plan *RollbackPlan `json:"plan,omitempty"`
OverrideFile string `json:"override_file,omitempty"`
PostSnapshotFile string `json:"post_snapshot_file,omitempty"`
StartedAt time.Time `json:"started_at"`
FinishedAt time.Time `json:"finished_at"`
Duration time.Duration `json:"duration"`
Err string `json:"error,omitempty"`
ReportFile string `json:"report_file,omitempty"`
}
RollbackReport captures the outcome. Written to .c2quay/rollbacks/<ts>.json and embedded in RollbackHint for operator-visible output.
func ExecuteRollback ¶ added in v0.4.0
func ExecuteRollback(ctx context.Context, deps RollbackDeps, plan *RollbackPlan, mode RollbackMode) (*RollbackReport, error)
ExecuteRollback drives the compensating action for a failed deploy.
Flow:
- Write an override YAML pinning each affected service to its previous image.
- Run `docker compose up -d --wait` with the override layered on top of the project's base files. We reuse the same adapter code path as deploy, so the docker/compose#10596 --wait workaround still applies.
- Capture a post-rollback snapshot for the audit trail.
- Serialise the RollbackReport next to the snapshots.
Non-fatal errors (post-snapshot capture, report write) are logged and flagged in the report but do not propagate: rollback's job is to restore the compose plane, not to guarantee perfect audit.
type ServiceDelta ¶
type ServiceDelta struct {
Service string
Before versioning.Release
After versioning.Release
Changed bool
}
ServiceDelta describes how a single service's release changed between two snapshots.
func Diff ¶
func Diff(pre, post *Snapshot) []ServiceDelta
Diff compares two snapshots by release identity.
type Snapshot ¶
type Snapshot struct {
CapturedAt time.Time `json:"captured_at"`
Env string `json:"env"`
Releases map[string]versioning.Release `json:"releases,omitempty"`
Containers []composeadapter.ContainerStatus `json:"containers"`
// Images maps service name → resolved image reference at capture time.
// Populated from `docker compose config --format json`. May be empty if
// the render call fails; callers treat empty Images as "rollback not
// possible from this snapshot".
Images map[string]string `json:"images,omitempty"`
}
Snapshot captures the state of a compose project at a point in time. Written before and after a deploy so we can diff, generate rollback hints, and execute auto-rollback when something fails.
func CaptureSnapshot ¶
func LoadSnapshot ¶ added in v0.4.0
LoadSnapshot reads a snapshot JSON file from disk. Used by the standalone `c2quay rollback --from-snapshot` command.
type UI ¶
type UI interface {
Step(label, detail string)
Ok(label, detail string)
Fail(label, detail string)
Warn(label, detail string)
}
UI is the progressive output surface used by orchestrators. output.Writer satisfies it, but release/ does not import output/ directly so tests can supply a trivial fake.
type VerifyDeps ¶
type VerifyDeps struct {
Broker GateChecker
Strategy versioning.Strategy
}
VerifyDeps bundles everything the verify orchestrator needs. The caller (CLI layer) constructs the concrete implementations and passes them in.
type VerifyReport ¶
type VerifyReport struct {
Plan *Plan
Outcomes []GateOutcome
}
VerifyReport is the output of a verify run. The CLI formats it as text or JSON.
func Verify ¶
func Verify(ctx context.Context, cfg *config.Config, envName, onlyService string, deps VerifyDeps) (*VerifyReport, error)
Verify resolves the plan and asks the broker about every service. It does not print anything or return non-nil error on gate failure: the caller inspects the report. Operator-level errors (plan build, broker unreachable) do return err.
func (*VerifyReport) AllPassed ¶
func (r *VerifyReport) AllPassed() bool
AllPassed is true when every outcome is deployable and error-free.
func (*VerifyReport) FirstError ¶
func (r *VerifyReport) FirstError() error
FirstError returns the first non-nil outcome error, or nil.