config_ninja

Config Ninja 🥷

Ruff 🎨 poe (push) pylint codecov pre-commit.ci status Checked with mypy docs: pdoc readthedocs PyPI version Downloads

Similar to confd, manage your system configuration files by populating Jinja2 templates with data from a remote provider.

Installation

config-ninja is installed using the official installer or with pip / pipx. If your system supports systemd, you can then enable config-ninja as a systemd service.

Official Installer

The recommended way to install config-ninja is with the official installer:

curl -sSL https://config-ninja.github.io/config-ninja/install.py | python3 -

To view available installation options, run the installer with the --help flag:

curl -sSL https://config-ninja.github.io/config-ninja/install.py | python3 - --help
usage: install [-h] [--version VERSION] [--pre] [--uninstall] [--force] [--path PATH] [--backends BACKENDS]

Installs the latest (or given) version of config-ninja

options:
  -h, --help           show this help message and exit
  --version VERSION    install named version
  --pre                allow pre-release versions to be installed
  --uninstall          uninstall config-ninja
  --force              respond 'yes' to confirmation prompts; overwrite existing installations
  --path PATH          install config-ninja to this directory
  --backends BACKENDS  comma-separated list of package extras to install, or 'none' to install no backends

With pip / pipx

Alternatively, use pip / pipx to install [all available backends] (or choose a specific one):

pip install 'config-ninja[all]'

Enable the systemd Service

After installing config-ninja, enable it as a systemd service for the current user:

# omit '--user' to install the agent at the system level
config-ninja self install --user

How It Works

To demonstrate how the mechanics work locally:

  1. create a settings file for config-ninja:

    cat <<EOF >config-ninja-settings.yaml
    CONFIG_NINJA_OBJECTS:
      example-0:
        dest:
          format: json
          path: ./.local/settings.json
        source:
          backend: local
          format: toml
          init:
            kwargs:
              path: ./.local/config.toml
    EOF
    
  2. start config-ninja in monitor mode:

    config-ninja monitor
    
  3. in a separate shell, create the config.toml:

    cat <<EOF >./.local/config.toml
    [example-0]
    a = "first value"
    b = "second value
    EOF
    
  4. Inspect the settings.json file created by config-ninja:

    cat ./.local/settings.json
    
    {
      "example-0": {
        "a": "first value",
        "b": "second value"
      }
    }
    
  5. Make changes to the data in config.toml, and config-ninja will update settings.json accordingly:

    cat <<EOF >>./.local/config.toml
    [example-1]
    c = "third value"
    d = "fourth value
    EOF
    cat ./.local/settings.json
    
    {
      "example-0": {
        "a": "first value",
        "b": "second value"
      },
      "example-1": {
        "c": "third value",
        "d": "fourth value"
      }
    }
    

    Chances are, you'll want to update the config-ninja-settings.yaml file to use a remote backend (instead of local). See config_ninja.contrib for a list of supported config providers.

Configuration Architecture

The config-ninja agent monitors the backend source for changes. When the source data is changed, the agent updates the local configuration file with the new data:

sequenceDiagram loop polling config-ninja->>backend: query for changes end backend->>+config-ninja: [backend changed] fetch config config-ninja->>-filesystem: write updated configuration file

Navigation

 1""".. include:: ../../README.md
 2
 3# Navigation
 4- `config_ninja.contrib` for supported backends:
 5    - `config_ninja.contrib.appconfig`
 6    - `config_ninja.contrib.local`
 7- `config_ninja.cli` for commands and CLI documentation
 8- `config_ninja.systemd` for `systemd` integration
 9"""  # noqa: D415
10
11from __future__ import annotations
12
13__version__ = '1.3.0.post50+cc272a7'
14
15from pathlib import Path
16
17from pyspry import Settings
18
19__all__ = ['DEFAULT_SETTINGS_PATHS', 'load_settings', 'resolve_settings_path']
20
21DEFAULT_SETTINGS_PATHS = [
22    Path.cwd() / 'config-ninja-settings.yaml',
23    Path.home() / 'config-ninja-settings.yaml',
24    Path('/etc/config-ninja/settings.yaml'),
25]
26"""Check each of these locations for `config-ninja`_'s settings file.
27
28The following locations are checked (ordered by priority):
29
301. `./config-ninja-settings.yaml`
312. `~/config-ninja-settings.yaml`
323. `/etc/config-ninja/settings.yaml`
33
34.. _config-ninja: https://bryant-finney.github.io/config-ninja/config_ninja.html
35"""
36
37SETTINGS_PREFIX = 'CONFIG_NINJA'
38
39
40def load_settings(path: Path) -> Settings:
41    """Load the settings from the given path."""
42    return Settings.load(path, SETTINGS_PREFIX)
43
44
45def resolve_settings_path() -> Path:
46    """Return the first path in `DEFAULT_SETTINGS_PATHS` that exists."""
47    for path in DEFAULT_SETTINGS_PATHS:
48        if path.is_file():
49            return path
50
51    raise FileNotFoundError('Could not find config-ninja settings', DEFAULT_SETTINGS_PATHS)
DEFAULT_SETTINGS_PATHS = [PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/config-ninja/checkouts/latest/config-ninja-settings.yaml'), PosixPath('/home/docs/config-ninja-settings.yaml'), PosixPath('/etc/config-ninja/settings.yaml')]

Check each of these locations for config-ninja's settings file.

The following locations are checked (ordered by priority):

  1. ./config-ninja-settings.yaml
  2. ~/config-ninja-settings.yaml
  3. /etc/config-ninja/settings.yaml
def load_settings(path: pathlib.Path) -> pyspry.base.Settings:
41def load_settings(path: Path) -> Settings:
42    """Load the settings from the given path."""
43    return Settings.load(path, SETTINGS_PREFIX)

Load the settings from the given path.

def resolve_settings_path() -> pathlib.Path:
46def resolve_settings_path() -> Path:
47    """Return the first path in `DEFAULT_SETTINGS_PATHS` that exists."""
48    for path in DEFAULT_SETTINGS_PATHS:
49        if path.is_file():
50            return path
51
52    raise FileNotFoundError('Could not find config-ninja settings', DEFAULT_SETTINGS_PATHS)

Return the first path in DEFAULT_SETTINGS_PATHS that exists.