lambdaapp

package
v1.76.10 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2026 License: MIT Imports: 31 Imported by: 0

Documentation

Overview

Package lambdaapp runs sluis as ONE AWS Lambda function: one binary, one zip, one function (docs/reference/sluis/lambda.md). The function takes

  • API Gateway HTTP API events: the issuer and the console, the same mux the Kubernetes process serves on its listener;
  • {"kind":"tick"|"run","controller":"github"|"slack","target":"<id>"}: one pass of one target under its lease, by the controller the policy says the target belongs to (the GitHub controller's organisations and `github:links`, the Slack controller's workspaces). `tick` is what an EventBridge Scheduler schedule sends, `run` what a console request sends (internal/port/invoke);
  • {"kind":"cloudflare"}: the rotation of the Cloudflare credentials that are due (the `cloudflare` section), which has no loop to run in on Lambda;
  • {"kind":"clients"}: the reconcile of the generated client secrets, which has no loop to run in on Lambda;
  • {"kind":"refresh"}: the same reconcile and, where the directory is hosted in the process, its refresh; the name before "clients", accepted for one release;
  • {"kind":"backup"}: the backup module's function (a document of apiVersion sluis-backup/v1), which takes this event and `rpc` events and nothing else;
  • {"kind":"restore"}: the same zip as the restore function (`backup.role: restore`), which takes this event and `rpc` events and nothing else.

This package assembles the function from the same pieces the Kubernetes process is assembled from (internal/rosterapp and the controllers' apps), and adds the two things Lambda needs: translating the platform's events, and reading secrets from SSM at cold start. The controllers named in the service document (`controllers`) do not run their loops here: each is assembled per invocation that needs it. It names no cluster, NATS or Valkey client, and cmd/sluis's lambda-build import guard and internal/boundaries keep it so.

Index

Constants

View Source
const (
	// KindTick is what an EventBridge Scheduler schedule sends, one per target.
	KindTick = "tick"
	// KindRun is what the http function sends when somebody asks for a pass now
	// (internal/port/invoke).
	KindRun = invoke.KindRun
)

The event kinds of a controller function. Both run the same pass; they differ in who asked.

View Source
const (
	// OutcomeRan is a pass that ran to its end.
	OutcomeRan = "ran"
	// OutcomeUnknown is a run-now of a target this controller does not run: the
	// notification was sent to every controller because nobody knew which kind
	// the target is, and the other one ends here, cleanly.
	OutcomeUnknown = "unknown"
	// OutcomeMaintenance is a pass that was skipped because the module is under
	// maintenance. A target another runner holds is [OutcomeContended].
	OutcomeMaintenance = "maintenance"
	// OutcomeContended is a target another invocation holds the lease of: the
	// pass is its, and this one ends cleanly. It is not a failure, so the
	// platform neither retries it nor counts it as an error.
	OutcomeContended = "contended"
)

The outcomes of a controller invocation.

View Source
const (
	ModuleIssuer     = "issuer"
	ModuleCloudflare = "cloudflare"
	ModuleBackup     = "backup"
	ModuleGitHub     = "github"
	ModuleSlack      = "slack"
	ModuleGoogle     = "google"
)

Module names: the role a function runs as. A zip is built for exactly one of them (cmd/sluis-<module>), and its document must be that module's.

View Source
const KindBackup = "backup"

KindBackup is the event an EventBridge Scheduler schedule sends the backup function: {"kind":"backup"} writes a backup in one invocation (format 2). {"kind":"backup","resume":true} was the schedule that continued a paused run of format 1; it still answers, idle, so a schedule left behind does not fail.

View Source
const KindCheck = "check"

KindCheck is the event a deploy sends the function after it exists, to prove that every secret the function's document and policy declare is in SSM: {"kind":"check"}. It reads no state and writes nothing; the answer is the deploycheck report (addresses and versions, never a value), and a missing or empty secret is an error of the invocation, naming the addresses, so the deploy fails.

View Source
const KindClients = "clients"

KindClients is the event an EventBridge Scheduler schedule sends the issuer function to look after the generated client secrets: {"kind":"clients"}. It reconciles the clients' secrets and nothing else; the directory's snapshots are the Google module's, refreshed by its own function.

Deprecated: KindRefresh is the same schedule under its old name, still accepted for one release.

View Source
const KindCloudflare = "cloudflare"

KindCloudflare is the event an EventBridge Scheduler schedule sends the `http` function to rotate the Cloudflare credentials that are due: {"kind":"cloudflare"}. A minute's schedule costs one read of the secrets store per preset until a rotation is due.

View Source
const KindRefresh = "refresh"

KindRefresh is the event an EventBridge Scheduler schedule sends the `http` function to take a new snapshot of every connected workspace's directory: {"kind":"refresh"}. The Kubernetes server does it on a ticker; there is no loop on Lambda, and a snapshot that is not refreshed stops being authoritative after the freshness window, which sign-in refuses on.

View Source
const KindRestore = "restore"

KindRestore is the event that drives the restore function (the backup zip with `backup.role: restore`, docs/decisions/0072): only an administrator or the break-glass role may invoke that function, by IAM. Invoked asynchronously (the platform's `Event` invocation type) it starts a restore and returns at once; the work goes on in this invocation and, when time runs out, in the next, which the function starts itself:

{"kind":"restore","backup":"<id>"}                    start a restore of the backup
{"kind":"restore","backup":"<id>","overwrite":true}   ... replacing different data in the destination
{"kind":"restore","backup":"<id>","preview":true}     what it would do; writes nothing (invoke it synchronously)
{"kind":"restore","resume":true}                      continue a restore that paused

"by" and "note" are the caller's own words for the status record. A restore that fails is not an error of the invocation, which the platform would retry: the outcome, `restore.status` and the audit trail say so, and every module stays under maintenance until a restore completes.

Variables

This section is empty.

Functions

func CheckAdapters added in v1.76.0

func CheckAdapters(verify func(context.Context) error,
	check func(context.Context) (deploycheck.Report, error)) func(context.Context) (deploycheck.Report, error)

CheckAdapters is a check that first builds the adapters the function uses (verify) and then answers check. The secrets check proves the secrets are there; it never builds the adapters, so a document that lacks a setting an adapter needs passes it and fails the function's first tick. verify's refusal is the check's, with the setting it names.

func CheckFunc added in v1.75.0

func CheckFunc(file string, only ...string) func(context.Context) (deploycheck.Report, error)

CheckFunc is the {"kind":"check"} event's body: the declared secrets of the document in file (and the policy it names), read from SSM. only keeps the kinds the function's module reads; none keeps them all. The document is read again when asked, which is once per deploy.

func CheckOwn added in v1.76.0

func CheckOwn(s *config.Secrets, pol *config.PolicyDocument, kinds ...string) func(context.Context) (deploycheck.Report, error)

CheckOwn is the {"kind":"check"} answer of a function whose module declares the given kinds of secret (none, for the GitHub and Slack controllers: an App's credential is made at run time and no document names it). It holds the document's `secrets` section to the same rules as every function's, and reads only what the module itself reads, never another module's paths: the policy's client secrets are the issuer's, and a controller must not report them.

func CheckPin added in v1.75.0

func CheckPin(pinned, module string) error

CheckPin refuses a binary whose pinned module (the build's, empty for an unpinned development build) is not the module its main is for.

func Logger added in v1.75.0

func Logger(ctx context.Context, service string, level slog.Level) (*slog.Logger, func(context.Context), error)

Logger is the JSON logger a function writes to stdout, which CloudWatch Logs (and the OTLP layer's log pipeline) collects.

func ModuleOf added in v1.75.0

func ModuleOf(file string) (string, error)

ModuleOf is the module a service document is for, by its apiVersion: controller-github/v2 is the GitHub module's, controller-slack/v2 the Slack module's, sluis-google/v1 the Google module's and sluis-backup/v1 the backup module's. A sluis/v3 document that sets cloudflare.serve is the Cloudflare module's; any other document is the issuer's.

func ParseEvent

func ParseEvent(payload json.RawMessage) (invoke.Event, error)

ParseEvent reads and checks a controller function's event.

func Register added in v1.75.0

func Register(module string, open OpenFunc)

Register links a module's function into the binary. Each module's package (internal/lambdaapp/issuerfn, backupfn, cloudflarefn) registers itself in its init, and a main imports the ones it carries: a zip holds the code of its module and of no other.

func Request

Request makes the http.Request an event describes. The headers arrive lower-cased and the cookies apart from them, and the body is base64 when the gateway says so.

func Response

func Response(status int, header http.Header, body []byte) events.APIGatewayV2HTTPResponse

Response makes an event's response of what a handler wrote. Set-Cookie goes in the response's cookies, as the gateway requires (a header of that name is dropped), the other headers keep their values comma-joined, and the body is base64 when it is not text.

func Start added in v1.75.0

func Start()

Start is the Lambda entry of the lambda build of cmd/sluis: it opens the function from the environment, whichever linked module the document is for, and serves API Gateway events and scheduler ticks until the runtime stops it.

func StartModule added in v1.75.0

func StartModule(module string)

StartModule is the entry of a zip built for one module (cmd/sluis-<module>). It refuses to run when the build pins another module (the release builds each zip with `-ldflags -X .../internal/version.Module=sluis-module=<module>`), and when the document is another module's.

Types

type BackupResult added in v1.75.0

type BackupResult struct {
	Kind string `json:"kind"`
	// Outcome is completed, idle, contended or maintenance (paused was a run
	// of format 1).
	Outcome string `json:"outcome"`
	ID      string `json:"id,omitempty"`
	// Done and Units were the progress of a paused run of format 1; always 0.
	Done  int `json:"done,omitempty"`
	Units int `json:"units,omitempty"`
}

BackupResult is what a backup invocation returns.

type CloudflareResult added in v1.74.0

type CloudflareResult struct {
	Kind    string `json:"kind"`
	Presets string `json:"presets"`
	Failed  int    `json:"failed"`
}

CloudflareResult is what a cloudflare invocation returns.

type Controller

type Controller struct {
	// Name is the controller's, in log lines.
	Name string
	// Open assembles the controller.
	Open func(ctx context.Context) (Pass, error)
	Log  *slog.Logger
	// Unknown says an error is the controller not running that target.
	Unknown func(error) bool
}

Controller runs one pass of one target per invocation.

The controller is assembled per invocation and closed at its end, as `sluis tick` does: the audit emitter delivers asynchronously, and a function that is frozen between invocations would hold its queue for hours. What is per process stays per process: the configuration, read and validated at cold start, and the secrets resolved into the environment.

func (*Controller) Handle

func (c *Controller) Handle(ctx context.Context, payload json.RawMessage) (any, error)

Handle implements Handler: payload is {"kind":"tick"|"run","controller":"github"|"slack","target":"<id>"}.

type Function

type Function struct {
	Handler Handler
	// Flush sends what telemetry holds; the platform freezes the process when
	// an invocation returns, and a batch waiting for its timer waits for the
	// next invocation.
	Flush func(context.Context)
	// Close releases what the function holds, at shutdown.
	Close func()
}

Function is the function, assembled at cold start and ready to be invoked.

func Open

func Open(ctx context.Context, getenv func(string) string) (*Function, error)

Open assembles the function of whichever module the document is for, among the modules linked into the binary (the deprecated `sluis-lambda` zip links all of them).

func OpenModule added in v1.75.0

func OpenModule(ctx context.Context, getenv func(string) string, module string) (*Function, error)

OpenModule is Open for a zip built for module: a document that is another module's is refused before anything is assembled, so a zip cannot run as a module it was not built for. An empty module accepts any linked one.

type HTTP

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

HTTP serves API Gateway HTTP API events (payload format 2.0) with an http.Handler: the same mux the Kubernetes server serves on its listener.

func NewHTTP

func NewHTTP(handler http.Handler, settle func(), log *slog.Logger) *HTTP

NewHTTP adapts handler. settle may be nil.

func (*HTTP) Handle

func (h *HTTP) Handle(ctx context.Context, payload json.RawMessage) (any, error)

Handle implements Handler: payload is an API Gateway HTTP API event.

func (*HTTP) Serve

Serve runs one request through the handler and returns its response. A request that cannot be made into an http.Request is a 400, and a handler that panics a 500: neither is an error of the invocation, which the platform would retry.

func (*HTTP) WithBackup added in v1.75.0

func (h *HTTP) WithBackup(run func(ctx context.Context, resume bool) (BackupResult, error)) *HTTP

WithBackup makes the function answer {"kind":"backup"} events with run; resume is the event's `resume`. A run that failed is an error of the invocation, so the schedule sees it; a run that paused, found the lease held, or was skipped for maintenance is not.

func (*HTTP) WithCheck added in v1.75.0

func (h *HTTP) WithCheck(run func(context.Context) (deploycheck.Report, error)) *HTTP

WithCheck makes the function answer {"kind":"check"} events with run.

func (*HTTP) WithClients added in v1.76.0

func (h *HTTP) WithClients(run func(context.Context) (RefreshResult, error)) *HTTP

WithClients makes the function answer {"kind":"clients"} events with run: the reconcile of the generated client secrets and nothing else.

func (*HTTP) WithCloudflare added in v1.74.0

func (h *HTTP) WithCloudflare(run func(context.Context) (failed int, summary string, err error)) *HTTP

WithCloudflare makes the function answer {"kind":"cloudflare"} events with run.

func (*HTTP) WithControllers added in v1.63.0

func (h *HTTP) WithControllers(kindsOf func(target string) []string, controllers map[string]*Controller) *HTTP

WithControllers makes the function answer {"kind":"tick"|"run","controller":...,"target":...} events by running one pass of the target under the controller the event names (the controllers' names are the keys: "github", "slack"). kindsOf says which controllers declare a target, from the policy; an empty answer is a target nobody declares. An event that names no controller is routed by it only when exactly one declares the target.

func (*HTTP) WithRPC added in v1.75.0

func (h *HTTP) WithRPC(s *modcall.Server) *HTTP

WithRPC makes the function answer {"kind":"rpc"} events, the calls other modules make to the module it runs, with s. Only the function that runs that module is given a server: any other refuses them.

func (*HTTP) WithRefresh added in v1.61.1

func (h *HTTP) WithRefresh(run func(context.Context) (RefreshResult, error)) *HTTP

WithRefresh makes the function answer {"kind":"refresh"} events with run. "refresh" is the name before "clients" and is accepted for one release.

func (*HTTP) WithRefreshWorkspace added in v1.76.0

func (h *HTTP) WithRefreshWorkspace(run func(ctx context.Context, workspace string) (RefreshResult, error)) *HTTP

WithRefreshWorkspace makes the function answer {"kind":"refresh","workspace":"<id>"} events with run, for the one workspace the event names.

func (*HTTP) WithRestore added in v1.75.0

func (h *HTTP) WithRestore(run func(ctx context.Context, ev RestoreEvent) (RestoreResult, error)) *HTTP

WithRestore makes the function answer {"kind":"restore"} events with run.

type Handler

type Handler interface {
	Handle(ctx context.Context, payload json.RawMessage) (any, error)
}

Handler is one function's entry point: the platform's event in, a result out.

type OpenFunc added in v1.75.0

type OpenFunc func(ctx context.Context, file string) (*Function, error)

OpenFunc assembles the function of one module from its document.

type Pass

type Pass interface {
	// Pass runs the target's pass under its lease; false, with no error, means
	// another runner holds it.
	Pass(ctx context.Context, target string, unsafeLocal bool) (ran bool, err error)
	// Close flushes what the pass recorded.
	Close() error
}

Pass is one assembled controller, for one invocation.

type RefreshResult added in v1.61.1

type RefreshResult struct {
	Kind       string `json:"kind"`
	Workspaces int    `json:"workspaces"`
	Ran        int    `json:"ran"`
	Contended  int    `json:"contended"`
	Failed     int    `json:"failed"`
	// Paused are the workspaces left alone because the module is under maintenance.
	Paused int `json:"paused,omitempty"`
}

RefreshResult is what a refresh or clients invocation returns.

type RestoreEvent added in v1.75.0

type RestoreEvent struct {
	Kind      string `json:"kind"`
	Backup    string `json:"backup,omitempty"`
	Overwrite bool   `json:"overwrite,omitempty"`
	Preview   bool   `json:"preview,omitempty"`
	Resume    bool   `json:"resume,omitempty"`
	By        string `json:"by,omitempty"`
	Note      string `json:"note,omitempty"`
}

RestoreEvent is the {"kind":"restore"} event.

type RestoreResult added in v1.75.0

type RestoreResult struct {
	Kind string `json:"kind"`
	// Outcome is started work's completed, paused, failed, contended or idle; a
	// start that changed nothing is refused; a preview is preview.
	Outcome string `json:"outcome"`
	ID      string `json:"id,omitempty"`
	Backup  string `json:"backup,omitempty"`
	// State and Maintenance are the run's, Reason and Error say why it failed,
	// paused or was refused.
	State       string `json:"state,omitempty"`
	Maintenance string `json:"maintenance,omitempty"`
	Reason      string `json:"reason,omitempty"`
	Error       string `json:"error,omitempty"`
	// Continued is whether the function started its own next invocation.
	Continued bool `json:"continued,omitempty"`
	// Report is a preview's: per module the counts and the names, never a value.
	Report any `json:"report,omitempty"`
}

RestoreResult is what a restore invocation returns.

type Result

type Result struct {
	Kind    string `json:"kind"`
	Target  string `json:"target"`
	Outcome string `json:"outcome"`
}

Result is what a controller invocation returns.

Directories

Path Synopsis
Package backupfn is the backup module's Lambda function, and the restore function of the same zip (`backup.role: restore`).
Package backupfn is the backup module's Lambda function, and the restore function of the same zip (`backup.role: restore`).
Package cloudflarefn is the Cloudflare module's Lambda function: it answers other modules' calls and has no HTTP surface.
Package cloudflarefn is the Cloudflare module's Lambda function: it answers other modules' calls and has no HTTP surface.
Package githubfn is the GitHub module's function: the organisations' team passes and the links pass, on a schedule (`tick`) or when the console asks (`run`), and the installation-token mint, the `github` module's `mint_installation` method, which the issuer calls over modcall through its `live-issuer` alias and no other class may (D-v176-7 a: one function serves both, with the concurrency limit and the mint deadline of the one).
Package githubfn is the GitHub module's function: the organisations' team passes and the links pass, on a schedule (`tick`) or when the console asks (`run`), and the installation-token mint, the `github` module's `mint_installation` method, which the issuer calls over modcall through its `live-issuer` alias and no other class may (D-v176-7 a: one function serves both, with the concurrency limit and the mint deadline of the one).
Package googlefn is the Google module's function: the directory refresh, on a schedule ({"kind":"refresh"}, every workspace) or for one workspace ({"kind":"refresh","workspace":"<id>"}), the check of its own secrets, and the module's calls: the console's Workspace service (`console.*`, caller class `console`) and the sign-in (`identify`, `sign_in_url`, caller class `issuer`).
Package googlefn is the Google module's function: the directory refresh, on a schedule ({"kind":"refresh"}, every workspace) or for one workspace ({"kind":"refresh","workspace":"<id>"}), the check of its own secrets, and the module's calls: the console's Workspace service (`console.*`, caller class `console`) and the sign-in (`identify`, `sign_in_url`, caller class `issuer`).
Package issuerfn is the issuer module's Lambda function: the issuer, the signer and the console in one process (docs/decisions/0072), assembled from the same pieces the Kubernetes process is.
Package issuerfn is the issuer module's Lambda function: the issuer, the signer and the console in one process (docs/decisions/0072), assembled from the same pieces the Kubernetes process is.
Package lambdatest is a container-free end-to-end harness for the Lambda functions: an httptest server that speaks the Lambda Invoke API and routes FunctionName plus Qualifier to in-process lambdaapp.Function handlers.
Package lambdatest is a container-free end-to-end harness for the Lambda functions: an httptest server that speaks the Lambda Invoke API and routes FunctionName plus Qualifier to in-process lambdaapp.Function handlers.
Package slackfn is the Slack module's controller function: the workspaces' user-group passes, on a schedule (`tick`) or when the console asks (`run`).
Package slackfn is the Slack module's controller function: the workspaces' user-group passes, on a schedule (`tick`) or when the console asks (`run`).

Jump to

Keyboard shortcuts

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