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.

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

Features

  • ✅ Integration with AWS AppConfig for managing server configuration files
  • ✅ Extensible design supports backends for new providers and formats
  • ✅ jinja2 templating for arbitrary configuration file formats
  • ✅ Execute poethepoet tasks after updating files

Installation

config-ninja is installed using the official installer or with pip / pipx. After installation, you can 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):

pipx install 'config-ninja[all]'

With uv

uv tool 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 (using the local backend):

  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. run config-ninja in monitor mode:

    config-ninja apply --poll
    
  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.

Navigation

config_ninja.cli

Commands and CLI documentation.

config_ninja.contrib

For supported backends.

config_ninja.contrib.appconfig

Integrate with the AWS AppConfig service.

config_ninja.contrib.local

Use a local file as the backend.

config_ninja.hooks

Execute poethepoet tasks as callback hooks for backend updates.

config_ninja.settings

For settings and configuration.

config_ninja.systemd

Integration with systemd.

 1""".. include:: ../../README.md
 2
 3# Navigation
 4
 5## `config_ninja.cli`
 6
 7Commands and CLI documentation.
 8
 9## `config_ninja.contrib`
10
11For supported backends.
12
13### `config_ninja.contrib.appconfig`
14
15Integrate with the AWS AppConfig service.
16
17### `config_ninja.contrib.local`
18
19Use a local file as the backend.
20
21## `config_ninja.hooks`
22
23Execute [`poethepoet`](https://poethepoet.natn.io/) tasks as callback hooks for backend updates.
24
25## `config_ninja.settings`
26
27For settings and configuration.
28
29## `config_ninja.systemd`
30
31Integration with `systemd`.
32"""  # noqa: D415
33
34from __future__ import annotations
35
36import sys
37import warnings
38from pathlib import Path
39from typing import Any
40
41import pyspry
42
43__version__ = '1.5.1'
44
45from config_ninja.settings import DEFAULT_PATHS, load
46from config_ninja.settings import resolve_path as resolve_settings_path
47
48__all__ = ['DEFAULT_SETTINGS_PATHS', 'load_settings', 'resolve_settings_path']
49
50DEFAULT_SETTINGS_PATHS = DEFAULT_PATHS
51"""Check each of these locations for `config-ninja`_'s settings file.
52
53The following locations are checked (ordered by priority):
54
551. `./config-ninja-settings.yaml`
562. `~/config-ninja-settings.yaml`
573. `/etc/config-ninja/settings.yaml`
58
59.. _config-ninja: https://config-ninja.readthedocs.io/home.html
60"""
61
62
63def load_settings(path: Path) -> pyspry.Settings:  # pragma: no cover
64    """(deprecated) Load the settings file at the given path.
65
66    This function is deprecated and will be removed in a future release. Use `config_ninja.settings.load()` instead.
67    """
68    warnings.warn(
69        '`config_ninja.load_settings()` is deprecated and will be removed in a future release. Use '
70        '`config_ninja.settings.load()` instead.',
71        DeprecationWarning,
72        stacklevel=2,
73    )
74    return load(path).settings
75
76
77def main(*args: Any) -> None:  # pylint: disable=missing-function-docstring
78    """Entrypoint for the `config-ninja` CLI.
79
80    When arguments are provided, they are used to replace `sys.argv[1:]`.
81    """
82    if args:
83        sys.argv[1:] = list(args)
84
85    from config_ninja.cli import app
86
87    app(prog_name='config-ninja')
DEFAULT_SETTINGS_PATHS = [PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/config-ninja/checkouts/v1.5.1/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.Settings:
64def load_settings(path: Path) -> pyspry.Settings:  # pragma: no cover
65    """(deprecated) Load the settings file at the given path.
66
67    This function is deprecated and will be removed in a future release. Use `config_ninja.settings.load()` instead.
68    """
69    warnings.warn(
70        '`config_ninja.load_settings()` is deprecated and will be removed in a future release. Use '
71        '`config_ninja.settings.load()` instead.',
72        DeprecationWarning,
73        stacklevel=2,
74    )
75    return load(path).settings

(deprecated) Load the settings file at the given path.

This function is deprecated and will be removed in a future release. Use config_ninja.settings.load() instead.

def resolve_settings_path() -> pathlib.Path:
137def resolve_path() -> Path:
138    """Return the first path in `DEFAULT_PATHS` that exists."""
139    for path in DEFAULT_PATHS:
140        if path.is_file():
141            return path
142
143    raise FileNotFoundError('Could not find config-ninja settings', DEFAULT_PATHS)

Return the first path in DEFAULT_PATHS that exists.