Documentation
¶
Overview ¶
Package resourceapi is a pure-Go (no cgo) port of the core of Puppet's puppet-resource_api gem — the modern type/provider API used to describe and manage resources.
It provides three cooperating pieces that mirror the gem:
Type definition and registration. A Definition describes a resource type: its name, its typed [Attribute]s (each carrying a Pcore type expression, an optional default, a documentation string and a Behaviour), title patterns, features and the auto-relation maps (autorequire/autobefore/autonotify/autosubscribe). Compile validates a definition and turns it into a ready-to-use Type; RegisterType does the same and stores the result in a package-global Registry (a private Registry can be built with NewRegistry for isolated use).
Instance validation. Type.Validate takes a desired-state resource hash, derives missing namevars from the title (directly for a single namevar, or via the compiled [TitlePattern]s), applies defaults, runs the per-attribute munge seam, checks every value against its declared Pcore type (using github.com/go-pcore/pcore) and runs the per-attribute custom validate seam. It rejects unknown attributes, missing namevars and any attempt to manage a read_only attribute, producing typed ValidationError values whose messages track the gem where reasonable.
The provider protocol. A Provider implements the get(context) -> []instance / set(context, changes) contract against a Context (logging, the owning Type and feature checks). [Apply] drives a full run: it calls Get for the current state, validates and canonicalizes desired against current, computes the per-title Change set honoring ensure and the init_only behaviour and any custom_insync hook, then calls Set. SimpleProvider is the base that translates that change set into create/update/delete calls on a CrudProvider, deciding each from the ensure values exactly like the gem's SimpleProvider.
Transport / device support. RegisterTransport compiles a TransportSchema (typed connection_info attributes) into a Transport that validates connection info like a type and opens a Connection through a host-side connect seam; NewDeviceContext hands a remote_resource provider that connection through Context.Device.
The feature flags the gem acts on are all honored: canonicalize, custom_insync (the per-property Definition.CustomInsync comparison seam that overrides the default deep-equal), simple_get_filter (a filtered FilterProvider.GetFiltered fetch of only the managed titles), supports_noop (a noop-aware NoopProvider.SetNoop dispatch) and remote_resource. Attributes declared Sensitive are wrapped in *Sensitive after validation so logs and error messages redact them (Type.Redact).
The package is a pure library: it embeds no Ruby runtime. The interpreter facing hooks (munge, validate, canonicalize, custom_insync, the transport connect seam and the provider itself) are Go func and interface seams that a consumer such as go-embedded-ruby (rbgo) wires to Ruby blocks; executing the Ruby bodies of those blocks is that binding layer's job. Everything the gem specifies as behaviour — title-pattern resolution, the validate/apply pipeline ordering, the change model and the feature flags — lives here.
Index ¶
- Constants
- type Attribute
- type Behaviour
- type Change
- type Connection
- type Context
- func (c *Context) Debug(msg string)
- func (c *Context) Device() Connection
- func (c *Context) Err(msg string)
- func (c *Context) Feature(name string) bool
- func (c *Context) HasDevice() bool
- func (c *Context) Info(msg string)
- func (c *Context) Log(level LogLevel, msg string)
- func (c *Context) Noop() bool
- func (c *Context) Notice(msg string)
- func (c *Context) SetNoop(noop bool) *Context
- func (c *Context) Transport() *Transport
- func (c *Context) Type() *Type
- func (c *Context) Warning(msg string)
- type CrudProvider
- type Definition
- type DefinitionError
- type DiscardLogger
- type FilterProvider
- type FilteredCrud
- type LogLevel
- type Logger
- type NoopProvider
- type Provider
- type Registry
- func (r *Registry) Get(name string) (*Type, bool)
- func (r *Registry) GetTransport(name string) (*Transport, bool)
- func (r *Registry) Names() []string
- func (r *Registry) Register(d Definition) (*Type, error)
- func (r *Registry) RegisterTransport(s TransportSchema) (*Transport, error)
- func (r *Registry) TransportNames() []string
- type Resource
- type Sensitive
- type SimpleProvider
- type Summary
- type TitlePattern
- type Transport
- type TransportSchema
- type Type
- func (t *Type) Apply(ctx *Context, p Provider, desired []Resource) (Summary, error)
- func (t *Type) AttributeNames() []string
- func (t *Type) Canonicalize(ctx *Context, resources []Resource) ([]Resource, error)
- func (t *Type) Canonicalizes() bool
- func (t *Type) CustomInsyncs() bool
- func (t *Type) Definition() Definition
- func (t *Type) HasFeature(name string) bool
- func (t *Type) Name() string
- func (t *Type) Namevars() []string
- func (t *Type) ParseTitle(title string) (map[string]string, error)
- func (t *Type) Redact(r Resource) Resource
- func (t *Type) RemoteResource() bool
- func (t *Type) SensitiveAttributes() []string
- func (t *Type) SimpleGetFilter() bool
- func (t *Type) SupportsNoop() bool
- func (t *Type) Title(r Resource) (string, error)
- func (t *Type) Validate(input Resource) (Resource, error)
- type ValidationError
Constants ¶
const ( Present = "present" Absent = "absent" )
Ensure values.
const ( // FeatureCanonicalize enables the [Definition.Canonicalize] hook. FeatureCanonicalize = "canonicalize" // FeatureCustomInsync enables the [Definition.CustomInsync] hook. FeatureCustomInsync = "custom_insync" // FeatureSimpleGetFilter lets [Type.Apply] fetch only the managed names via // a [FilterProvider]. FeatureSimpleGetFilter = "simple_get_filter" // FeatureSupportsNoop lets [Type.Apply] hand the noop flag to a // [NoopProvider] instead of skipping Set itself. FeatureSupportsNoop = "supports_noop" // FeatureRemoteResource marks a type managed over a transport/device // connection rather than the local host. FeatureRemoteResource = "remote_resource" )
Feature names recognised by the gem. Unknown feature names are still accepted (the gem only warns); these constants name the ones this package acts on.
const EnsureAttr = "ensure"
EnsureAttr is the conventional name of the ensure attribute; when a type declares it, [Apply] uses its value ("present"/"absent") to decide between create/update and delete.
const RedactedString = "[redacted]"
RedactedString is the placeholder substituted for a sensitive value whenever it is rendered for humans (logs, error messages, [Redact]). It mirrors the text Puppet uses for Puppet::Pops::Types::PSensitiveType::Sensitive.
const TitleKey = "title"
TitleKey is the reserved key under which a resource title is supplied.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Attribute ¶
type Attribute struct {
// Type is a Pcore type expression, e.g. "String", "Integer[0,150]" or
// "Enum['present','absent']". It is required and is validated at compile
// time.
Type string
// Desc documents the attribute.
Desc string
// Default is the value substituted when the attribute is absent from a
// desired resource. It is applied only when HasDefault is true, so that a
// nil default can be distinguished from "no default".
Default any
// HasDefault enables Default.
HasDefault bool
// Behaviour selects how the attribute participates in management.
Behaviour Behaviour
// Munge, when set, transforms a raw value before type validation. It is the
// seam a Ruby munge block binds to.
Munge func(any) (any, error)
// Validate, when set, runs custom validation after type validation. It is
// the seam a Ruby validate block binds to.
Validate func(any) error
// Sensitive marks an attribute whose value must never be revealed by
// rendering. [Type.Validate] wraps a present sensitive value in [*Sensitive]
// after munge/type-check/validate, so logs and error messages redact it,
// mirroring the gem's `sensitive: true` option.
Sensitive bool
}
Attribute describes a single attribute of a resource type.
type Behaviour ¶
type Behaviour string
Behaviour classifies how an Attribute participates in management. It mirrors the gem's :behaviour option.
const ( // Property is the default: a managed, readable and writable attribute. Property Behaviour = "" // Namevar uniquely identifies an instance and must be supplied. Namevar Behaviour = "namevar" // ReadOnly may be reported by a provider but never managed; supplying it in // a desired resource is an error. ReadOnly Behaviour = "read_only" // Parameter is data used by the provider but not enforced on the target and // never fetched back. Parameter Behaviour = "parameter" // InitOnly may only be set when the resource is created; changing it on an // existing resource is an error. InitOnly Behaviour = "init_only" )
type Change ¶
Change is one entry in the set of changes handed to Provider.Set, keyed by title. Is is the current state (nil when the resource is absent) and Should is the desired state (nil when the resource should be absent).
type Connection ¶
type Connection = any
Connection is a live transport connection object. It is opaque to this package: the actual I/O is host-side, so a consumer (rbgo, a device provider) supplies whatever concrete type it likes. It mirrors the object the gem's context.device returns.
type Context ¶
type Context struct {
// contains filtered or unexported fields
}
Context is handed to a Provider's Get and Set. It exposes the owning type, feature checks, logging, the noop flag and — for a device/transport provider — the transport connection, mirroring Puppet::ResourceApi::BaseContext and its device subclass.
func NewContext ¶
NewContext builds a Context for the given type. A nil logger is replaced with DiscardLogger.
func NewDeviceContext ¶
func NewDeviceContext(t *Type, logger Logger, transport *Transport, conn Connection) *Context
NewDeviceContext builds a Context bound to a transport and its live connection, mirroring the device-provider context the gem hands a remote_resource provider. conn is the host-side connection object (opaque to this package). A nil logger is replaced with DiscardLogger.
func (*Context) Device ¶
func (c *Context) Device() Connection
Device returns the live transport connection, or nil for a local run. It mirrors the gem's context.device.
func (*Context) Transport ¶
Transport returns the transport schema the context is bound to, or nil for a local run.
type CrudProvider ¶
type CrudProvider interface {
// Get returns the current state of every managed instance.
Get(ctx *Context) ([]Resource, error)
// Create makes a new resource with the given title and desired state.
Create(ctx *Context, name string, should Resource) error
// Update reconciles an existing resource to the desired state.
Update(ctx *Context, name string, should Resource) error
// Delete removes an existing resource.
Delete(ctx *Context, name string) error
}
CrudProvider is the simpler contract a provider may implement instead; wrap it in a SimpleProvider to obtain a Provider.
type Definition ¶
type Definition struct {
// Name is the resource type name; it must match [a-z][a-z0-9_]*.
Name string
// Desc documents the type.
Desc string
// Attributes maps attribute names to their schemas. At least one attribute
// with the [Namevar] behaviour is required.
Attributes map[string]Attribute
// TitlePatterns decompose a title into namevars when they are not supplied
// explicitly. They are tried in order.
TitlePatterns []TitlePattern
// Features lists optional provider capabilities such as "canonicalize" or
// "simple_get_filter". Unknown feature names are accepted (the gem only
// warns); empty names are rejected.
Features []string
// Autorequire, Autobefore, Autonotify and Autosubscribe map a target
// resource type name to the attribute whose value names the related
// resource, mirroring the gem's auto-relation options.
Autorequire map[string]string
Autobefore map[string]string
Autonotify map[string]string
Autosubscribe map[string]string
// Canonicalize, when set and enabled by the "canonicalize" feature,
// normalises both current and desired resources so they compare equal when
// semantically identical. It is the seam a Ruby canonicalize method binds
// to.
Canonicalize func(ctx *Context, resources []Resource) ([]Resource, error)
// CustomInsync, when set and enabled by the "custom_insync" feature, decides
// per property whether the current value (is) already matches the desired
// value (should), overriding the default deep-equal comparison. It is called
// once per property that would otherwise be compared, with the full is and
// should hashes and the property name. It returns insync (true when the
// property needs no change) and handled (false to fall through to the
// default comparison, mirroring a Ruby insync? block returning nil). It is
// the seam a Ruby provider insync? method binds to.
CustomInsync func(ctx *Context, name, property string, is, should Resource) (insync, handled bool, err error)
}
Definition is the schema passed to Compile / RegisterType, mirroring the hash accepted by Puppet::ResourceApi.register_type.
type DefinitionError ¶
type DefinitionError struct {
// Type is the name of the offending definition (may be empty if the name
// itself is invalid).
Type string
// Msg explains what is wrong with the schema.
Msg string
}
DefinitionError reports a malformed type Definition rejected by Compile. The gem raises Puppet::DevError for the equivalent schema problems.
func (*DefinitionError) Error ¶
func (e *DefinitionError) Error() string
type FilterProvider ¶
FilterProvider is the optional get-with-names contract a Provider may also satisfy. When the type declares the simple_get_filter feature, [Apply] calls GetFiltered with the titles it is about to manage instead of Get, mirroring the gem's my_provider.get(context, names).
type FilteredCrud ¶
type FilteredCrud interface {
CrudProvider
GetFiltered(ctx *Context, names []string) ([]Resource, error)
}
FilteredCrud is a CrudProvider that also supports fetching only the named instances; a SimpleProvider wrapping one exposes SimpleProvider.GetFiltered against it.
type Logger ¶
Logger receives log lines from a provider via its Context. It is the seam a Ruby logger binds to.
type NoopProvider ¶
NoopProvider is the optional noop-aware set contract a Provider may satisfy. When the type declares the supports_noop feature, [Apply] calls SetNoop with the context's noop flag instead of Set, mirroring the gem's my_provider.set(context, changes, noop:).
type Provider ¶
type Provider interface {
// Get returns the current state of every managed instance.
Get(ctx *Context) ([]Resource, error)
// Set applies the given changes, keyed by title.
Set(ctx *Context, changes map[string]Change) error
}
Provider is the get/set contract every provider implements.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds compiled types by name, mirroring Puppet's global type registry. It is safe for concurrent use.
func (*Registry) GetTransport ¶
GetTransport returns the registered transport with the given name.
func (*Registry) Register ¶
func (r *Registry) Register(d Definition) (*Type, error)
Register compiles d and stores the resulting Type, returning an error if the definition is invalid or a type with the same name is already registered.
func (*Registry) RegisterTransport ¶
func (r *Registry) RegisterTransport(s TransportSchema) (*Transport, error)
RegisterTransport compiles s and stores the resulting Transport, returning an error if the schema is invalid or a transport with the same name is already registered.
func (*Registry) TransportNames ¶
TransportNames returns the registered transport names in sorted order.
type Resource ¶
Resource is a resource instance represented as an attribute-name to value map, mirroring the Ruby hash that flows through the gem. The special key "title" carries the resource title when the namevar(s) are not given explicitly.
type Sensitive ¶
type Sensitive struct {
// contains filtered or unexported fields
}
Sensitive wraps a value whose content must never be revealed by String or by ordinary rendering, mirroring the gem's automatic wrapping of attributes declared with `sensitive: true` (Puppet's Sensitive data type). The wrapped value stays reachable through Sensitive.Unwrap for the provider code that legitimately needs it. Equality (see [equalAny]) compares the unwrapped values, so a sensitive attribute still detects real changes without leaking its content.
func NewSensitive ¶
NewSensitive wraps v. Wrapping an already-Sensitive value returns it unchanged so double-wrapping is a no-op, matching the gem.
type SimpleProvider ¶
type SimpleProvider struct {
// Crud is the wrapped provider.
Crud CrudProvider
}
SimpleProvider adapts a CrudProvider to the Provider interface by turning each Change into a create, update or delete decided from the ensure values of the current and desired states, exactly like the gem's Puppet::ResourceApi::SimpleProvider: absent->present creates, present->present updates and present->absent deletes; absent->absent is a no-op.
func (SimpleProvider) Get ¶
func (s SimpleProvider) Get(ctx *Context) ([]Resource, error)
Get delegates to the wrapped provider.
func (SimpleProvider) GetFiltered ¶
func (s SimpleProvider) GetFiltered(ctx *Context, names []string) ([]Resource, error)
GetFiltered delegates to the wrapped provider's filtered get when it supports one, otherwise falls back to a full Get. It lets a SimpleProvider satisfy FilterProvider for the simple_get_filter feature.
type Summary ¶
type Summary struct {
Created []string
Updated []string
Deleted []string
Unchanged []string
// Changes is the exact change set handed to the provider's Set.
Changes map[string]Change
}
Summary reports what an [Apply] run did. Counts are keyed by the action.
type TitlePattern ¶
type TitlePattern struct {
// Pattern is a Go regular expression with named groups.
Pattern string
// Desc documents the pattern.
Desc string
}
TitlePattern maps a resource title onto namevar values via a regular expression with named capture groups; each group name must be a declared attribute.
type Transport ¶
type Transport struct {
// contains filtered or unexported fields
}
Transport is a compiled, validated transport schema. It validates connection_info like a resource type and opens connections through the schema's connect seam. It is safe for concurrent use.
func CompileTransport ¶
func CompileTransport(s TransportSchema) (*Transport, error)
CompileTransport validates a TransportSchema and returns the corresponding Transport. It returns a *DefinitionError for any schema problem.
func LookupTransport ¶
LookupTransport returns a transport from the package-global registry.
func RegisterTransport ¶
func RegisterTransport(s TransportSchema) (*Transport, error)
RegisterTransport compiles s and registers it in the package-global registry.
func (*Transport) Connect ¶
func (tr *Transport) Connect(ctx *Context, info Resource) (Connection, error)
Connect validates connection_info and opens a Connection through the schema's connect seam. The returned context (via NewDeviceContext) is how a remote_resource provider reaches the device. Connect returns a *ValidationError when the schema declares no connect seam.
func (*Transport) ConnectionInfoNames ¶
ConnectionInfoNames returns the connection attribute names in the schema's declared order when given, else sorted.
func (*Transport) Validate ¶
Validate checks connection_info against the transport schema and returns a fully-populated copy: Bolt-injected keys are stripped, defaults applied, munge and validate seams run, every value type-checked and sensitive values wrapped. It mirrors the gem's transport validate step and rejects unknown attributes.
type TransportSchema ¶
type TransportSchema struct {
// Name is the transport name; it must match [a-z][a-z0-9_]*.
Name string
// Desc documents the transport. It is required, like the gem's :desc.
Desc string
// ConnectionInfo maps a connection attribute name to its schema (type,
// default, munge/validate seams, sensitive flag). At least one is required.
ConnectionInfo map[string]Attribute
// ConnectionInfoOrder is the preferred order of the connection attributes;
// when empty it defaults to sorted attribute names. Every entry must be a
// declared connection attribute.
ConnectionInfoOrder []string
// Connect is the host-side seam that opens a [Connection] from validated
// connection_info. It is the seam a Ruby transport class binds to; the
// package itself performs no I/O.
Connect func(ctx *Context, info Resource) (Connection, error)
}
TransportSchema is the schema passed to RegisterTransport, mirroring the hash accepted by Puppet::ResourceApi::Transport.register. It describes how to reach a remote device: a set of typed connection_info attributes, their preferred order and a host-side connect seam.
type Type ¶
type Type struct {
// contains filtered or unexported fields
}
Type is a compiled, validated resource type. It is safe for concurrent use.
func Compile ¶
func Compile(d Definition) (*Type, error)
Compile validates a Definition and returns the corresponding Type. It returns a *DefinitionError for any schema problem.
func RegisterType ¶
func RegisterType(d Definition) (*Type, error)
RegisterType compiles d and registers it in the package-global registry.
func (*Type) Apply ¶
Apply drives a full management run for the desired resources against provider p: it validates and keys desired by title, fetches current state (via a filtered get when simple_get_filter is declared), canonicalizes both sides, computes the change set honoring ensure, the init_only behaviour and any custom_insync hook, hands it to p.Set (or SetNoop under supports_noop, or nothing under a plain noop run) and returns a Summary.
func (*Type) AttributeNames ¶
AttributeNames returns the attribute names in sorted order.
func (*Type) Canonicalize ¶
Canonicalize runs the type's Definition.Canonicalize hook over resources when the canonicalize feature is declared and a hook is set, returning the normalised resources; otherwise it returns resources unchanged. It is the public entry point mirroring the gem's my_provider.canonicalize call.
func (*Type) Canonicalizes ¶
Canonicalizes reports whether the type both declares the canonicalize feature and supplies a hook.
func (*Type) CustomInsyncs ¶
CustomInsyncs reports whether the type both declares the custom_insync feature and supplies a hook.
func (*Type) Definition ¶
func (t *Type) Definition() Definition
Definition returns a copy of the source definition.
func (*Type) HasFeature ¶
HasFeature reports whether the named feature is declared on the type.
func (*Type) ParseTitle ¶
ParseTitle decomposes a resource title into namevar values, mirroring Puppet::ResourceApi's title-pattern resolution. When the type declares Definition.TitlePatterns each pattern is tried in order and the named captures of the first one that matches become the returned attribute values; if none match, a *ValidationError is returned, exactly as the gem raises when no set of title patterns matches. With no declared patterns a single-namevar type maps the whole title to its namevar (the gem's default [[/(.*)/m, [[namevar]]]] pattern) and a multi-namevar type is an error.
func (*Type) Redact ¶
Redact returns a shallow copy of r in which every attribute the type declares sensitive — and every value already wrapped in *Sensitive — is replaced by RedactedString, so the result is safe to log. r itself is not modified.
func (*Type) RemoteResource ¶
RemoteResource reports whether the type declares the remote_resource feature.
func (*Type) SensitiveAttributes ¶
SensitiveAttributes returns, in sorted order, the names of the attributes the type declares sensitive.
func (*Type) SimpleGetFilter ¶
SimpleGetFilter reports whether the type declares the simple_get_filter feature.
func (*Type) SupportsNoop ¶
SupportsNoop reports whether the type declares the supports_noop feature.
func (*Type) Title ¶
Title derives the resource title from r: the explicit TitleKey if present, otherwise the value of the single namevar. For a multi-namevar type an explicit title is required. A namevar wrapped in *Sensitive is unwrapped.
func (*Type) Validate ¶
Validate checks a desired-state resource against the type and returns a new, fully-populated resource: missing namevars are derived from the title (via the title patterns), missing attributes with defaults are filled, munge seams run, every value is checked against its Pcore type, custom validate seams run and sensitive values are wrapped. It returns a *ValidationError on the first problem.
type ValidationError ¶
type ValidationError struct {
// Type is the resource type name.
Type string
// Attribute is the offending attribute name, or "" for a resource-level
// problem.
Attribute string
// Msg is the human-readable explanation.
Msg string
}
ValidationError reports a resource instance that does not satisfy its type. It mirrors the gem's Puppet::ResourceError family; Attribute names the offending attribute (empty for whole-resource problems such as a missing namevar).
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string