server

package
v0.44.1 Latest Latest
Warning

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

Go to latest
Published: Sep 23, 2026 License: Apache-2.0 Imports: 50 Imported by: 0

Documentation

Overview

Package server is the control-plane HTTP service: it records multi-stack Terraform executions, drives one GitHub check run per environment, and projects approval-gate state — all as a pure function of its SQLite store. Terraform itself runs in the user's own CI; this server observes and gates.

Shared view helpers: pure projections of stack state used by the check-run rendering (risk tags, op summaries, verdict tallies). The HTML live viewer that originally owned them retired in favor of the central UI.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Actor added in v0.21.0

func Actor(r *http.Request) string

Actor returns the authenticated caller of an /api/* request: the verified OIDC email, or "" when auth is disabled (no APIVerifier).

Types

type App

type App struct {

	// Approval is the optional approval-gate backend. nil disables gating
	// (gates recorded AWAITING are never satisfied — the check stays pending
	// with the awaiting-approval title). Set after
	// construction (e.g. by the serve command), so New's signature is unchanged.
	Approval approval.Backend
	// Objects is the optional object store for completed-log offload. nil keeps
	// logs in the local buffer only. Set after construction, like Approval.
	Objects ObjectStore
	// Executor is the optional CI backend serve drives when it triggers runs
	// itself (webhook → build). nil keeps serve reactive-only (runs start via
	// the CI system's own triggers, as before). Set after construction.
	Executor executor.Backend
	// PushVerifier verifies a Pub/Sub push OIDC bearer token, returning the
	// token's email claim. Set externally (like Approval/Objects); nil disables
	// the /pubsub/push endpoint (it returns 404).
	PushVerifier func(ctx context.Context, bearer string) (email string, err error)
	// APIVerifier verifies a Google-signed OIDC bearer token on /api/* routes,
	// returning the token's email claim (audience checking included). Set
	// externally, like PushVerifier; nil disables /api/* auth entirely
	// (local/dev). It is the only /api/* credential — the legacy shared-secret
	// HS256 path was removed once every caller migrated to OIDC.
	APIVerifier func(ctx context.Context, bearer string) (email string, err error)
	// contains filtered or unexported fields
}

App is the HTTP application.

func New

func New(db *sql.DB, gh GitHub, cfg Config) *App

New builds an App.

func (*App) ClaimsSweepLoop added in v0.15.0

func (a *App) ClaimsSweepLoop(ctx context.Context, interval time.Duration)

ClaimsSweepLoop periodically releases expired claims and re-evaluates held checks (mirrors OrphanSweepLoop).

func (*App) CleanLogBuffers added in v0.9.0

func (a *App) CleanLogBuffers(maxAge time.Duration)

CleanLogBuffers removes execution buffer directories older than maxAge — orphans left by restarts mid-execution. Call at startup.

func (*App) OrphanSweepLoop added in v0.10.0

func (a *App) OrphanSweepLoop(ctx context.Context, interval time.Duration)

OrphanSweepLoop periodically runs sweepOrphanedGrants until ctx is cancelled. Slower cadence than ReconcileLoop: it calls the GitHub API per open-grant PR and orphan cleanup is not time-critical. No-op without an approval backend or GitHub client.

func (*App) ReconcileLoop

func (a *App) ReconcileLoop(ctx context.Context, interval time.Duration)

ReconcileLoop periodically re-evaluates every not-yet-ACTIVE gate, so a grant that goes ACTIVE after the request (the common case) converges to success even with no provider event. No-op without a backend. Blocks until ctx is cancelled.

func (*App) Routes

func (a *App) Routes() http.Handler

Routes returns the HTTP handler. Uses stdlib method-pattern routing (Go 1.22+). Auth model:

  • /api/* /logs/* /plan/* require a Google-signed OIDC ID token (aud=serve URL); the central UI proxies them for browsers

The tier serves no longer serve HTML pages — the central UI (`tfstackplan ui`) is the human surface; the 30-day view-JWT machinery retired with it.

func (*App) RunWatchdogLoop added in v0.22.0

func (a *App) RunWatchdogLoop(ctx context.Context, interval, timeout time.Duration)

RunWatchdogLoop backstops serve-initiated runs: a run whose build never produces a reporting runner would otherwise hang its check forever. Every interval it probes executions that are still queued (no runner activity) past timeout and fails the ones whose build is gone, failed, or finished silent. No-op unless run triggering is armed.

type CheckRunUpdate

type CheckRunUpdate struct {
	Title      string // check-run title (e.g. "Terraform plan" / "Terraform apply")
	Summary    string // GFM blast-radius headline + verdict chips + per-stack table (renders at the top)
	Text       string // the rendered plan report (renders below the summary)
	DetailsURL string // the live page for the full view
	Conclusion string // "" while running; otherwise success|failure|action_required|neutral
}

CheckRunUpdate carries everything a PATCH to a check run needs.

type Config

type Config struct {
	// PublicBaseURL is the public origin of THIS serve, used to build check-run
	// Details links and as the OIDC audience for its own endpoints. Empty
	// derives it from the inbound request.
	PublicBaseURL string
	// UIBaseURL is the central UI service's external base URL. Check-run
	// details and approval links point there; empty leaves them unset. The
	// UI's configured tier names must match serve environments (its routes
	// are /t/<tier>/e/<id> and /pr/<n>).
	UIBaseURL string
	// Environment is this serve's tier (e.g. "nonprod"). Run triggering scopes
	// its ChangeSets to it; empty disables webhook-driven run triggering.
	Environment string
	// LogsDir is where per-stack log buffers are written. Empty disables log
	// ingestion (the endpoints become no-ops).
	LogsDir string
	// PushServiceAccount is the allowed verified OIDC email for /pubsub/push.
	// Empty accepts any verified token.
	PushServiceAccount string
	// APIPrincipals maps a verified OIDC caller email (lowercase) to the API
	// scopes it holds (see the scope* constants). Only consulted when
	// App.APIVerifier is set.
	APIPrincipals map[string][]string
	// GitHubWebhookSecret is the HMAC-SHA256 secret GitHub sends on every webhook
	// delivery (X-Hub-Signature-256). Empty disables the /github/webhook endpoint.
	GitHubWebhookSecret string
	// Progress is the optional per-operation weighted phase set (from the policy
	// file's progress{} block) driving the full-progress bar. nil → built-in
	// fractions.
	Progress *config.ProgressConfig
	// BuildTriggerNames maps a Cloud Build trigger NAME to the run kind
	// ("plan"/"apply") it drives. Populated from the executor block; lets the
	// /pubsub/cloud-builds ingest recognize builds from serve's own triggers and
	// derive their kind. Empty disables inbound build reconciliation.
	BuildTriggerNames map[string]string
	// RequesterPool is the set of service-account identities configured for the applier pool.
	RequesterPool []string
}

Config is the server runtime configuration.

type GitHub

type GitHub interface {
	// CreateCheckRun opens an in_progress check run with the given name whose
	// Details link is detailsURL; returns its id.
	CreateCheckRun(ctx context.Context, repo, sha, name, detailsURL string) (int64, error)
	// UpdateCheckRun patches an existing check run.
	UpdateCheckRun(ctx context.Context, repo string, checkRunID int64, u CheckRunUpdate) error
	// PostStatus sets a commit status (commit-status fallback).
	PostStatus(ctx context.Context, repo, sha, context_, state, description, targetURL string) error
	// PRHeadSHA returns the current head commit SHA of a pull request, so a
	// verdict is posted on the live head rather than a stale execution SHA.
	PRHeadSHA(ctx context.Context, repo string, pr int) (string, error)
	// PRAbandoned reports whether a pull request is closed WITHOUT having been
	// merged (i.e. abandoned). A merged PR returns false so its post-merge apply
	// keeps its grant. Returns (false, err) when the PR cannot be read.
	PRAbandoned(ctx context.Context, repo string, pr int) (bool, error)
	// MergeGroupPRs returns the PR numbers whose commits compose the merge group
	// at headSHA, via the commit→PRs association API.
	MergeGroupPRs(ctx context.Context, repo, headSHA string) ([]int, error)
	// ReRequestCheckRun re-requests a GitHub check-run.
	ReRequestCheckRun(ctx context.Context, repo string, checkRunID int64) error
	// MergeQueue returns the repository's merge-queue entries for the default
	// branch (GraphQL). A missing/invisible queue returns an empty result, not
	// an error, so the UI simply hides the hero.
	MergeQueue(ctx context.Context, repo string) (MergeQueueResult, error)
}

GitHub is the surface the server needs from GitHub. A rich check run requires a GitHub App with checks:write; the commit-status fallback (statuses:write) posts a status whose target URL points at the live page, used only until an apply's check run exists. Tests use MockGitHub.

type MergeQueueEntry added in v0.37.0

type MergeQueueEntry struct {
	Position int
	PR       int
	State    string // GitHub MergeQueueEntryState (QUEUED, MERGEABLE, AWAITING_CHECKS, LOCKED, UNMERGEABLE)
}

MergeQueueEntry is one PR queued on a branch's GitHub merge queue.

type MergeQueueResult added in v0.37.0

type MergeQueueResult struct {
	Branch  string
	Entries []MergeQueueEntry
}

MergeQueueResult is a repository's merge queue for its default branch.

type MockGitHub

type MockGitHub struct {
	CreateCheckRunFn    func(ctx context.Context, repo, sha, name, detailsURL string) (int64, error)
	UpdateCheckRunFn    func(ctx context.Context, repo string, checkRunID int64, u CheckRunUpdate) error
	PostStatusFn        func(ctx context.Context, repo, sha, context_, state, description, targetURL string) error
	PRHeadSHAFn         func(ctx context.Context, repo string, pr int) (string, error)
	PRAbandonedFn       func(ctx context.Context, repo string, pr int) (bool, error)
	MergeGroupPRsFn     func(ctx context.Context, repo, headSHA string) ([]int, error)
	ReRequestCheckRunFn func(ctx context.Context, repo string, checkRunID int64) error
	MergeQueueFn        func(ctx context.Context, repo string) (MergeQueueResult, error)
	// CreateCheckRunCalls counts CreateCheckRun invocations so tests can assert
	// the check run is created exactly once (idempotency).
	CreateCheckRunCalls int
}

MockGitHub is a test double for GitHub. Unset funcs are no-ops.

func (*MockGitHub) CreateCheckRun

func (m *MockGitHub) CreateCheckRun(ctx context.Context, repo, sha, name, detailsURL string) (int64, error)

func (*MockGitHub) MergeGroupPRs added in v0.15.0

func (m *MockGitHub) MergeGroupPRs(ctx context.Context, repo, headSHA string) ([]int, error)

func (*MockGitHub) MergeQueue added in v0.37.0

func (m *MockGitHub) MergeQueue(ctx context.Context, repo string) (MergeQueueResult, error)

func (*MockGitHub) PRAbandoned added in v0.10.0

func (m *MockGitHub) PRAbandoned(ctx context.Context, repo string, pr int) (bool, error)

func (*MockGitHub) PRHeadSHA

func (m *MockGitHub) PRHeadSHA(ctx context.Context, repo string, pr int) (string, error)

func (*MockGitHub) PostStatus

func (m *MockGitHub) PostStatus(ctx context.Context, repo, sha, context_, state, description, targetURL string) error

func (*MockGitHub) ReRequestCheckRun added in v0.18.2

func (m *MockGitHub) ReRequestCheckRun(ctx context.Context, repo string, checkRunID int64) error

ReRequestCheckRun re-requests a GitHub check-run.

func (*MockGitHub) UpdateCheckRun

func (m *MockGitHub) UpdateCheckRun(ctx context.Context, repo string, checkRunID int64, u CheckRunUpdate) error

type ObjectStore

type ObjectStore interface {
	Put(ctx context.Context, key string, r io.Reader) error
	Get(ctx context.Context, key string) (io.ReadCloser, error)
}

ObjectStore persists completed log objects (and, later, plan/verify outputs). A cloud impl (e.g. GCS) is wired by the serve command for deployment.

type PRMergeState added in v0.27.0

type PRMergeState struct {
	Environment     string
	RequiredCheck   string
	CheckConclusion string
	MergeBlocked    bool
	Blocker         string
}

PRMergeState is a read-only snapshot of a PR's merge-readiness on this serve's tier: the terraform/<env> required-check conclusion (GitHub's success/failure/action_required/"" vocabulary — "" means still in progress) plus the apply-lock verdict. Task 4 exposes this via GET /api/pr/{n}.

type RealClient

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

RealClient is the production GitHub implementation. It mints a fresh App installation token per request, so the token never goes stale on a long-running service. The App key + ids are supplied by the caller (a deployment concern); RealClient has no cloud-secret-store dependency.

func NewRealClient

func NewRealClient(appID, installationID string, pemKey []byte) (*RealClient, error)

NewRealClient parses the PEM App private key and returns a client for the given App id + installation id.

func (*RealClient) CreateCheckRun

func (c *RealClient) CreateCheckRun(ctx context.Context, repo, sha, name, detailsURL string) (int64, error)

CreateCheckRun opens an in_progress check run with the given name.

func (*RealClient) MergeGroupPRs added in v0.15.0

func (c *RealClient) MergeGroupPRs(ctx context.Context, repo, headSHA string) ([]int, error)

MergeGroupPRs returns the PR numbers whose commits compose the merge group at headSHA, via the commit→PRs association API.

func (*RealClient) MergeQueue added in v0.37.0

func (c *RealClient) MergeQueue(ctx context.Context, repo string) (MergeQueueResult, error)

MergeQueue queries the repository's merge queue for its default branch via the GraphQL API. A successful response with null repository or null mergeQueue (no queue configured or not visible to the installation token) degrades to an empty result within this method. Transport errors, token-mint failures, or non-2xx HTTP responses are returned to the caller (the /api/merge-queue HTTP handler degrades them to an empty queue and logs them). GraphQL-level errors are handled implicitly: GitHub returns HTTP 200 with null data.repository when the request has GraphQL errors, and the nil checks below handle this case — there is no explicit errors-array inspection.

func (*RealClient) PRAbandoned added in v0.10.0

func (c *RealClient) PRAbandoned(ctx context.Context, repo string, pr int) (bool, error)

PRAbandoned reports whether the PR is closed without having been merged.

func (*RealClient) PRHeadSHA

func (c *RealClient) PRHeadSHA(ctx context.Context, repo string, pr int) (string, error)

PRHeadSHA returns a pull request's current head commit SHA.

func (*RealClient) PostStatus

func (c *RealClient) PostStatus(ctx context.Context, repo, sha, context_, state, description, targetURL string) error

PostStatus sets a commit status (commit-status fallback; needs only statuses:write).

func (*RealClient) ReRequestCheckRun added in v0.18.2

func (c *RealClient) ReRequestCheckRun(ctx context.Context, repo string, checkRunID int64) error

ReRequestCheckRun triggers a check-run re-request.

func (*RealClient) UpdateCheckRun

func (c *RealClient) UpdateCheckRun(ctx context.Context, repo string, checkRunID int64, u CheckRunUpdate) error

UpdateCheckRun patches a check run. A non-empty Conclusion completes the run.

type Shell added in v0.9.9

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

Shell is the imperative shell around the pure reconcile core. It gathers a World by replaying the event stream, runs Decide to produce events, folds them via Evolve, persists (appends events + snapshot + projects), and executes the React actions — serialized per (pr, environment).

func NewShell added in v0.9.9

func NewShell(app *App) *Shell

NewShell builds a Shell bound to an App.

func (*Shell) Handle added in v0.9.9

func (sh *Shell) Handle(ctx context.Context, pr int, env, repo string, sig reconcile.Signal) error

Handle processes one signal for (pr, env) to a fixpoint: gather (replay) → Decide → persist (append+snapshot+project) → React → execute, repeating while actions yield feedback signals (RequestGrant → GrantsObserved, StartRun → RunStartResult). Serialized per (pr, env). maxIters guards the loop.

func (*Shell) HandleExec added in v0.43.0

func (sh *Shell) HandleExec(ctx context.Context, execID string, sig execution.Signal) error

HandleExec processes one execution-lifecycle signal for execID: gather (replay) → Decide → fold (Evolve) → persist (append + snapshot) → project. Serialized per execID. Lock ordering is strictly one-directional — gate-before-exec, never the reverse: the run-trigger path holds the (pr,env) gate lock across execute→materializeRun→HandleExec (so gate is the outer lock there), while the ingest handlers take the exec lock alone. No code ever acquires the gate lock while holding the exec lock, so the two aggregates cannot deadlock. (projectExecution takes no lock.) Preserve this ordering when adding new cross-aggregate paths.

func (*Shell) RebuildClaims added in v0.17.0

func (sh *Shell) RebuildClaims(env string) error

RebuildClaims replays env's claim ledger and rewrites its apply_claims projection from the folded state — the regenerate-the-read-model-from-the-log seam (used by the sweep to compact).

Jump to

Keyboard shortcuts

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