deploy

package
v0.12.14-0...-1e9b4d5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 20, 2026 License: AGPL-3.0 Imports: 44 Imported by: 0

Documentation

Overview

Package deploy orchestrates the deployment lifecycle for user applications.

Deployments move through a multi-step pipeline that builds container images, provisions infrastructure across regions, waits for health, and configures domain routing — all durably, so a crash at any point resumes from the last completed step rather than restarting from scratch.

Why Restate Workflows

Every handler in this package runs as a Restate Workflow (restate.dev), keyed by a caller-supplied workflow ID. Restate provides automatic retries, durable execution across process restarts, and exactly-once semantics per step. Each step is wrapped in restate.Run (or RunAsync for parallel work), making the entire pipeline resumable. If the process dies mid-deploy, Restate picks up from the last committed step without re-executing earlier side effects.

Operations

Workflow.Deploy is the primary entrypoint. It validates the deployment record, loads workspace/project/environment context, then either builds a Docker image from a Git repository via Depot or accepts a pre-built image. It creates deployment topologies for every configured region (each with its own version from VersioningService), ensures sentinel containers and Cilium network policies exist per region, and polls in parallel until all instances are running. Once healthy, it generates frontline routes for per-commit, per-branch, and per-environment domains, reassigns sticky routes through RoutingService, marks the deployment ready, and — for non-rolled-back production environments — updates the app's live deployment pointer. The previous live deployment is scheduled for standby after 30 minutes via DeploymentService.ScheduleDesiredStateChange.

Workflow.Rollback switches sticky frontline routes (environment and live) from the current live deployment to a previous one, atomically through RoutingService. Because the live pointer now points to an older deployment, subsequent deploys detect the rolled-back state and skip auto-promotion.

Workflow.Promote reassigns sticky routes to a new target deployment and updates the live pointer, restoring normal auto-promote behavior for future deploys.

Workflow.ScaleDownIdlePreviewDeployments paginates through preview environments and sets any deployment to archived that has been idle (zero requests in ClickHouse) for longer than six hours.

Image Builds

When a deploy request carries a Git source, the workflow builds a container image remotely through Depot. It retrieves or creates a Depot project per Unkey project, acquires a BuildKit machine, fetches the repository via a GitHub installation token, and streams build-step telemetry to ClickHouse.

Domain Generation

[buildDomains] produces four or five domain patterns per deployment:

  • Per-commit: <project>-<app>-git-<sha>-<workspace>.<apex> (non-sticky, immutable)
  • Per-branch: <project>-<app>-git-<branch>-<workspace>.<apex> (sticky to branch)
  • Per-environment: <project>-<app>-<env>-<workspace>.<apex> (sticky to environment)
  • Per-live (production only): <project>-<app>-<workspace>.<apex> (sticky to live)
  • Per-deployment: <project>-<app>-dep-<id>-<workspace>.<apex> (sticky to deployment)

Sticky domains automatically follow the latest deployment matching their criteria; commit domains never move. CLI uploads add a random numeric suffix to the commit domain to avoid collisions from repeated pushes of the same SHA. Live-traffic routing is handled separately by sticky route reassignment in RoutingService, not by a dedicated "live" domain type.

Network Policy

[Workflow.ensureCiliumNetworkPolicy] persists Cilium network policies in the database for each region that lacks one. Each policy allows ingress from the sentinel namespace to deployment pods on port 8080 and is applied by regional reconcilers.

Error Handling

Deploy defers a cleanup handler that marks the deployment as failed if the workflow does not finish successfully. Terminal errors (invalid input, not found) are returned with appropriate HTTP status codes so Restate does not retry them; transient failures are returned as regular errors for automatic retry.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BuildPlatform

type BuildPlatform struct {
	Platform     string
	Architecture string
}

BuildPlatform specifies the target platform for container builds.

type Compensation

type Compensation struct {
	// contains filtered or unexported fields
}

Compensation stores deployment rollback actions.

Actions are registered in forward order with Compensation.Add and executed in reverse order by Compensation.Execute, so teardown naturally unwinds the setup sequence that succeeded before a failure.

func NewCompensation

func NewCompensation() *Compensation

NewCompensation creates an empty Compensation.

func (*Compensation) Add

func (c *Compensation) Add(name string, run func(ctx restate.RunContext) error)

Add registers a compensation step.

Add wraps run in restate.RunVoid and stores it for later execution during Compensation.Execute. The step name is used as Restate run metadata.

func (*Compensation) Execute

func (c *Compensation) Execute(ctx restate.ObjectContext) error

Execute runs all registered compensations in reverse registration order.

Execute returns nil when every step succeeds. When multiple steps fail, it joins all failures with errors.Join. Execute does not clear registered steps, so calling it more than once re-runs the same compensations.

type Config

type Config struct {
	// DB is the main database connection for workspace, project, and deployment data.
	DB db.Database

	// DefaultDomain is the apex domain for generated deployment URLs (e.g., "unkey.app").
	DefaultDomain string

	// Vault provides encryption/decryption services for secrets.
	Vault vault.VaultServiceClient

	// SentinelImage is the Docker image used for sentinel containers.
	SentinelImage string

	// GitHub provides access to GitHub API for downloading tarballs.
	GitHub githubclient.GitHubClient

	// DepotConfig configures the Depot API connection.
	DepotConfig DepotConfig

	// RegistryConfig provides credentials for the container registry.
	RegistryConfig RegistryConfig

	// BuildPlatform specifies the target platform for all builds.
	BuildPlatform BuildPlatform

	// Clickhouse receives build step telemetry for observability.
	Clickhouse clickhouse.ClickHouse

	// AllowUnauthenticatedDeployments controls whether builds can skip GitHub authentication.
	// Set to true only for local development with public repositories.
	AllowUnauthenticatedDeployments bool

	// DashboardURL is the base URL of the dashboard for constructing log URLs
	// in GitHub deployment statuses (e.g., "https://app.unkey.com").
	DashboardURL string
}

Config holds the configuration for creating a deployment workflow.

type DepotConfig

type DepotConfig struct {
	APIUrl        string
	ProjectRegion string
}

DepotConfig holds configuration for connecting to the Depot.dev API.

type RegistryConfig

type RegistryConfig struct {
	URL      string
	Username string
	Password string
}

RegistryConfig holds credentials for the container registry.

type Workflow

type Workflow struct {
	hydrav1.UnimplementedDeployServiceServer
	// contains filtered or unexported fields
}

Workflow orchestrates deployment lifecycle operations.

This workflow manages the complete deployment lifecycle including deploying new versions, rolling back to previous versions, and promoting deployments to live. It coordinates between container orchestration (Krane), database updates, domain routing, and sentinel configuration to ensure consistent deployment state.

The workflow uses Restate virtual objects keyed by app ID to ensure that only one deployment operation runs per app at any time, preventing race conditions during concurrent deploy/rollback/promote operations while allowing parallel deploys across different apps within the same project.

func New

func New(cfg Config) *Workflow

New creates a new deployment workflow instance.

func (*Workflow) Deploy

func (w *Workflow) Deploy(ctx restate.ObjectContext, req *hydrav1.DeployRequest) (_ *hydrav1.DeployResponse, retErr error)

Deploy executes a full deployment workflow for a new application version.

This is a Restate durable workflow, meaning it is idempotent and can safely resume from any step after a crash. The workflow orchestrates five phases:

  1. [Workflow.buildImage] — resolve or build the container image
  2. [Workflow.createTopologies] — provision deployment topologies across regions
  3. [Workflow.ensureSentinels] and [Workflow.ensureCiliumNetworkPolicy] — set up routing infrastructure and network policies
  4. [Workflow.configureRouting] — assign domain routes to the deployment
  5. [Workflow.swapLiveDeployment] — promote to live (production only)

Each phase is wrapped in deployment step tracking so the UI can show progress. A compensation stack (executed in reverse on failure) cleans up partial state such as inserted topologies and updates the deployment status to failed.

Returns terminal errors for validation failures and retryable errors for transient system failures.

func (*Workflow) DeploymentStep

func (w *Workflow) DeploymentStep(
	ctx restate.ObjectContext,
	step db.DeploymentStepsStep,
	deployment db.Deployment,
	fn func(innerCtx restate.ObjectContext) error,
) error

func (*Workflow) Promote

Promote reassigns all sticky domains to a deployment and clears the rolled back state.

This durable workflow supports two modes:

1. Normal promotion: moves sticky domains (environment and live) from the current live deployment to a new target deployment.

2. Confirm rollback: when the app is rolled back and the target is already the current deployment, clears the rolled back flag without reassigning routes (they already point to the correct deployment). This allows future deployments to automatically take over sticky domains again. The deployment that was rolled back from is scheduled for standby after 30 minutes.

The workflow validates that the target deployment is ready and the app has a live deployment. For normal promotion, it also validates that the target is not already the live deployment and that there are sticky domains to promote.

Returns terminal errors (400/404) for validation failures and retryable errors for system failures.

func (*Workflow) Rollback

Rollback performs a rollback to a previous deployment.

This durable workflow switches sticky frontline routes (environment and live) from the current live deployment back to a previous deployment. The operation is performed atomically through the routing service to prevent partial updates that could leave the system in an inconsistent state.

The workflow validates that source and target are different deployments, that the source deployment is the current live deployment, that both deployments belong to the same app and environment, and that there are sticky frontline routes to rollback.

Before switching routes, any pending scheduled state changes on the target deployment are cleared so it won't be spun down while serving live traffic. After switching routes, the app is marked as rolled back to prevent new deployments from automatically taking over the live routes.

Returns terminal errors (400/404) for validation failures and retryable errors for system failures.

func (*Workflow) ScaleDownIdlePreviewDeployments

ScaleDownIdlePreviewDeployments reclaims resources from preview deployments that have received no traffic within the idle window defined by idleTime. Preview environments can accumulate many running deployments from feature branches that are no longer actively used, so this workflow paginates through all preview environments and transitions idle deployments to archived by checking request counts in ClickHouse.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL