Documentation
¶
Overview ¶
Package lambdaapp runs sluis as ONE AWS Lambda function: one binary, one zip, one function (docs/reference/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":"refresh"} and {"kind":"exports"}: the directory refresh and the exports pass, which have no loop to run in on Lambda.
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
- func ParseEvent(payload json.RawMessage) (invoke.Event, error)
- func Request(ctx context.Context, event events.APIGatewayV2HTTPRequest) (*http.Request, error)
- func Response(status int, header http.Header, body []byte) events.APIGatewayV2HTTPResponse
- type Controller
- type ExportsResult
- 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) WithControllers(kindOf func(target string) string, controllers map[string]*Controller) *HTTP
- func (h *HTTP) WithExports(run func(context.Context) (ExportsResult, error)) *HTTP
- func (h *HTTP) WithRefresh(run func(context.Context) (RefreshResult, error)) *HTTP
- type Handler
- type Pass
- type RefreshResult
- 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" // 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 KindExports = "exports"
KindExports is the event an EventBridge Scheduler schedule sends the `http` function to keep the exports (the copies of secrets under /sluis/export/...) current: {"kind":"exports"}. There is no loop to do it on Lambda.
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.
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.
Types ¶
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 ExportsResult ¶ added in v1.59.0
type ExportsResult struct {
Kind string `json:"kind"`
// Outcome is "ran", "none" (no export is declared) or "failed".
Outcome string `json:"outcome"`
Exports int `json:"exports"`
Done int `json:"done"`
Contended int `json:"contended"`
Failed int `json:"failed"`
}
ExportsResult is what an exports invocation returns.
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.
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) 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) WithExports ¶ added in v1.59.0
WithExports makes the function answer {"kind":"exports"} events with run.
func (*HTTP) WithRefresh ¶ added in v1.61.1
WithRefresh makes the function answer {"kind":"refresh"} 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.