binding

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package binding is the SDK for OpenRun binding providers: out-of-process plugins that implement service bindings (account and grant management on external services like databases). A provider implements the ServiceBinding interface for one or more service types and calls Serve from its main function. The OpenRun server launches the provider executable on demand and communicates with it over gRPC using hashicorp/go-plugin.

This package is a separate Go module so that providers only depend on the SDK's small dependency tree, not on the OpenRun server. It is distinct from the server embedding API in pkg/api.

Index

Constants

View Source
const (
	AccountKeyURL       = "url"
	AccountKeyURLDirect = "url_direct"
)

Account map keys common to all bindings. AccountKeyURL is the connection URL apps use (with the localhost binding hostname applied when apps run in containers); AccountKeyURLDirect keeps the service hostname and is used for server-side connections like RunCommand.

View Source
const BindingHostnameDisable = "disable"
View Source
const BindingIDPrefix = "bnd_"

BindingIDPrefix is the prefix of binding ids as generated by the server. Bindings strip it when deriving account object names to stay within database identifier length limits.

View Source
const (
	GrantTargetAll = "*"
)
View Source
const PluginName = "binding"

PluginName is the go-plugin dispense name for the binding provider plugin.

View Source
const ProtocolVersion = 1

ProtocolVersion is the go-plugin protocol version for the v1 binding provider protocol. Incompatible protocol changes bump this and are served side by side through VersionedPlugins during a transition.

Variables

View Source
var Handshake = plugin.HandshakeConfig{
	ProtocolVersion:  ProtocolVersion,
	MagicCookieKey:   "OPENRUN_BINDING_PROVIDER",
	MagicCookieValue: "5c3f7a1e-openrun-binding-provider",
}

Handshake is the go-plugin handshake shared by the server and providers. The magic cookie is a sanity check that the launched executable is a binding provider, not a security measure.

Functions

func AccountName

func AccountName(prefixProd, prefixStg, bindingId string, isStaging bool, opts NameOptions) (string, error)

AccountName builds an account object name (user/role/schema/login) from the binding id: the prod or staging prefix followed by the binding id with its bnd_ prefix stripped (unless KeepIDPrefix is set).

func AccountURLs

func AccountURLs(adminURL, user, password, bindingHostname string) (accountURL, accountDirectURL string, err error)

AccountURLs builds the account connection URL pair from the admin URL: url has the binding hostname applied (empty or "disable" leaves the host unchanged), urlDirect always keeps the service hostname.

func DiffGrants

func DiffGrants(currentGrants []BindingGrant, newGrants []BindingGrant) ([]BindingGrant, []BindingGrant)

DiffGrants returns the grants to revoke (in currentGrants but not newGrants) and the grants to apply (in newGrants but not currentGrants).

func IsLocalBindingHost

func IsLocalBindingHost(host string) bool

IsLocalBindingHost reports whether host refers to the local host.

func RandomHex

func RandomHex(n int) (string, error)

RandomHex returns n random bytes hex-encoded, for generated account passwords.

func RevokeThenRegrant

func RevokeThenRegrant(revokes, regrants []BindingGrant,
	perms func(op string, grants []BindingGrant) error) error

RevokeThenRegrant is the RevokeGrants scaffolding for incremental bindings: execute the revokes, then re-apply the grants that must remain, because a revoke at the same scope removes privileges the remaining grants still need (e.g. revoking full:t1 while read:t1 remains drops the shared SELECT on t1). perms executes one grant or revoke batch; op is "grant" or "revoke".

func Serve

func Serve(config *ServeConfig)

Serve runs the provider plugin. It is called from a provider executable's main function and blocks until the server side closes the plugin. The OPENRUN_PROVIDER_LOG_LEVEL environment variable (set by the server from its own log level) controls provider log verbosity.

func ServiceConfigWithLocalhostBindingHostname

func ServiceConfigWithLocalhostBindingHostname(serviceConfig map[string]string, serviceURL string, runtime ServiceBindingRuntime) map[string]string

ServiceConfigWithLocalhostBindingHostname returns the service config with binding_hostname defaulted to the runtime's localhost binding hostname when the service URL points at localhost and no explicit binding_hostname is set.

func SetURLHostname

func SetURLHostname(u *url.URL, hostname string)

SetURLHostname replaces the hostname in u, preserving the port. A hostname of "" or "disable" leaves the URL unchanged.

func VerifyKeys

func VerifyKeys(inputKeys []string, requiredKeys []string, optionalKeys []string) error

VerifyKeys validates a service config's keys against the binding's required and optional key lists.

Types

type Artifact

type Artifact struct {
	Type ArtifactType
	Name string
}

Artifact identifies one object created on the service by GenerateAccount, such as a role/schema (postgres) or user/database (mysql). The caller tracks the created artifacts and passes them back to DeleteArtifact to undo the creation on rollback.

type ArtifactType

type ArtifactType string
const (
	ArtifactRole     ArtifactType = "role"
	ArtifactSchema   ArtifactType = "schema"
	ArtifactUser     ArtifactType = "user"
	ArtifactDatabase ArtifactType = "database"
	ArtifactLogin    ArtifactType = "login" // SQL Server server-level login backing a database user
)

type BindingGrant

type BindingGrant struct {
	GrantType   GrantType `json:"grant_type"`
	GrantTarget string    `json:"grant_target"`
}

func ParseGrant

func ParseGrant(grant string, supportedGrantTypes []GrantType) (BindingGrant, error)

ParseGrant parses a "type:target" grant string, verifying the type against the service's supported grant types.

func ParseGrants

func ParseGrants(grants []string, supportedGrantTypes []GrantType) ([]BindingGrant, error)

ParseGrants parses a list of "type:target" grant strings.

func SubtractGrants

func SubtractGrants(list, remove []BindingGrant) []BindingGrant

SubtractGrants returns the grants in list that are not in remove, preserving order.

func UnionGrants

func UnionGrants(base, extra []BindingGrant) []BindingGrant

UnionGrants returns base plus any grants from extra not already present, preserving order.

func (BindingGrant) String

func (g BindingGrant) String() string

type BindingMetadata

type BindingMetadata struct {
	Grants        []string          `json:"grants"`
	GrantsApplied []BindingGrant    `json:"grants_applied"`
	Config        map[string]string `json:"config"`
	Account       map[string]string `json:"account,omitempty"`
	ApplyInfo     []byte            `json:"apply_info"`
}

BindingMetadata is the metadata of one binding entry, as stored by the OpenRun server and passed to provider calls.

type Builder

type Builder func() ServiceBinding

Builder creates a new, uninitialized ServiceBinding instance.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the typed gRPC client for the provider protocol, dispensed by go-plugin. Server code uses Provider, which wraps process lifecycle around it.

func (*Client) ApplyGrants

func (c *Client) ApplyGrants(ctx context.Context, account map[string]string,
	bindingMetadata, derivedFromMetadata BindingMetadata, reapplyAll bool) (GrantApplyResult, error)

func (*Client) CloseService

func (c *Client) CloseService(ctx context.Context) error

func (*Client) DeleteArtifact

func (c *Client) DeleteArtifact(ctx context.Context, artifact Artifact) error

func (*Client) Describe

func (c *Client) Describe(ctx context.Context) (string, []ServiceTypeInfo, error)

func (*Client) GenerateAccount

func (c *Client) GenerateAccount(ctx context.Context, bindingId, bindingPath string, bindingMetadata BindingMetadata,
	derivedFromMetadata *BindingMetadata, isStaging bool) (map[string]string, []Artifact, error)

func (*Client) GetAccountEnv

func (c *Client) GetAccountEnv(ctx context.Context, serviceType string) ([]string, []string, error)

func (*Client) InitializeService

func (c *Client) InitializeService(ctx context.Context, serviceType string, serviceConfig map[string]string, runtime ServiceBindingRuntime) error

func (*Client) RevokeGrants

func (c *Client) RevokeGrants(ctx context.Context, account map[string]string,
	derivedFromMetadata BindingMetadata, revokes, regrants []BindingGrant) error

func (*Client) RunCommand

func (c *Client) RunCommand(ctx context.Context, bindingMetadata BindingMetadata, command string) (map[string]any, error)

type GrantApplyResult

type GrantApplyResult struct {
	// GrantsApplied is the set of grants now in effect on the service for the
	// account, to be recorded in the binding metadata. Grants pending revoke are
	// still included; the caller removes them from the metadata after RevokeGrants
	// succeeds.
	GrantsApplied []BindingGrant
	// Granted lists the grants newly applied on the service by this call. If the
	// caller's metadata transaction is rolled back, these are the grants to
	// compensate via RevokeGrants.
	Granted []BindingGrant
	// PendingRevokes lists grants that are applied on the service but no longer
	// desired. The caller executes them via RevokeGrants after its metadata
	// transaction commits.
	PendingRevokes []BindingGrant
}

GrantApplyResult is the outcome of ApplyGrants. ApplyGrants only executes the additive part of a grant change; revokes are computed but not executed, so a caller can defer them until after its metadata transaction commits (a running app may see extra grants during the operation, but never loses a grant from an operation that is later rolled back).

func ApplyGrantsIncremental

func ApplyGrantsIncremental(bindingMetadata BindingMetadata, supportedGrantTypes []GrantType, reapplyAll bool,
	apply func(grants []BindingGrant) ([]BindingGrant, error)) (GrantApplyResult, error)

ApplyGrantsIncremental is the ApplyGrants scaffolding for bindings that execute grant changes incrementally (SQL databases): parse the desired grants, diff them against the applied grants, execute only the new ones through apply, and assemble the GrantApplyResult bookkeeping the server's commit/rollback machinery depends on.

apply executes the given grants on the service and returns the grants that were actually processed (a grant may be skipped, e.g. when its target table does not exist yet; it is then retried on a later reapplyAll). Grants no longer desired are never executed here: they are returned in PendingRevokes for the caller to run via RevokeGrants after its metadata transaction commits.

func ApplyGrantsRebuild

func ApplyGrantsRebuild(bindingMetadata BindingMetadata, supportedGrantTypes []GrantType,
	rebuild func(grantsApplied []BindingGrant) error) (GrantApplyResult, error)

ApplyGrantsRebuild is the ApplyGrants scaffolding for bindings that replace the account's whole permission set atomically (redis ACL rules, mongodb role arrays): the desired state is the union of the applied and desired grants (revokes are deferred), and rebuild replaces the account's permissions with exactly that set.

type GrantType

type GrantType string
const (
	GrantTypeRead   GrantType = "READ"
	GrantTypeCreate GrantType = "CREATE"
	GrantTypeFull   GrantType = "FULL"
)

type LaunchConfig

type LaunchConfig struct {
	// ExecPath is the provider executable.
	ExecPath string
	// Logger receives go-plugin lifecycle logs and the provider's forwarded log
	// lines. Nil uses go-plugin's default (stderr).
	Logger hclog.Logger
	// LogLevel is passed to the provider via OPENRUN_PROVIDER_LOG_LEVEL.
	LogLevel string
	// SecureConfig, when set, verifies the executable's checksum before launch.
	SecureConfig *plugin.SecureConfig
}

LaunchConfig configures launching a provider process.

type Logger

type Logger struct {
	*zerolog.Logger
}

Logger is the logger passed to provider implementations. It mirrors the OpenRun server's logger type (a zerolog wrapper) so binding code written against the server's internal interface ports without changes.

func NewLogger

func NewLogger(level string) *Logger

NewLogger returns a plain stderr logger at the given level ("WARN", "INFO", "DEBUG", "TRACE"; anything else means INFO). Intended for provider tests; provider processes get their logger from Serve.

type NameOptions

type NameOptions struct {
	// MaxLen rejects generated names longer than this; 0 disables the check.
	MaxLen int
	// Uppercase upper-cases the generated name (Oracle-style identifiers).
	Uppercase bool
	// KeepIDPrefix keeps the bnd_ prefix of the binding id instead of
	// stripping it (used where the full id fits the identifier budget).
	KeepIDPrefix bool
}

NameOptions controls AccountName generation.

type Provider

type Provider struct {
	// contains filtered or unexported fields
}

Provider is a running provider process, launched by the OpenRun server. Methods mirror the ServiceBinding interface using SDK types; the server converts to its internal types. Application-level failures are returned as *ProviderError; any other error is a transport failure (the process died or broke protocol) and the caller may respawn the provider and retry.

func LaunchProvider

func LaunchProvider(config LaunchConfig) (*Provider, error)

LaunchProvider starts the provider executable and completes the go-plugin handshake. The returned Provider must be closed with Kill.

func (*Provider) ApplyGrants

func (p *Provider) ApplyGrants(ctx context.Context, account map[string]string,
	bindingMetadata, derivedFromMetadata BindingMetadata, reapplyAll bool) (GrantApplyResult, error)

func (*Provider) CloseService

func (p *Provider) CloseService(ctx context.Context) error

func (*Provider) DeleteArtifact

func (p *Provider) DeleteArtifact(ctx context.Context, artifact Artifact) error

func (*Provider) Describe

func (p *Provider) Describe(ctx context.Context) (string, []ServiceTypeInfo, error)

func (*Provider) Exited

func (p *Provider) Exited() bool

Exited reports whether the provider process has exited.

func (*Provider) GenerateAccount

func (p *Provider) GenerateAccount(ctx context.Context, bindingId, bindingPath string, bindingMetadata BindingMetadata,
	derivedFromMetadata *BindingMetadata, isStaging bool) (map[string]string, []Artifact, error)

func (*Provider) GetAccountEnv

func (p *Provider) GetAccountEnv(ctx context.Context, serviceType string) ([]string, []string, error)

func (*Provider) InitializeService

func (p *Provider) InitializeService(ctx context.Context, serviceType string, serviceConfig map[string]string, runtime ServiceBindingRuntime) error

func (*Provider) Kill

func (p *Provider) Kill()

Kill terminates the provider process.

func (*Provider) RevokeGrants

func (p *Provider) RevokeGrants(ctx context.Context, account map[string]string,
	derivedFromMetadata BindingMetadata, revokes, regrants []BindingGrant) error

func (*Provider) RunCommand

func (p *Provider) RunCommand(ctx context.Context, bindingMetadata BindingMetadata, command string) (map[string]any, error)

type ProviderError

type ProviderError struct {
	Message string
}

ProviderError is an application-level error reported by a provider: the provider ran and returned a failure. Transport-level failures (provider crashed, protocol error) are returned as ordinary gRPC errors instead, and may be retried by the server after respawning the provider.

func (*ProviderError) Error

func (e *ProviderError) Error() string

type ServeConfig

type ServeConfig struct {
	// Bindings maps each service type served by this provider to its builder.
	Bindings map[string]Builder

	// TypeInfo optionally describes each service type's grant types and config
	// schema, keyed by service type, reported to the server via Describe.
	TypeInfo map[string]ServiceTypeInfo

	// ProviderVersion is the provider's release version, reported via Describe.
	ProviderVersion string
}

ServeConfig configures a binding provider process.

type ServiceBinding

type ServiceBinding interface {
	// GetAccountEnv returns the names of the env values included in the
	// account info for this binding: the always-present params first, then the
	// optional params. This is static info: it must be callable on an
	// uninitialized instance, before InitializeService.
	GetAccountEnv(ctx context.Context) ([]string, []string, error)

	// Initialize the service with the given config. This is called when the service binding is created.
	InitializeService(ctx context.Context, logger *Logger, serviceConfig map[string]string, runtime ServiceBindingRuntime) error

	// Close the service connection. This is called when the service binding is no longer needed.
	CloseService(ctx context.Context) error

	// Generate the account based on the binding config. This is called once when the binding is created, after the service is initialized.
	// The account and its backing artifacts (role/schema, user/database) are created on the endpoint specified in the service config
	// and are persisted immediately. The artifacts that were created are returned in creation order; pre-existing objects that the
	// account merely references (like the base binding's schema for a derived binding) must not be included. If creation fails
	// partway and already-created artifacts cannot be rolled back internally, they are returned along with the error so the
	// caller can clean them up.
	GenerateAccount(ctx context.Context, bindingId, bindingPath string, bindingMetadata BindingMetadata,
		derivedFromMetadata *BindingMetadata, isStaging bool) (map[string]string, []Artifact, error)

	// Delete one artifact previously reported as created by GenerateAccount. The caller only passes back artifacts
	// created during the current operation; the implementation must delete only the named artifact.
	DeleteArtifact(ctx context.Context, artifact Artifact) error

	// Apply the grants to the account. This is called when the binding is created, after the account is generated.
	// It can be called again if the grants are changed. Only new grants are executed (and persisted immediately);
	// grants that need to be removed are returned in PendingRevokes without being executed, for the caller to run
	// via RevokeGrants once its metadata transaction commits.
	ApplyGrants(ctx context.Context, account map[string]string,
		bindingMetadata, derivedFromMetadata BindingMetadata, reapplyAll bool) (GrantApplyResult, error)

	// Revoke the given grants from the account, then re-apply the regrants. Called with the PendingRevokes of an
	// earlier ApplyGrants after the caller's metadata transaction commits (regrants = the grants that remain
	// desired), or with the Granted list to compensate when the transaction is rolled back (regrants = the grants
	// that were applied before the operation). The regrants restore privileges that an overlapping revoke removes
	// (e.g. revoking read:t1 while read:* remains would otherwise drop SELECT on t1). Revoking a grant that is not
	// currently applied must be harmless.
	RevokeGrants(ctx context.Context, account map[string]string,
		derivedFromMetadata BindingMetadata, revokes, regrants []BindingGrant) error

	// Run a command on the endpoint specified in the service config as the binding account.
	RunCommand(ctx context.Context, bindingMetadata BindingMetadata, command string) (map[string]any, error)
}

ServiceBinding is the interface a binding provider implements for each service type it serves. The method contracts match the OpenRun server's internal service binding interface; see the method comments for the semantics the server relies on.

type ServiceBindingRuntime

type ServiceBindingRuntime struct {
	// LocalhostBindingHostname is the hostname apps should use to reach a
	// service running on the server host (e.g. host.docker.internal when apps
	// run in containers). Empty when apps run directly on the host.
	LocalhostBindingHostname string
}

ServiceBindingRuntime carries server-side runtime settings a binding may need when connecting to the service.

type ServiceTypeInfo

type ServiceTypeInfo struct {
	ServiceType         string
	SupportedGrantTypes []GrantType
	RequiredConfigKeys  []string
	OptionalConfigKeys  []string
}

ServiceTypeInfo describes one service type served by a provider, reported to the server through the Describe RPC.

Directories

Path Synopsis
Package bindingtest provides test helpers for binding provider tests.
Package bindingtest provides test helpers for binding provider tests.
Package sqlbinding provides helpers for bindings backed by database/sql drivers: service initialization, the RunCommand implementation, and identifier quoting primitives.
Package sqlbinding provides helpers for bindings backed by database/sql drivers: service initialization, the RunCommand implementation, and identifier quoting primitives.

Jump to

Keyboard shortcuts

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