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
- func CheckAdapters(verify func(context.Context) error, ...) func(context.Context) (deploycheck.Report, error)
- func CheckFunc(file string, only ...string) func(context.Context) (deploycheck.Report, error)
- func CheckOwn(s *config.Secrets, pol *config.PolicyDocument, kinds ...string) func(context.Context) (deploycheck.Report, error)
- func CheckPin(pinned, module string) error
- func Logger(ctx context.Context, service string, level slog.Level) (*slog.Logger, func(context.Context), error)
- func ModuleOf(file string) (string, error)
- func ParseEvent(payload json.RawMessage) (invoke.Event, error)
- func Register(module string, open OpenFunc)
- func Request(ctx context.Context, event events.APIGatewayV2HTTPRequest) (*http.Request, error)
- func Response(status int, header http.Header, body []byte) events.APIGatewayV2HTTPResponse
- func Start()
- func StartModule(module string)
- type BackupResult
- type CloudflareResult
- type Controller
- type Function
- type HTTP
- func (h *HTTP) Handle(ctx context.Context, payload json.RawMessage) (any, error)
- func (h *HTTP) Serve(ctx context.Context, event events.APIGatewayV2HTTPRequest) (resp events.APIGatewayV2HTTPResponse)
- func (h *HTTP) WithBackup(run func(ctx context.Context, resume bool) (BackupResult, error)) *HTTP
- func (h *HTTP) WithCheck(run func(context.Context) (deploycheck.Report, error)) *HTTP
- func (h *HTTP) WithClients(run func(context.Context) (RefreshResult, error)) *HTTP
- func (h *HTTP) WithCloudflare(run func(context.Context) (failed int, summary string, err error)) *HTTP
- func (h *HTTP) WithControllers(kindsOf func(target string) []string, controllers map[string]*Controller) *HTTP
- func (h *HTTP) WithRPC(s *modcall.Server) *HTTP
- func (h *HTTP) WithRefresh(run func(context.Context) (RefreshResult, error)) *HTTP
- func (h *HTTP) WithRefreshWorkspace(run func(ctx context.Context, workspace string) (RefreshResult, error)) *HTTP
- func (h *HTTP) WithRestore(run func(ctx context.Context, ev RestoreEvent) (RestoreResult, error)) *HTTP
- type Handler
- type OpenFunc
- type Pass
- type RefreshResult
- type RestoreEvent
- type RestoreResult
- type Result
Constants ¶
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.
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.
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.
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.
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.
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.
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.
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.
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
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
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
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
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 ¶
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 ¶
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
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 (*HTTP) Serve ¶
func (h *HTTP) Serve(ctx context.Context, event events.APIGatewayV2HTTPRequest) (resp events.APIGatewayV2HTTPResponse)
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
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
WithCheck makes the function answer {"kind":"check"} events with run.
func (*HTTP) WithClients ¶ added in v1.76.0
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
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
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 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.
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`). |