provcat.config

Configuration sources.

A configuration is a dict with up to seven sections:

The view of one tenant, which ConfigSource.load(tenant) gives, is the shared sections and that tenant’s part. With no tenant it is the admin’s view, the shared sections, the admin part and every tenant’s part. A tenant may name only its own stores, stages and entries, so that no tenant can take over another’s. Each scheme that names its tenant part, with tenant: org, says whose a URI is.

In etcd each tenant’s part lives under <prefix>/tenants/<name>/<section>, <prefix>/tenants/<name>/name marks the tenant, and <prefix>/tenants lists the names. The admin part lives under <prefix>/admin/<section>. A reader for one tenant reads only the shared keys and its own, so etcd’s access control on key prefixes can keep the tenants apart.

ConfigSource is the interface. load() returns the configuration with every ${oc.env:VAR} filled in from the environment of the reader. raw() returns it as written, with the ${...} left in. config push copies the raw form, so each reader of a shared etcd fills in its own environment.

YamlConfig reads a YAML file with PyYAML. EtcdConfig keeps each section as JSON under <prefix>/<section> and talks to the etcd v3 HTTP gateway, so it needs no etcd client library. save() replaces all sections in one etcd transaction, and deletes the sections that the new configuration does not have.

class View

source

View(cfg: dict, tenant: str | None)

A configuration dict cut down to one view, with a tenant attribute that says whose.

Parameter Type Default Description
cfg dict required The sections of the view
tenant str | None required The tenant whose view it is; None for the admin’s

class ConfigSource

source

Where a configuration comes from: the interface of a configuration source.

A source gives the configuration as written, raw(), or as one tenant sees it, load().

ConfigSource.raw

source

ConfigSource.raw() -> dict

Return the whole configuration as written.

Parameter Type Default Description
returns dict The configuration as written, with every ${…} left in

ConfigSource.raw_for

source

ConfigSource.raw_for(tenant: str | None) -> dict

Read the configuration as written, for one tenant. A source may leave out other tenants.

Parameter Type Default Description
tenant str | None required The tenant to read for; None for the admin, who reads everything
returns dict The configuration as written, with the shared sections and the tenant’s part

ConfigSource.load

source

ConfigSource.load(tenant: str | None = None) -> View

Load the configuration that a tenant sees, with its ${…} values filled in.

The admin’s view holds every tenant’s part. The view is cut first, so a ${…} in another tenant’s part is never read, and a tenant’s part cannot reach into another part.

Parameter Type Default Description
tenant str | None None The tenant whose view to load; None for the admin’s view
returns View The tenant’s view, with every ${…} filled in

ConfigSource.save

source

ConfigSource.save(cfg: dict) -> None

Replace the stored configuration. A source that cannot be written, as this one, raises ConfigError.

Parameter Type Default Description
cfg dict required The whole configuration, as written

class EmptyConfig

source

No configuration: the catalog has only the default stores.

EmptyConfig.raw

source

EmptyConfig.raw() -> dict

Return an empty configuration.

Parameter Type Default Description
returns dict An empty configuration

class YamlConfig

source

YamlConfig(path: str | Path)

Configuration in a YAML file, read with PyYAML. It cannot be saved.

Parameter Type Default Description
path str | Path required The YAML file

YamlConfig.raw

source

YamlConfig.raw() -> dict

Read and check the file. ConfigError when it is missing or bad.

Parameter Type Default Description
returns dict The configuration as the file holds it

class EtcdConfig

source

EtcdConfig(url: str, timeout: float = 5.0)

Configuration in etcd, addressed as etcd://host:port/prefix.

Parameter Type Default Description
url str required etcd://host:port/prefix; the port defaults to 2379, and the prefix may be empty
timeout float 5.0 Seconds to wait for each request to etcd

EtcdConfig.get

source

EtcdConfig.get(key: str) -> str | None

Read one key from etcd.

Parameter Type Default Description
key str required The full etcd key, prefix included
returns str | None The value stored under the key; None when there is none or it is empty

EtcdConfig.raw

source

EtcdConfig.raw() -> dict

Read the whole configuration from etcd.

Parameter Type Default Description
returns dict The shared sections, every tenant’s part and the admin part, checked

EtcdConfig.raw_for

source

EtcdConfig.raw_for(tenant: str | None) -> dict

Read only the shared keys and the tenant’s, so etcd’s access control can keep the tenants apart.

Parameter Type Default Description
tenant str | None required The tenant to read for; None for the admin, who reads everything
returns dict The shared sections and the tenant’s part alone, checked

EtcdConfig.save

source

EtcdConfig.save(cfg: dict) -> None

Replace every section in one etcd transaction, and delete the sections and tenants that cfg does not have.

The configuration must load again for every tenant. ConfigError when etcd refuses.

Parameter Type Default Description
cfg dict required The whole configuration, as written, with any ${…} left in

open_config

source

open_config(spec: str | None = None) -> ConfigSource

Pick a config source.

The order is: the given spec, then PROVCAT_CONFIG, then ./provcat.yaml if it exists, then the built-in defaults. A spec that starts with etcd:// is etcd; anything else is a YAML path.

Parameter Type Default Description
spec str | None None A YAML path or an etcd://host:port/prefix URL
returns ConfigSource An EtcdConfig, a YamlConfig or an EmptyConfig