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 ¶
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 ErrMalformedEnv = errors.New("configetcd: malformed etcdctl environment")
ErrMalformedEnv reports an ETCDCTL_ variable Default cannot use. The error names the variable.
var ErrNoEndpoints = errors.New("configetcd: no etcd endpoints configured")
ErrNoEndpoints reports a configuration that names no etcd endpoint. Neither FromConfig nor Default falls back to localhost: a configuration store that guesses its source serves wrong values instead of failing.
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.
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 OwnedBackend ¶ added in v0.3.0
OwnedBackend is a backend that built its own etcd client and is therefore responsible for closing it (config spec 0012 R2).
Hand it to the Store with config.WithCloser, and closing the Store releases the client:
b, err := configetcd.FromConfig(cfg, "app/")
if err != nil { return err }
store, err := config.NewStore(ctx, config.WithBackend(b), config.WithCloser(b))
if err != nil { return err } // a Store that was never built has closed b
defer store.Close()
New and FromClient return a plain config.Backend, because there the caller built the client and still owns it.
func Default ¶ added in v0.4.1
func Default(prefix string, opts ...Option) (*OwnedBackend, error)
Default builds the etcd client from the environment etcdctl reads, so a tool reaches the same cluster as `etcdctl get` does in the same shell, and returns a backend that owns the client. The client must be closed; see OwnedBackend.
It reads ETCDCTL_ENDPOINTS (comma-separated), ETCDCTL_CACERT, ETCDCTL_CERT, ETCDCTL_KEY, ETCDCTL_USER, ETCDCTL_PASSWORD and ETCDCTL_DIAL_TIMEOUT, once, at construction. TLS is used when any of the three certificate variables is set. ETCDCTL_USER may carry the password as user:password when ETCDCTL_PASSWORD is unset, as it may for etcdctl.
It differs from etcdctl in three places, each on purpose:
- No ETCDCTL_ENDPOINTS is ErrNoEndpoints, not localhost:2379. A configuration store that guesses its source serves wrong values rather than failing.
- A user with no password is an error naming ETCDCTL_PASSWORD, where etcdctl would prompt. A library has no terminal to ask on.
- ETCDCTL_INSECURE_SKIP_TLS_VERIFY is never read. A consumer who needs it for a test cluster builds the config and calls FromConfig.
With ETCDCTL_DIAL_TIMEOUT unset there is no dial timeout: nothing dials until Load, and Load's context bounds it.
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 that owns it. The client must be closed; see OwnedBackend. To take the same settings from the environment etcdctl reads, use Default.
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.
func (*OwnedBackend) Close ¶ added in v0.3.0
func (b *OwnedBackend) Close() error
Close releases the etcd client this backend built. It is safe to call more than once.
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.