provcat.config
A configuration is a dict with up to seven sections:
stores: named store instances, each{driver: ..., <options>}.schemes: logical URI schemes and their templates (see resolver.py).stages: stage pointers,{logical-uri: {stage-name: "kind:value"}}.entries: rules for one logical name or prefix,{logical-uri: {target: ..., ref: ...}}(see resolver.py).cache:{root: <folder>}.tenants:{name: {stores: ..., stages: ..., entries: ..., cache: ...}}, the part of each tenant. The schemes are shared.admin:{stores: ..., stages: ..., entries: ..., cache: ...}, the part that only the admin sees, such as a store with keys that read every tenant’s data.
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
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
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
ConfigSource.raw() -> dictReturn the whole configuration as written.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | dict | The configuration as written, with every ${…} left in |
ConfigSource.raw_for
ConfigSource.raw_for(tenant: str | None) -> dictRead 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
ConfigSource.load(tenant: str | None = None) -> ViewLoad 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
ConfigSource.save(cfg: dict) -> NoneReplace 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
No configuration: the catalog has only the default stores.
EmptyConfig.raw
EmptyConfig.raw() -> dictReturn an empty configuration.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | dict | An empty configuration |
class YamlConfig
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
YamlConfig.raw() -> dictRead 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
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
EtcdConfig.get(key: str) -> str | NoneRead 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
EtcdConfig.raw() -> dictRead 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
EtcdConfig.raw_for(tenant: str | None) -> dictRead 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
EtcdConfig.save(cfg: dict) -> NoneReplace 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
open_config(spec: str | None = None) -> ConfigSourcePick 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 |