Documentation
¶
Overview ¶
Package configawsssm contributes configuration from an AWS Systems Manager Parameter Store hierarchy as a first-class config layer: precedence, per-key merge, provenance and safe poll-based hot-reload, exactly like a file layer.
A prefix scopes the backend to one hierarchy of the store; the parameter names beneath it, split on "/", become the layer's nested tree. Parameter Store holds strings, so a value is a scalar string by default and the View's typed accessors coerce it (GetInt("server.port") parses "8080"). A StringList becomes a []string leaf; a SecureString is read decrypted, which makes the whole layer Sensitive so the core's leak guard stops a decrypted secret being written into a plain layer beneath. A String value that is itself a JSON or YAML document is decoded into a subtree when a codec is supplied with WithValueCodec — see the config-aws-ssm spec, D3.
The store is reached through the narrow ParamStore interface, which Wrap adapts from a configured *ssm.Client. Injecting the client keeps every credential, Region, endpoint and KMS-key decision with the consumer and lets the whole unit suite run against a fake, needing no AWS account.
v0.1.0 is read-only: Parameter Store has no compare-and-swap write, so the version-at-Load conflict trap the shared conformance suite enforces cannot be honoured, and read-only is a first-class outcome (spec D4, D9). The backend implements config.Backend and config.WatchableBackend but not config.WritableBackend; the Store's routing skips it as a write target.
Index ¶
- Constants
- Variables
- func FromClient(client *ssm.Client, prefix string, opts ...Option) config.Backend
- func FromConfig(cfg aws.Config, prefix string, opts ...Option) (config.Backend, error)
- func New(store ParamStore, prefix string, opts ...Option) config.Backend
- type Option
- type Param
- type ParamStore
Examples ¶
Constants ¶
const SourceKind = config.SourceKind("aws-ssm")
SourceKind is what a layer from this backend reports as, so provenance can distinguish an SSM value from a file or an environment variable — Origin("server.port") renders as "aws-ssm:/app/".
Variables ¶
var ErrNoRegion = errors.NewSentinel("configawsssm.no_region",
"no AWS region in the supplied config; set one, or set AWS_REGION for the ambient rung")
ErrNoRegion reports a configuration that names no AWS region.
A config with no region names a parameter in an account nobody chose, so it is refused here rather than failing at the first request. There is no default: AWS documents none, and inventing one is a different act from adopting a provider's own — which is why go/awsclient refuses too.
Functions ¶
func FromClient ¶
FromClient is the common path: New over a Wrap-ped SSM client, so a consumer writes configawsssm.FromClient(client, "/app") without touching the narrow interface.
Example ¶
ExampleFromClient reads configuration from an AWS SSM Parameter Store hierarchy. You build and configure the SSM client — Region, credentials, endpoint and the KMS key that decrypts a SecureString all stay yours — and hand it in with a prefix that scopes and is stripped from the names, so /app/server/port in Parameter Store reads back as server.port.
package main
import (
"context"
"fmt"
"log"
awscfg "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"gitlab.com/phpboyscout/go/config"
configawsssm "gitlab.com/phpboyscout/go/config-aws-ssm"
)
func main() {
ctx := context.Background()
awsConfig, err := awscfg.LoadDefaultConfig(ctx)
if err != nil {
log.Fatal(err)
}
client := ssm.NewFromConfig(awsConfig)
store, err := config.NewStore(ctx,
config.WithBackend(configawsssm.FromClient(client, "/app")),
)
if err != nil {
log.Fatal(err)
}
fmt.Println(store.View().GetInt("server.port"))
}
Output:
func FromConfig ¶ added in v0.3.0
FromConfig builds the SSM client from an aws.Config the caller resolved — a specific profile, an assumed role, a client pointed at LocalStack — and returns a backend over the prefix.
ssm.NewFromConfig does no I/O, returns no error and needs no close, so this rung constructs eagerly and reaching Parameter Store stays deferred to Load, which has a context to bound it.
Where the ambient rung is ¶
Deliberately NOT here. Resolving the ambient AWS chain costs this module ten further dependencies — sso, ssooidc, sts, imds and the rest — and charging every consumer for a credential graph they may never use would spend the dependency-footprint guarantee this adapter states in its own test.
It lives in the ambient subpackage instead, over go/awsclient, so a consumer who wants it pays for it and a consumer who does not is unaffected:
import ssmambient "gitlab.com/phpboyscout/go/config-aws-ssm/ambient"
b, err := ssmambient.Default("/app")
To share ONE resolved chain across several adapters — and across go/signing and go/encryption — resolve it with go/awsclient and pass the config here (spec 0012 L-4, L-5).
func New ¶
func New(store ParamStore, prefix string, opts ...Option) config.Backend
New returns a backend contributing the Parameter Store hierarchy under prefix as a config layer. store is the injected client — a fake in tests, a Wrap-ped *ssm.Client in production; prefix scopes and is stripped from the names.
Types ¶
type Option ¶
type Option func(*backend)
Option configures a backend.
func WithPollInterval ¶
WithPollInterval sets the watch poll cadence, overriding the 60-second default (D7). A consumer who needs fresher config and knows their rate budget can shorten it; one watching many prefixes can lengthen it. This cadence is what the backend advertises through config.PollIntervalHinter, so a default Store.Watch adopts it in place of the eager 2-second local-file default; an explicit config.WithPollInterval on the Store still wins, and the resolved interval is honoured by the poll loop either way.
func WithValueCodec ¶
WithValueCodec decodes each String value through codec: a value that decodes to a mapping becomes a subtree at its key's path, and a value the codec rejects — a bare scalar, or bytes that are not a document — stays a scalar string, so a prefix mixing flat keys and object blobs reads correctly. Omitted, every value is a scalar string.
codec is any config.Codec — the interface the sibling format adapters implement — so a JSON-blob store is read with configjson.Codec{} and a YAML-blob store with the core's, and this module takes no codec dependency of its own.
The codec is offered only for String values: a StringList is already a list, and a decrypted SecureString is a secret scalar, so decode-or-string stays unambiguous (D3).
type Param ¶
type Param struct {
// Name is the full SSM parameter name, prefix included (e.g. "/app/server/port").
Name string
// Value is the parameter's value: the plain string for a String, the
// comma-separated members for a StringList, and — because the adapter reads
// with decryption — the plaintext for a SecureString.
Type string
// Value holds the parameter's value (see the field doc above); it follows Type
// for gofumpt's field-alignment, not for meaning.
Value string
// Version is the parameter's monotonic per-name version, the poll loop's exact
// change marker (D7) — no value diffing needed.
Version int64
}
Param is one Parameter Store entry, with the version the watch path compares.
type ParamStore ¶
type ParamStore interface {
// GetByPath returns every parameter under prefix — recursive, paginated and
// fully drained into one slice. decrypt requests SecureString plaintext (D3,
// D6). Each [Param] carries the Version the poll loop compares to detect
// change (D7). An absent prefix returns an empty slice and a nil error, not an
// error, so the backend can report it not-there.
GetByPath(ctx context.Context, prefix string, decrypt bool) ([]Param, error)
}
ParamStore is the slice of SSM this adapter uses, behind an interface it owns so a fake drives the unit suite and the real client is adapted by Wrap.
It is read-only in v0.1.0: a single method that both Load and the poll loop use. The write verbs are deliberately absent — they are the deferred write task (spec D9), which will widen this interface when it lands.
func Wrap ¶
func Wrap(client *ssm.Client) ParamStore
Wrap adapts a configured SSM SDK client to the narrow ParamStore interface. The client carries every credential, Region, endpoint and retry decision — and the KMS key that decrypts a SecureString; Wrap adds none — it only translates calls (spec D5, D6).