lambdaapp

package
v1.75.0-rc.3 Latest Latest
Warning

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

Go to latest
Published: Oct 10, 2026 License: MIT Imports: 42 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","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":"refresh"}: the directory refresh, which has no loop to run in on Lambda;
  • {"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-lambda's import guard keeps 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 KindBackup = "backup"

KindBackup is the event an EventBridge Scheduler schedule sends the backup function: {"kind":"backup"} writes a backup, or continues one that paused for want of time; {"kind":"backup","resume":true} only continues a paused one and costs one read of the State when there is none, which is the schedule to run every few minutes beside the daily one so a large installation finishes.

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 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 ParseEvent

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

ParseEvent reads and checks a controller function's event.

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 service: it opens the function from the environment and serves API Gateway events and scheduler ticks until the runtime stops it. Both mains that ship as `bootstrap` call it (cmd/sluis-lambda and the lambda build of cmd/sluis), so there is one dispatch.

Types

type BackupResult added in v1.75.0

type BackupResult struct {
	Kind string `json:"kind"`
	// Outcome is completed, paused, idle, contended or maintenance.
	Outcome string `json:"outcome"`
	ID      string `json:"id,omitempty"`
	// Done and Units are the export's progress of a paused run.
	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","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. getenv is the process's.

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) 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(kindOf func(target string) string, controllers map[string]*Controller) *HTTP

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

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.

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 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"`
}

RefreshResult is what a refresh 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.

Jump to

Keyboard shortcuts

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