parameters

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package parameters reads configuration and secrets with a cache in front: the Go side of Powertools' Parameters utility.

The providers live in the packages below this one, one per store:

  • parameters/ssm: Systems Manager Parameter Store, including SecureString decryption, reads by path and by name, and writes;
  • parameters/secrets: Secrets Manager, single and batch reads, writes;
  • parameters/appconfig: AppConfig configuration profiles, through AppConfigData sessions;
  • parameters/dynamodb: a DynamoDB table with Python's schema.

This package holds what they share: the Provider and its cache, the transforms, the options of a read, the errors, and the Store interface for a provider of your own.

Reading a value

A provider is created once (in main, or at package level) and shared by every invocation; it is safe for concurrent use. Its Get method returns a string:

var params = ssm.FromConfig(cfg)

func handle(ctx context.Context, event Event) (string, error) {
	endpoint, err := params.Get(ctx, "/app/endpoint")
	...
}

Values that change shape are read with the generic Get and GetMultiple: the caller names the Go type and the transform decodes the value into it.

cfg, err := parameters.Get[Config](ctx, params, "/app/config",
	parameters.WithTransform(parameters.TransformJSON))

The transforms are Python's: TransformJSON decodes a JSON document into any T; TransformBinary decodes base64 into a []byte (or a string); TransformAuto picks one of those from the suffix of the name ("config.json", "cert.binary"); TransformNone, the default, returns the value as stored, in a string, a []byte or an any. A value read without a transform cannot become a struct or a number: that is a *TransformError telling to use TransformJSON.

Each package below also has the shortcuts of Python's high-level functions, which use a provider built on first use from the default AWS configuration: ssm.GetParameter, ssm.GetParameters, ssm.GetParametersByName, ssm.SetParameter, secrets.GetSecret, secrets.GetSecretsByName, secrets.SetSecret, appconfig.GetAppConfig. ClearCaches forgets those default providers.

Caching

Every value is cached for five minutes, or POWERTOOLS_PARAMETERS_MAX_AGE seconds, or what WithMaxAge says for one read; a max age of zero or less never caches. WithForceFetch reads the store even when the cache holds the value. The cache keeps the value after its transform, apart for each transform, as Python does, and apart for each store option that changes the value read (a decryption, a version), which Python does not. Concurrent reads of a value missing from the cache share one call to the store; a caller whose context ends stops waiting without failing the others. Every read decodes a fresh copy, so callers never share mutable values through the cache.

Your own store

A Store reads values from a backend: Get returns one value, GetMultiple the values under a path. NewProvider puts the cache and the transforms in front of it. Options of your store travel with the common ones through WithStoreOption, and the store reads them with StoreOptions.Apply.

Environment variables

  • POWERTOOLS_PARAMETERS_MAX_AGE: the default max age, in seconds (300).
  • POWERTOOLS_PARAMETERS_SSM_DECRYPT: whether SSM reads decrypt SecureString parameters by default (false).
  • POWERTOOLS_SERVICE_NAME: the default AppConfig application.

They are read on every call, as in Python; an invalid value is reported on standard error and the default applies.

Errors

A value the store cannot provide is a *GetParameterError (ErrGetParameter), which wraps the store's error: errors.As reaches the SDK's own error, for example a *types.ParameterNotFound of SSM. A failed transform is a *TransformError (ErrTransform). Writes fail with a *SetParameterError or a *SetSecretError, a batch of secrets with a *GetSecretError. ErrNotFound marks the values a store without its own not-found error lacks.

Differences from Python

  • The shortcuts live in the provider packages (ssm.GetParameter rather than parameters.get_parameter): this package cannot import them, since they build on it. Powertools for TypeScript has the same layout.
  • Typed reads replace the str | bytes | dict results: Get[T] and GetMultiple[T]. A value whose transform fails in a read of several values is the zero T, where Python gives None.
  • Python's **sdk_options become typed options: ssm.WithDecrypt, ssm.WithRecursive, ssm.WithParameterFilters, secrets.WithVersionID, secrets.WithVersionStage, secrets.WithFilters, appconfig.WithMinimumPollInterval, dynamodb.WithConsistentRead, and request editors for the writes.
  • Fixed: Python's cache ignores the SDK options of a read, so a value read with VersionStage=AWSPREVIOUS answered later reads of the current version, and a decrypted read answered encrypted ones. Here the options that change the value are part of the cache key.
  • Fixed: a read with WithRaiseOnTransformError fails on a value whose transform failed, even when an earlier tolerant read cached it, where Python returns the cached None.
  • Base64 decoding skips characters outside the alphabet, as Python's b64decode does; what follows the padding is ignored.
  • JSON is read as Python's json.loads reads it, repeated object members included (the last one wins), but NaN and Infinity are refused.
  • An invalid POWERTOOLS_PARAMETERS_MAX_AGE falls back to the default instead of raising ValueError.
  • The botocore user agent registration is not ported.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrGetParameter: a value could not be read from its store.
	ErrGetParameter = errors.New("failed to get parameter")
	// ErrTransform: a value was read but its transform failed, or the Go type
	// asked for cannot hold it.
	ErrTransform = errors.New("failed to transform parameter")
	// ErrSetParameter: an SSM parameter could not be written.
	ErrSetParameter = errors.New("failed to set parameter")
	// ErrGetSecret: several secrets could not be read in one batch.
	ErrGetSecret = errors.New("failed to get secrets")
	// ErrSetSecret: a secret could not be written.
	ErrSetSecret = errors.New("failed to set secret")
	// ErrNotFound: the store holds nothing under the name asked for. Stores
	// whose SDK has its own not-found error (SSM, Secrets Manager) wrap that
	// one instead; DynamoDB and custom stores use this.
	ErrNotFound = errors.New("parameter not found")
)

The sentinel errors every failure of this package and of the providers below it matches with errors.Is.

Functions

func ClearCaches

func ClearCaches()

ClearCaches forgets the default providers of the package-level shortcuts (ssm.GetParameter, secrets.GetSecret, appconfig.GetAppConfig and the others), with everything they cached: the next shortcut builds a new provider and a new SDK client. Providers you created yourself are not affected; call their ClearCache.

func Get

func Get[T any](ctx context.Context, src Source, name string, opts ...Option) (T, error)

Get reads the value of name from src and returns it as a T.

Without a transform the value is returned as stored: T is a string, a []byte, or any (which then holds a string). With TransformBinary it is decoded from base64 first, and any holds a []byte. With TransformJSON it is decoded into T, whatever T is: a struct, a map, a slice, a number. TransformAuto picks one of those from the name's suffix.

The value comes from the cache when it is there and fresh, unless WithForceFetch says otherwise. Errors are a *GetParameterError when the store fails and a *TransformError when the value cannot become a T.

Example
package main

import (
	"context"
	"fmt"
	"strings"

	"github.com/alessandrolattao/powertools-lambda-go/parameters"
)

// vaultStore is a custom store, standing in for HashiCorp Vault, S3 or a
// database: Get and GetMultiple are all a provider needs.
type vaultStore struct {
	secrets map[string]string
}

func (v vaultStore) Get(_ context.Context, name string, _ parameters.StoreOptions) ([]byte, error) {
	value, ok := v.secrets[name]
	if !ok {
		return nil, parameters.ErrNotFound
	}
	return []byte(value), nil
}

func (v vaultStore) GetMultiple(_ context.Context, path string, _ parameters.StoreOptions) (map[string][]byte, error) {
	values := make(map[string][]byte)
	for name, value := range v.secrets {
		if rest, ok := strings.CutPrefix(name, path+"/"); ok {
			values[rest] = []byte(value)
		}
	}
	return values, nil
}

var vault = vaultStore{secrets: map[string]string{
	"/app/db":            `{"host":"db.internal","port":5432}`,
	"/app/api-key":       "k3y",
	"/app/feature.json":  `{"enabled":true}`,
	"/app/cert.binary":   "aGVsbG8=",
	"/app/region":        "eu-west-1",
	"/other/unrelated":   "x",
	"/app/nested/is/too": "deep",
}}

func main() {
	type database struct {
		Host string `json:"host"`
		Port int    `json:"port"`
	}
	provider := parameters.NewProvider(vault)

	db, err := parameters.Get[database](context.Background(), provider, "/app/db", parameters.WithTransform(parameters.TransformJSON))
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s:%d\n", db.Host, db.Port)
}
Output:
db.internal:5432

func GetMultiple

func GetMultiple[T any](ctx context.Context, src Source, path string, opts ...Option) (map[string]T, error)

GetMultiple reads the values under path from src and returns them as Ts, keyed by their name relative to path. Each value becomes a T as in Get; with TransformAuto each one by its own suffix, so a GetMultiple[any] may mix strings, bytes and decoded JSON.

A value whose transform fails is the zero T, unless WithRaiseOnTransformError makes the whole read fail. Not every store has paths: AppConfig and Secrets Manager do not.

Example
package main

import (
	"context"
	"fmt"
	"maps"
	"slices"
	"strings"

	"github.com/alessandrolattao/powertools-lambda-go/parameters"
)

// vaultStore is a custom store, standing in for HashiCorp Vault, S3 or a
// database: Get and GetMultiple are all a provider needs.
type vaultStore struct {
	secrets map[string]string
}

func (v vaultStore) Get(_ context.Context, name string, _ parameters.StoreOptions) ([]byte, error) {
	value, ok := v.secrets[name]
	if !ok {
		return nil, parameters.ErrNotFound
	}
	return []byte(value), nil
}

func (v vaultStore) GetMultiple(_ context.Context, path string, _ parameters.StoreOptions) (map[string][]byte, error) {
	values := make(map[string][]byte)
	for name, value := range v.secrets {
		if rest, ok := strings.CutPrefix(name, path+"/"); ok {
			values[rest] = []byte(value)
		}
	}
	return values, nil
}

var vault = vaultStore{secrets: map[string]string{
	"/app/db":            `{"host":"db.internal","port":5432}`,
	"/app/api-key":       "k3y",
	"/app/feature.json":  `{"enabled":true}`,
	"/app/cert.binary":   "aGVsbG8=",
	"/app/region":        "eu-west-1",
	"/other/unrelated":   "x",
	"/app/nested/is/too": "deep",
}}

func main() {
	provider := parameters.NewProvider(vault)

	values, err := parameters.GetMultiple[any](context.Background(), provider, "/app", parameters.WithTransform(parameters.TransformAuto))
	if err != nil {
		panic(err)
	}
	for _, name := range slices.Sorted(maps.Keys(values)) {
		fmt.Printf("%s: %v (%T)\n", name, values[name], values[name])
	}
}
Output:
api-key: k3y (string)
cert.binary: [104 101 108 108 111] ([]uint8)
db: {"host":"db.internal","port":5432} (string)
feature.json: map[enabled:true] (map[string]interface {})
nested/is/too: deep (string)
region: eu-west-1 (string)

Types

type GetParameterError

type GetParameterError struct {
	// Name is the name, or the path, that was read.
	Name string
	Err  error
}

GetParameterError reports a value that could not be read: Python's GetParameterError.

func (*GetParameterError) Error

func (e *GetParameterError) Error() string

func (*GetParameterError) Is

func (*GetParameterError) Is(target error) bool

Is makes the error match ErrGetParameter.

func (*GetParameterError) Unwrap

func (e *GetParameterError) Unwrap() error

Unwrap returns the cause.

type GetSecretError

type GetSecretError struct {
	// Names are the secrets asked for.
	Names []string
	Err   error
}

GetSecretError reports a batch of secrets that could not be read: Python's GetSecretError. A single secret that cannot be read is a GetParameterError, as in Python.

func (*GetSecretError) Error

func (e *GetSecretError) Error() string

func (*GetSecretError) Is

func (*GetSecretError) Is(target error) bool

Is makes the error match ErrGetSecret.

func (*GetSecretError) Unwrap

func (e *GetSecretError) Unwrap() error

Unwrap returns the cause.

type Option

type Option func(*core.Options)

Option configures one read.

func WithForceFetch

func WithForceFetch(force bool) Option

WithForceFetch reads the value from its store even when the cache holds it; the fresh value is cached as usual.

func WithMaxAge

func WithMaxAge(d time.Duration) Option

WithMaxAge keeps the value cached for d, instead of POWERTOOLS_PARAMETERS_MAX_AGE (five minutes when unset). Zero or less reads the cache but never writes to it, as in Python.

func WithRaiseOnTransformError

func WithRaiseOnTransformError(raise bool) Option

WithRaiseOnTransformError makes a read of several values fail on the first value whose transform fails. By default such a value is returned as the zero value of its type (Python returns None) and the others are returned as usual. A read of a single value always fails on a transform error.

func WithStoreOption

func WithStoreOption[O any](variant string, apply func(*O)) Option

WithStoreOption carries an option of a specific store along with the common ones: the store reads it with StoreOptions.Apply. apply receives the store's own options type O.

variant says how the option changes the value read, for example "decrypt=true", or "" when it does not change it (a page size, a consistency level). Values read with different variants are cached apart.

func WithTransform

func WithTransform(t Transform) Option

WithTransform decodes the value with t. The value is cached after the transform, so the same name read with two transforms is fetched and cached twice, as in Python. ".json" and ".binary" are accepted as aliases of TransformJSON and TransformBinary.

type Provider

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

Provider reads values through a Store, caching them and applying the transforms. It is safe for concurrent use: create it once, in main or at package level, and share it across invocations.

func NewProvider

func NewProvider(store Store) *Provider

NewProvider returns a provider that reads through store.

Example
package main

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/alessandrolattao/powertools-lambda-go/parameters"
)

// vaultStore is a custom store, standing in for HashiCorp Vault, S3 or a
// database: Get and GetMultiple are all a provider needs.
type vaultStore struct {
	secrets map[string]string
}

func (v vaultStore) Get(_ context.Context, name string, _ parameters.StoreOptions) ([]byte, error) {
	value, ok := v.secrets[name]
	if !ok {
		return nil, parameters.ErrNotFound
	}
	return []byte(value), nil
}

func (v vaultStore) GetMultiple(_ context.Context, path string, _ parameters.StoreOptions) (map[string][]byte, error) {
	values := make(map[string][]byte)
	for name, value := range v.secrets {
		if rest, ok := strings.CutPrefix(name, path+"/"); ok {
			values[rest] = []byte(value)
		}
	}
	return values, nil
}

var vault = vaultStore{secrets: map[string]string{
	"/app/db":            `{"host":"db.internal","port":5432}`,
	"/app/api-key":       "k3y",
	"/app/feature.json":  `{"enabled":true}`,
	"/app/cert.binary":   "aGVsbG8=",
	"/app/region":        "eu-west-1",
	"/other/unrelated":   "x",
	"/app/nested/is/too": "deep",
}}

func main() {
	provider := parameters.NewProvider(vault)

	key, err := provider.Get(context.Background(), "/app/api-key", parameters.WithMaxAge(time.Minute))
	if err != nil {
		panic(err)
	}
	fmt.Println(key)

	_, err = provider.Get(context.Background(), "/app/missing")
	fmt.Println(errors.Is(err, parameters.ErrNotFound))
}
Output:
k3y
true

func (*Provider) ClearCache

func (p *Provider) ClearCache()

ClearCache forgets every value the provider cached.

func (*Provider) Get

func (p *Provider) Get(ctx context.Context, name string, opts ...Option) (string, error)

Get returns the value of name as a string: Get[string].

func (*Provider) GetMultiple

func (p *Provider) GetMultiple(ctx context.Context, path string, opts ...Option) (map[string]string, error)

GetMultiple returns the values under path as strings: GetMultiple[string].

type SetParameterError

type SetParameterError struct {
	Name string
	Err  error
}

SetParameterError reports an SSM parameter that could not be written: Python's SetParameterError.

func (*SetParameterError) Error

func (e *SetParameterError) Error() string

func (*SetParameterError) Is

func (*SetParameterError) Is(target error) bool

Is makes the error match ErrSetParameter.

func (*SetParameterError) Unwrap

func (e *SetParameterError) Unwrap() error

Unwrap returns the cause.

type SetSecretError

type SetSecretError struct {
	Name string
	Err  error
}

SetSecretError reports a secret that could not be written: Python's SetSecretError.

func (*SetSecretError) Error

func (e *SetSecretError) Error() string

func (*SetSecretError) Is

func (*SetSecretError) Is(target error) bool

Is makes the error match ErrSetSecret.

func (*SetSecretError) Unwrap

func (e *SetSecretError) Unwrap() error

Unwrap returns the cause.

type Source

type Source interface {
	// contains filtered or unexported methods
}

Source is what Get and GetMultiple read from: a *Provider, or a provider of the packages below this one, which embeds it.

type Store

type Store interface {
	// Get returns the value of name.
	Get(ctx context.Context, name string, opts StoreOptions) ([]byte, error)
	// GetMultiple returns the values under path, keyed by their name
	// relative to it. A store without paths returns an error matching
	// errors.ErrUnsupported.
	GetMultiple(ctx context.Context, path string, opts StoreOptions) (map[string][]byte, error)
}

Store reads values from a backend: the part of a provider that talks to it, Python's BaseProvider._get and _get_multiple. NewProvider puts the cache and the transforms in front of it, so a custom provider (Vault, S3, a database) only implements these two methods.

A store may be called concurrently. It returns the value as stored; the provider wraps its errors in GetParameterError.

type StoreOptions

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

StoreOptions are the store-specific options of one read, handed to the Store.

func (StoreOptions) Apply

func (s StoreOptions) Apply[O any](target *O)

Apply applies to target, in the order the caller gave them, the options written for its type with WithStoreOption. Set the defaults in target first; options for other stores are skipped.

type Transform

type Transform string

Transform names how a stored value is decoded before it is returned and cached.

const (
	// TransformNone returns the value as stored.
	TransformNone Transform = core.TransformNone
	// TransformJSON parses the value as a JSON document, which Get and
	// GetMultiple then decode into the Go type the caller names.
	TransformJSON Transform = core.TransformJSON
	// TransformBinary decodes the value from base64. Characters outside the
	// base64 alphabet are skipped, as Python's base64.b64decode skips them.
	TransformBinary Transform = core.TransformBinary
	// TransformAuto picks the transform from the suffix of each value's name:
	// "config.json" is read as JSON, "key.binary" as base64, anything else as
	// stored.
	TransformAuto Transform = core.TransformAuto
)

The transforms, as Python names them.

type TransformError

type TransformError struct {
	// Name is the name of the value.
	Name string
	// Transform is the transform asked for.
	Transform Transform
	Err       error
}

TransformError reports a value whose transform failed: Python's TransformParameterError.

func (*TransformError) Error

func (e *TransformError) Error() string

func (*TransformError) Is

func (*TransformError) Is(target error) bool

Is makes the error match ErrTransform.

func (*TransformError) Unwrap

func (e *TransformError) Unwrap() error

Unwrap returns the cause.

Directories

Path Synopsis
Package appconfig reads AWS AppConfig configuration profiles through AppConfigData sessions, with the cache and the transforms of package parameters.
Package appconfig reads AWS AppConfig configuration profiles through AppConfigData sessions, with the cache and the transforms of package parameters.
Package dynamodb reads parameters from an Amazon DynamoDB table, with the cache and the transforms of package parameters.
Package dynamodb reads parameters from an Amazon DynamoDB table, with the cache and the transforms of package parameters.
internal
core
Package core is the machinery the parameters packages share: the options of one read, the transforms, the conversion of a value into the Go type the caller names, the cache and the registry of default providers.
Package core is the machinery the parameters packages share: the options of one read, the transforms, the conversion of a value into the Go type the caller names, the cache and the registry of default providers.
Package secrets reads and writes AWS Secrets Manager secrets, with the cache and the transforms of package parameters.
Package secrets reads and writes AWS Secrets Manager secrets, with the cache and the transforms of package parameters.
Package ssm reads and writes AWS Systems Manager Parameter Store parameters, with the cache and the transforms of package parameters.
Package ssm reads and writes AWS Systems Manager Parameter Store parameters, with the cache and the transforms of package parameters.

Jump to

Keyboard shortcuts

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