Documentation
¶
Overview ¶
Package procplugin implements both sides of the process-plugin protocol (docs/SPEC.md §37.4): executables named `ds-<kind>-<name>` on PATH that speak one JSON object per line over stdin and stdout, so a plugin can be written in any language and a Go plugin can be shipped as a process with no extra code.
The host enforces what the plugin is not trusted with: a timeout, a cap on response size, a protocol handshake, and the rule that a resolver reply may contain nothing but existence and a hash. A resolver that returns a value fails here, before anything downstream can see it.
Index ¶
- Constants
- Variables
- func Name(kind, name string) string
- func Serve(r io.Reader, w io.Writer, name string, kinds []string, handle Handler) error
- type Handler
- type Host
- func (h Host) Call(ctx context.Context, executable string, req Request) (Response, error)
- func (h Host) Pick(ctx context.Context, format, file, expr string) (Response, error)
- func (h Host) Records(ctx context.Context, source string, query json.RawMessage) (json.RawMessage, error)
- func (h Host) Resolve(ctx context.Context, provider, addr, want string) (exists bool, hash string, err error)
- func (h Host) Verb(ctx context.Context, verb, op string, ref, stateView json.RawMessage) (Response, error)
- type Range
- type Request
- type Response
Constants ¶
const ( KindPick = "pick" KindResolve = "resolve" KindVerb = "verb" KindRecords = "records" )
Plugin kinds and the executable name pattern `ds-<kind>-<name>`.
const ( OpHandshake = "handshake" OpPick = "pick" OpResolve = "resolve" OpCheck = "check" OpRender = "render" OpQuery = "query" )
Operations.
const ( WantExists = "exists" WantHash = "hash" )
Resolver wants.
const ( DefaultTimeout = 10 * time.Second DefaultMaxBytes = 1 << 20 )
Defaults. The timeout is per call; the size cap bounds one response line.
const Protocol = 1
Protocol is the handshake version. A plugin answering a different number is refused by name, never by misparsing.
Variables ¶
var ( ErrNotFound = errors.New("procplugin: plugin executable not found") ErrProtocol = errors.New("procplugin: protocol mismatch or malformed reply") ErrTooLarge = errors.New("procplugin: reply exceeds the size cap") ErrValueLeak = errors.New("procplugin: resolver reply carried more than existence and hash") ErrPlugin = errors.New("procplugin: plugin reported an error") ErrTimeout = errors.New("procplugin: plugin timed out") )
Errors.
var KindValues = []string{KindPick, KindResolve, KindVerb, KindRecords}
KindValues is the canonical list.
Functions ¶
Types ¶
type Host ¶
type Host struct {
Timeout time.Duration
MaxBytes int
LookPath func(name string) (string, error)
// Args are prepended arguments for every invocation (a test harness
// re-executing itself needs them; normal plugins take none).
Args []string
// Stderr receives the plugin's stderr; nil discards it.
Stderr io.Writer
}
Host runs plugins. The zero value uses exec.LookPath, DefaultTimeout, and DefaultMaxBytes. LookPath and Args exist so tests and embedders can point at a specific binary; neither is read from the environment by this package.
func (Host) Call ¶
Call runs one request against the named plugin executable and returns its reply. Both the handshake and the request are written up front and the process is run to completion, so the exchange has no interleaving to race on; a plugin speaking another protocol is refused before its reply to the operation is read.
func (Host) Records ¶
func (h Host) Records(ctx context.Context, source string, query json.RawMessage) (json.RawMessage, error)
Records asks the `ds-records-<source>` plugin for rows.
type Request ¶
type Request struct {
Op string `json:"op"`
Protocol int `json:"protocol,omitempty"`
// pick
File string `json:"file,omitempty"`
Expr string `json:"expr,omitempty"`
// resolve
Addr string `json:"addr,omitempty"`
Want string `json:"want,omitempty"`
// verb
Ref json.RawMessage `json:"ref,omitempty"`
StateView json.RawMessage `json:"state_view,omitempty"`
// records
Query json.RawMessage `json:"query,omitempty"`
}
Request is one host→plugin message. Fields are per operation; unused ones are omitted on the wire.
type Response ¶
type Response struct {
// handshake
Protocol int `json:"protocol,omitempty"`
Name string `json:"name,omitempty"`
Kinds []string `json:"kinds,omitempty"`
// pick
Value *string `json:"value,omitempty"`
Range *Range `json:"range,omitempty"`
// resolve
Exists *bool `json:"exists,omitempty"`
Hash string `json:"hash,omitempty"`
// verb and records
Findings json.RawMessage `json:"findings,omitempty"`
Node json.RawMessage `json:"node,omitempty"`
Records json.RawMessage `json:"records,omitempty"`
// any
Error string `json:"error,omitempty"`
}
Response is one plugin→host message.