An app's configuration, from the literal config.exs on its data volume.
Each setting resolves as env var > config.exs > default. An app
declares its settings once, as {name, type, default}
(Macaw.Host.Config.Types), and loads them at boot:
@settings [
{:public_host, :string, "localhost"},
{:port, :port, 4000},
{:smtp_password, :secret, nil}
]
Macaw.Host.Config.load!(:my_app, @settings,
path: "/data/config.exs", env_prefix: "MY_APP")
Macaw.Host.Config.get(:my_app, :port)MY_APP_PORT wins over port in the file, which wins over 4000. Every
invalid value and every unknown key in the file fails the load at
once, each named with where it came from, so a broken boot says
everything that is wrong in one message. The resolved values, with each
one's source, are kept in :persistent_term and read without copying.
put!/4 and delete!/3 edit the file for the <app>-ctl config command:
the value is parsed as a literal, checked against the setting's type, and
the file is written atomically; nothing is evaluated. reset!/1 sets a file
that no longer parses aside and starts an empty one.
Summary
Functions
Removes name from the file, so it falls back to its env var or default.
The env var a setting is read from: PREFIX_NAME.
A loaded setting's value. Raises for a name the app didn't declare.
The settings' names, in declared order.
Reads the file and the environment, resolves, and stores the result under
app. Options: :path (the file), :env_prefix, :env (default
System.get_env/0). Raises ArgumentError listing every fault.
A value as shown; anything but a secret is inspected. A secret is
unset when nil or empty, set (…last4) when it is at least 16
characters long (so at most a quarter of it shows), else set. The
generated script's Ctl.Secret.mask/1 follows the same rule.
Sets name in the file at path to the literal text, after checking it
against the setting's type. {:ok, value}, the checked value, or
{:error, why}; the file is untouched on error. What is stored is the
literal as typed, never the check's result, which may have resolved a
reference to a secret (macaw_host#1).
The file at path as a map, never raising: {:ok, map} (%{} when it
doesn't exist) or {:error, message}, every fault on one line, for the
ctl, whose failure is one line.
Repairs a file that no longer parses: sets it aside beside itself as
<name>.broken (mode 0600, its bytes intact) and writes an empty one.
{:ok, set_aside}, the name it was set aside as, or {:error, why} with
both files untouched: there is no file, it parses (nothing to repair), or
a .broken one is already there, which is never overwritten.
Resolves settings from an env map, a file map and the env var prefix.
Pure: {:ok, resolved} or {:error, faults}.
Resolves one setting as resolve/4 does, env var > file > default, the
file's key an atom or a string: {:ok, %{value, source}}, or
{:error, %{raw, source, fault}} with the value as found (the env var's
string, the file's literal) and the fault as resolve/4 words it.
resolve/4 is built on it, and so is <app>-ctl config list, so the
two never disagree.
Every loaded setting as {name, shown value, source}, secrets masked.
Where a loaded setting's value came from: :env, :file or :default.
Types
Functions
Removes name from the file, so it falls back to its env var or default.
The env var a setting is read from: PREFIX_NAME.
A loaded setting's value. Raises for a name the app didn't declare.
The settings' names, in declared order.
Reads the file and the environment, resolves, and stores the result under
app. Options: :path (the file), :env_prefix, :env (default
System.get_env/0). Raises ArgumentError listing every fault.
@spec mask(Macaw.Host.Config.Types.t(), term()) :: String.t()
A value as shown; anything but a secret is inspected. A secret is
unset when nil or empty, set (…last4) when it is at least 16
characters long (so at most a quarter of it shows), else set. The
generated script's Ctl.Secret.mask/1 follows the same rule.
Sets name in the file at path to the literal text, after checking it
against the setting's type. {:ok, value}, the checked value, or
{:error, why}; the file is untouched on error. What is stored is the
literal as typed, never the check's result, which may have resolved a
reference to a secret (macaw_host#1).
The file at path as a map, never raising: {:ok, map} (%{} when it
doesn't exist) or {:error, message}, every fault on one line, for the
ctl, whose failure is one line.
Repairs a file that no longer parses: sets it aside beside itself as
<name>.broken (mode 0600, its bytes intact) and writes an empty one.
{:ok, set_aside}, the name it was set aside as, or {:error, why} with
both files untouched: there is no file, it parses (nothing to repair), or
a .broken one is already there, which is never overwritten.
@spec resolve([spec()], %{required(String.t()) => String.t()}, map(), String.t()) :: {:ok, resolved()} | {:error, [String.t()]}
Resolves settings from an env map, a file map and the env var prefix.
Pure: {:ok, resolved} or {:error, faults}.
@spec resolve_setting( spec(), %{required(String.t()) => String.t()}, map(), String.t() ) :: {:ok, %{value: term(), source: source()}} | {:error, %{raw: term(), source: source(), fault: String.t()}}
Resolves one setting as resolve/4 does, env var > file > default, the
file's key an atom or a string: {:ok, %{value, source}}, or
{:error, %{raw, source, fault}} with the value as found (the env var's
string, the file's literal) and the fault as resolve/4 words it.
resolve/4 is built on it, and so is <app>-ctl config list, so the
two never disagree.
Every loaded setting as {name, shown value, source}, secrets masked.
Where a loaded setting's value came from: :env, :file or :default.