Documentation
¶
Overview ¶
Package modcall is how one module calls another (docs/decisions/0071 D93 a): a typed request and response over one of three transports, behind one Go interface chosen by configuration.
- Local calls a Server in the same process: tests, and every deployment that has not split the callee out.
- HTTPCaller posts to the callee's Service on Kubernetes, with a bearer the callee verifies (Server.Handler).
- internal/modcall/lambdacall invokes the callee's function through one alias per caller class (`live-issuer`, `live-console`, ...) on Lambda; it is a package of its own so that a Kubernetes build does not carry the Lambda client.
A call is a module name, a method name and a JSON payload. The callee answers a JSON result or a coded Error; anything else a handler returns is reported as an internal error with no detail, since the caller is another module's trust domain.
Who is calling is the transport's to say, never the payload's: Local knows it from its constructor (Local.As), HTTP from the verified token, Lambda from the alias the function was invoked through. A method that registers Allow answers only those caller classes and refuses the rest with CodeForbidden.
The envelope also carries the caller's trace (`traceparent`, `tracestate`), its deadline, and is bounded in size; a callee answers a request of the previous version in that version. See Request and [Dispatch].
Index ¶
- Constants
- Variables
- func Begin(ctx context.Context, module, method string, payload []byte, maxBytes int) (context.Context, Request, *Pending, error)
- func CallerOf(ctx context.Context) string
- func Coded(code, message string) error
- func CodedCause(code, message string, cause error) error
- func DefaultTokenFile(module string) string
- func Do[Req, Resp any](ctx context.Context, c Caller, module, method string, req Req) (Resp, error)
- func Handle[Req, Resp any](s *Server, method string, fn func(ctx context.Context, req Req) (Resp, error), ...)
- func NoRetry(ctx context.Context) bool
- func Result(r Response) ([]byte, error)
- func Start[Req any](ctx context.Context, c Caller, module, method string, req Req) error
- func WithCaller(ctx context.Context, class string) context.Context
- func WithCallerClasses(ctx context.Context, classes ...string) context.Context
- func WithRequestID(ctx context.Context, id string) context.Context
- func WithoutRetry(ctx context.Context) context.Context
- type AsyncCaller
- type Caller
- type Config
- type Error
- type HTTPCaller
- type HandlerOption
- type Local
- type Method
- type Option
- type Pending
- type Request
- type Response
- type Router
- type Server
- type Target
- type TokenSource
- type Verifier
Constants ¶
const ( DefaultMaxBytes = 256 << 10 HardMaxBytes = 5 << 20 )
The size bounds of a call. A method answers requests and responses of at most DefaultMaxBytes unless it registers MaxBytes; no method may exceed HardMaxBytes, which keeps a call under Lambda's 6 MB synchronous payload.
const ( CodeInternal = "internal" CodeBadRequest = "bad_request" CodeNoSuchMethod = "no_such_method" CodeForbidden = "forbidden" // CodeDeadlineExceeded is a call whose deadline had passed when it arrived, // or passed while the method ran. CodeDeadlineExceeded = "deadline_exceeded" )
Codes of an Error. A module may define more for its own methods.
const Kind = "rpc"
Kind is the `kind` of a Lambda event that carries a module call, beside the scheduler's ticks.
const MinVersion = Version - 1
MinVersion is the oldest version a callee still answers (N-1).
const RPCPath = "/rpc"
RPCPath is where a module's Service answers calls.
const Version = 2
Version is the protocol version a caller writes in Request.V. Version 1, which wrote none, is read as 1. A callee answers versions MinVersion to Version, each in the caller's own version: a release is deployed one function at a time, so for a while the two sides differ by one.
Variables ¶
var ( // ErrNoRoute is a call to a module no transport is configured for. ErrNoRoute = errors.New("module call: no route") // ErrTransport is a call that did not get an answer from the callee. ErrTransport = errors.New("module call: transport failed") )
var ErrNoAsync = errors.New("module call: this transport cannot start a call and return")
ErrNoAsync is a Start over a transport that cannot return early.
Functions ¶
func Begin ¶
func Begin(ctx context.Context, module, method string, payload []byte, maxBytes int) (context.Context, Request, *Pending, error)
Begin prepares an outgoing call for a transport: it refuses a payload over maxBytes (DefaultMaxBytes when 0) and a context already done, starts the client span, and builds the envelope with the trace and the deadline of the returned context. Every transport calls it, so that they carry the same fields; the caller class is never one of them.
func CallerOf ¶
CallerOf is the caller class the transport authenticated the call as; empty for a call the transport cannot name.
func CodedCause ¶ added in v1.76.0
CodedCause is Coded that keeps the error behind it for the callee's log. The cause is not sent: a caller is another module's trust domain.
func DefaultTokenFile ¶ added in v1.76.0
DefaultTokenFile is where the projected token for a module's Service is mounted when the configuration does not say.
func Handle ¶
func Handle[Req, Resp any](s *Server, method string, fn func(ctx context.Context, req Req) (Resp, error), opts ...Option)
Handle registers a typed method. A method registered twice is a programming error and panics.
func NoRetry ¶ added in v1.76.0
NoRetry reports whether ctx was marked by WithoutRetry.
func Start ¶ added in v1.77.0
Start is a typed call that returns once the callee has been handed the call.
func WithCaller ¶
WithCaller records the caller class the transport authenticated the call as. Only a transport calls it; a request's payload cannot.
func WithCallerClasses ¶ added in v1.76.0
WithCallerClasses records that the caller the transport authenticated holds every one of classes. On Kubernetes one ServiceAccount is the issuer's pod, which is both the issuer and the console; on Lambda those are two aliases. Only a transport calls it. CallerOf is the first class.
func WithRequestID ¶ added in v1.76.0
WithRequestID marks ctx with the transport's id of the request (a Lambda request id), for the callee's log.
func WithoutRetry ¶ added in v1.76.0
WithoutRetry marks a call that must not be sent twice: a transport that retries a failed send on its own (the AWS client does, for a throttled or reset invoke) does not, for a call with this context. A method that spends something single-use, such as an OAuth code, is called this way, because a retry after a lost answer would find the code spent.
Types ¶
type AsyncCaller ¶ added in v1.77.0
type AsyncCaller interface {
CallAsync(ctx context.Context, module, method string, payload []byte) error
}
AsyncCaller is a Caller that can start a call and return before the callee has finished: a method that runs for minutes (a backup) is started by the issuer for a person who asked, and read back by its record. The callee answers nobody; what it did is in its own records.
A Lambda call is an asynchronous invocation, accepted when the service queues it. A call to a Service, or in this process, runs in the background of the caller, which lives on.
type Caller ¶
type Caller interface {
Call(ctx context.Context, module, method string, payload []byte) ([]byte, error)
}
Caller sends one call to a module and returns its raw result.
type Error ¶
type Error struct {
Code string `json:"code"`
Message string `json:"message,omitempty"`
// contains filtered or unexported fields
}
Error is a failure the callee chose to tell: a code a caller can switch on and a message that is safe to show another module.
type HTTPCaller ¶
type HTTPCaller struct {
// URLs maps a module to its Service's base URL; Audiences to the audience of
// the bearer it verifies (the module's name when absent).
URLs map[string]string
Audiences map[string]string
Token TokenSource
Client *http.Client
// MaxBytes bounds a request payload and a result ([DefaultMaxBytes] when 0);
// raise it only for a callee that registered a larger [MaxBytes].
MaxBytes int
}
HTTPCaller calls modules' Services.
func (*HTTPCaller) Call ¶
func (c *HTTPCaller) Call(ctx context.Context, module, method string, payload []byte) ([]byte, error)
Call implements Caller.
func (*HTTPCaller) CallAsync ¶ added in v1.77.0
CallAsync implements AsyncCaller.
type HandlerOption ¶
type HandlerOption func(*handlerConfig)
HandlerOption configures Server.Handler.
func WithClasses ¶
func WithClasses(classes map[string]string) HandlerOption
WithClasses maps verified subjects to caller classes. A subject it does not map is its own class, as a test transport names it; on Kubernetes use WithSubjectClasses, which refuses what it does not list.
func WithSubjectClasses ¶ added in v1.76.0
func WithSubjectClasses(holds map[string][]string) HandlerOption
WithSubjectClasses maps verified subjects to the caller classes each holds, for a subject that is several (the issuer's pod is the issuer and the console). A subject it does not list is refused with a 401 before it reaches a method: the class is never guessed from a ServiceAccount's name, which any namespace may reuse.
type Local ¶
Local calls servers in this process, through the same JSON the other transports carry, so what works here works across a boundary. Called as itself it has no caller class, so a method with Allow refuses it; Local.As names the class the caller is.
type Method ¶ added in v1.76.0
type Method struct {
Name string
// Allow is the caller classes the method answers, sorted. Nil is any
// caller the transport let in: no [Allow] was registered.
Allow []string
// MaxBytes is the bound of the request payload and of the result.
MaxBytes int
}
Method describes one registered method, for a lint over what a module registered: it carries no handler.
type Option ¶
type Option func(*handler)
Option configures a method at registration.
func Allow ¶
Allow limits a method to the named caller classes. A call from any other class, or from one the transport cannot name, is refused with CodeForbidden before the method runs.
func MaxBytes ¶
MaxBytes sets the bound, in bytes, of a method's request payload and of its result, in place of DefaultMaxBytes. A value over HardMaxBytes is a programming error and panics at registration. A caller of such a method sets the same bound on its transport.
type Pending ¶
type Pending struct {
// contains filtered or unexported fields
}
Pending is one outgoing call between Begin and Pending.Finish.
type Request ¶
type Request struct {
// V is the protocol version; absent is 1.
V int `json:"v,omitempty"`
Kind string `json:"kind"`
Module string `json:"module"`
Method string `json:"method"`
Payload json.RawMessage `json:"payload,omitempty"`
// Version 2 and later. A version 1 request has none of them, and a callee
// ignores them on one.
//
// Traceparent and Tracestate are the caller's W3C trace context; the callee
// continues the trace with a span of its own.
Traceparent string `json:"traceparent,omitempty"`
Tracestate string `json:"tracestate,omitempty"`
// Deadline is the caller's deadline as Unix milliseconds. The callee bounds
// its work by it, and refuses a call that arrives after it. It is read
// against the callee's clock, so hosts need ordinary time sync.
Deadline int64 `json:"deadline,omitempty"`
}
Request is one call on the wire. A reader ignores fields it does not know, so a later version can add some without breaking this one.
type Response ¶
type Response struct {
V int `json:"v,omitempty"`
Result json.RawMessage `json:"result,omitempty"`
Error *Error `json:"error,omitempty"`
}
Response is its answer: a result, or an error. V is the version the callee answered in, the caller's own; version 1 wrote none.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router sends each call by the configuration: to the server in this process unless the module has a Function or a URL.
func NewRouter ¶
NewRouter builds the Caller. lambda and http may be nil when the configuration names no such target; a target whose transport is missing is an error here, not at the first call.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server dispatches calls to the methods a module registered.
func (*Server) Dispatch ¶
Dispatch answers one request. It never returns a Go error: a failure is the response's.
It answers version MinVersion to Version in the request's own version; a newer one is refused. The caller's trace continues in a server span, the caller's deadline bounds the method's context, and a payload or a result over the method's bound is refused without being echoed.
func (*Server) Handler ¶
func (s *Server) Handler(verify Verifier, opts ...HandlerOption) http.Handler
Handler serves the module's calls at RPCPath. A call with no bearer, or one verify refuses, is a 401 and reaches no method. The caller class is the verified subject's (WithClasses, else the subject itself); the request body has no say in it.
func (*Server) Methods ¶ added in v1.76.0
Methods lists the methods registered on s, sorted by name.
func (*Server) SetLogger ¶ added in v1.76.0
SetLogger makes the server log what it refuses or fails to answer, with the request id WithRequestID put on the context: a caller is told a code and nothing else, so without this the callee's log says nothing of a failed call. A server with no logger logs nothing; SetLogger does not replace one already set.
type Target ¶
type Target struct {
Function string `json:"function,omitempty"`
URL string `json:"url,omitempty"`
Audience string `json:"audience,omitempty"`
// TokenFile is where the pod's projected token for a URL target is mounted;
// [DefaultTokenFile] when unset.
TokenFile string `json:"tokenFile,omitempty"`
// MaxBytes bounds a request payload and a result to this module in bytes,
// above [DefaultMaxBytes] for a module whose methods list ([MaxBytes]); zero
// is the default.
MaxBytes int `json:"maxBytes,omitempty"`
}
Target is where one module is reached when it is not in this process. Set Function for a Lambda function (the alias of the caller's class), URL for a Kubernetes Service. Neither: the module is in this process.
type TokenSource ¶
TokenSource yields the bearer for a call to the module whose audience is given (internal/consoleauth.Source, one per audience).
Directories
¶
| Path | Synopsis |
|---|---|
|
Package dial is how a process turns the routing its document names into a modcall.Caller: Dial builds the modcall.Router for a modcall.Config, with the Lambda transport only in a build with the `lambda` tag, so that the Kubernetes build carries no Lambda client.
|
Package dial is how a process turns the routing its document names into a modcall.Caller: Dial builds the modcall.Router for a modcall.Config, with the Lambda transport only in a build with the `lambda` tag, so that the Kubernetes build carries no Lambda client. |
|
Package lambdacall is the Lambda transport of internal/modcall: a call is a synchronous lambda:InvokeFunction of the callee's `live-<class>` alias for the caller class with the JSON envelope as payload, and a function answers one with Serve.
|
Package lambdacall is the Lambda transport of internal/modcall: a call is a synchronous lambda:InvokeFunction of the callee's `live-<class>` alias for the caller class with the JSON envelope as payload, and a function answers one with Serve. |