Documentation
¶
Overview ¶
Package configetcd contributes configuration from an etcd v3 cluster as a first-class config layer: precedence, per-key merge, provenance, safe hot-reload and compare-and-swap writes, exactly like a file layer.
A prefix scopes the backend to one namespace of the key space; the keys beneath it, split on "/", become the layer's nested tree. etcd stores bytes, so a value is a scalar string by default and the View's typed accessors coerce it (GetInt("server.port") parses "8080"). A value that is itself a JSON or YAML document is decoded into a subtree when a codec is supplied with WithValueCodec — see the config-etcd spec, D4.
The cluster is reached through the narrow KV interface, which Wrap adapts from a configured *clientv3.Client. Injecting the client keeps every endpoint, credential and TLS decision with the consumer and lets the whole unit suite run against a fake, needing no etcd.
etcd is the family's second native-watch backend after Consul, and the only one whose compare-and-swap covers a whole batch: a transaction is atomic across every key it touches, so config.Capabilities.AtomicMultiKey is true.
Index ¶
- Constants
- Variables
- func FromClient(client *clientv3.Client, prefix string, opts ...Option) config.Backend
- func FromConfig(cfg clientv3.Config, prefix string, opts ...Option) (config.Backend, error)
- func New(kv KV, prefix string, opts ...Option) config.Backend
- type Cmp
- type KV
- type Op
- type Option
- type Pair
Constants ¶
const SourceKind = config.SourceKind("etcd")
SourceKind identifies an etcd layer in provenance. A value read from here reports as coming from "etcd:<prefix>" rather than from a file.
Variables ¶
var ErrNoEndpoints = errors.New("configetcd: no etcd endpoints configured; set Endpoints on the config")
ErrNoEndpoints reports a config that names no etcd endpoint.
etcd has no ambient convention to fall back on — see FromConfig — so an empty Endpoints slice cannot be filled in from the environment and would fail at the first request instead of here.
Functions ¶
func FromClient ¶
FromClient is the common path: New over a Wrap-ped etcd client, so a consumer writes configetcd.FromClient(client, "app/") without touching the narrow interface.
func FromConfig ¶ added in v0.2.0
FromConfig builds the etcd client from a config the caller assembled — the endpoints, dial timeout, credentials and TLS — and returns a backend over it.
clientv3.New is non-blocking: it validates the config and prepares the gRPC connection without dialling, so a failure here is a malformed configuration rather than an unreachable cluster. Reaching etcd is deferred to Load, which has a context to bound it.
There is no ambient rung here, deliberately ¶
Every other remote adapter in this family offers a zero-conf Default() over its SDK's own ambient convention — LoadDefaultConfig for AWS, DefaultAzureCredential for Azure, Application Default Credentials for GCP, DefaultConfig for Vault and Consul. etcd has none: clientv3 has no DefaultConfig, and its only environment variable is ETCD_CLIENT_DEBUG, a debug flag rather than an endpoint or a credential.
A Default() here would therefore have to *invent* the convention, and adopting a provider's documented default is a different act from inventing one. It would also mean a configuration store quietly connecting to localhost:2379 and serving a tool's settings from an unintended source — a failure that presents as wrong values rather than as a connection error.
The question is deferred rather than closed: config spec 0012 R1 revisits it once every other adapter has been upgraded and the full set of shapes is visible, so it is settled once rather than one adapter at a time.
Types ¶
type Cmp ¶
Cmp is one compare-and-swap guard: the key must still be at the ModRevision it held at Load. Revision zero requires the key be absent — that is how etcd spells "does not exist" — so a create cannot silently overwrite a key someone else added in the meantime.
type KV ¶
type KV interface {
// Get returns every pair under prefix and the store revision identifying
// that read. The revision is what the write and watch paths build on, so it
// must be the header revision etcd reported for this read, not a fresh
// fetch: the watch replays from it to close the gap between reading and
// attaching, and Verify compares against it.
Get(ctx context.Context, prefix string) (pairs []Pair, revision int64, err error)
// Txn applies ops as one transaction, guarded by cmps. ok is false, with a
// nil error, when a comparison failed — a key moved since Load. etcd applies
// the whole set or none of it.
Txn(ctx context.Context, cmps []Cmp, ops []Op) (ok bool, err error)
// Watch calls onChange for every change under prefix, replaying from
// fromRevision so a change that landed between Get and the watch attaching
// is still delivered. The returned stop ends the watch.
Watch(ctx context.Context, prefix string, fromRevision int64, onChange func()) (stop func(), err error)
}
KV is the slice of etcd this adapter uses, behind an interface it owns so a fake drives the unit suite and the real client is adapted by Wrap.
type Op ¶
Op is one write in a transaction: a put or a delete of a full etcd key. The guard lives in the accompanying Cmp rather than on the op, matching etcd's own If/Then split.
type Option ¶
type Option func(*backend)
Option configures a backend.
func WithValueCodec ¶
WithValueCodec decodes each etcd 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.
Writes always target flat etcd keys; writing into a value that a codec decoded from a blob is not supported and lands as a sibling flat key.
type Pair ¶
type Pair struct {
// Key is the full etcd key, prefix included.
Key string
// Value is the raw bytes etcd holds.
Value []byte
// ModRevision is the store revision at which this key was last modified.
// It is the per-key version compare-and-swap guards on.
ModRevision int64
}
Pair is one etcd key/value, with the revision the write path compares against.