From 00e68872c82aed01bf26bac87b7170a557d86452 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Tue, 30 Jan 2024 21:35:30 +0100 Subject: [PATCH 1/3] Add support for `global` section in `.kapitan` --- docs/pages/commands/kapitan_dotfile.md | 15 +++----- kapitan/utils.py | 14 +++---- tests/test_from_dot_kapitan.py | 53 ++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 18 deletions(-) create mode 100644 tests/test_from_dot_kapitan.py diff --git a/docs/pages/commands/kapitan_dotfile.md b/docs/pages/commands/kapitan_dotfile.md index 476f248c8..cc4fac8c7 100644 --- a/docs/pages/commands/kapitan_dotfile.md +++ b/docs/pages/commands/kapitan_dotfile.md @@ -27,7 +27,7 @@ version: 0.30 # Allows any 0.30.x release to run ... ``` -### `compile` +### Command line flags You can also permanently define all command line flags in the `.kapitan` config file. For example: @@ -45,19 +45,16 @@ would be equivalent to running: kapitan compile --indent 4 --parallelism 8 ``` -### inventory +For flags which are shared by multiple commands, you can either selectively define them for single commmands in a section with the same name as the command, or you can set any flags in section `global`, in which case they're applied for all commands. +If you set a flag in both the `global` section and a command's section, the value from the command's section takes precedence over the value from the global section. -In some cases, you might want to store the inventory under a different directory. You can configure the `inventory` section of the **Kapitan** dotfile to make sure it's persisted across all **Kapitan** runs. +As an example, you can configure the `inventory-path` in the `global` section of the **Kapitan** dotfile to make sure it's persisted across all **Kapitan** runs. ```yaml ... -inventory: +global: inventory-path: ./some_path ``` -which would be equivalent to always running: - -```shell -kapitan inventory --inventory-path=./some_path -``` +which would be equivalent to running any command with `--inventory-path=./some_path`. diff --git a/kapitan/utils.py b/kapitan/utils.py index bf53588ab..5d7e3418b 100644 --- a/kapitan/utils.py +++ b/kapitan/utils.py @@ -410,19 +410,15 @@ def dot_kapitan_config(): def from_dot_kapitan(command, flag, default): """ - Returns the 'flag' for 'command' from .kapitan file. If failed, returns 'default' + Returns the 'flag' from the '' or from the 'global' section in the .kapitan file. If + neither section proivdes a value for the flag, the value passed in `default` is returned. """ kapitan_config = dot_kapitan_config() - try: - if kapitan_config[command]: - flag_value = kapitan_config[command][flag] - if flag_value: - return flag_value - except KeyError: - pass + global_config = kapitan_config.get("global", {}) + cmd_config = kapitan_config.get(command, {}) - return default + return cmd_config.get(flag, global_config.get(flag, default)) def compare_versions(v1_raw, v2_raw): diff --git a/tests/test_from_dot_kapitan.py b/tests/test_from_dot_kapitan.py new file mode 100644 index 000000000..b24e73366 --- /dev/null +++ b/tests/test_from_dot_kapitan.py @@ -0,0 +1,53 @@ +import os +import shutil +import tempfile +import unittest +import yaml + +from kapitan.utils import from_dot_kapitan +from kapitan.cached import reset_cache + + +class FromDotKapitanTest(unittest.TestCase): + "Test loading flags from .kapitan" + + def _setup_dot_kapitan(self, config): + with open(self.work_dir.name + "/.kapitan", "w", encoding="utf-8") as f: + yaml.safe_dump(config, f) + + def setUp(self): + self.orig_dir = os.getcwd() + self.work_dir = tempfile.TemporaryDirectory() + os.chdir(self.work_dir.name) + + def test_no_file(self): + assert from_dot_kapitan("compile", "inventory-path", "./some/fallback") == "./some/fallback" + + def test_no_option(self): + self._setup_dot_kapitan( + {"global": {"inventory-backend": "reclass"}, "compile": {"inventory-path": "./path/to/inv"}} + ) + assert from_dot_kapitan("inventory", "inventory-path", "./some/fallback") == "./some/fallback" + + def test_cmd_option(self): + self._setup_dot_kapitan( + {"global": {"inventory-backend": "reclass"}, "compile": {"inventory-path": "./path/to/inv"}} + ) + assert from_dot_kapitan("compile", "inventory-path", "./some/fallback") == "./path/to/inv" + + def test_global_option(self): + self._setup_dot_kapitan( + {"global": {"inventory-path": "./some/path"}, "compile": {"inventory-path": "./path/to/inv"}} + ) + assert from_dot_kapitan("inventory", "inventory-path", "./some/fallback") == "./some/path" + + def test_command_over_global_option(self): + self._setup_dot_kapitan( + {"global": {"inventory-path": "./some/path"}, "compile": {"inventory-path": "./path/to/inv"}} + ) + assert from_dot_kapitan("compile", "inventory-path", "./some/fallback") == "./path/to/inv" + + def tearDown(self): + self.work_dir.cleanup() + os.chdir(self.orig_dir) + reset_cache() From bdef3e3656a6f8e1072a05235edc09e3de5d5b9a Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Sat, 27 Jan 2024 14:11:04 +0100 Subject: [PATCH 2/3] Refactor handling of inventory backend command line flag This commit switches the inventory backend command line flag to the form `--inventory-backend=`. Additionally, we restrict the allowed values for the flag to the known backends. To ensure the allowed values for the command line flag, and the known implementations are in sync, we introduce a dict holding the mapping from flag values to `Inventory` subclasses. Finally, we ensure that the selected backend is cached as a string in `cached.args["inventory-backend"]`. Note that this is not how `cached.args` is used otherwise, but since it's unlikely that there ever will be a real command `inventory-backend`, this should be fine. --- kapitan/cli.py | 15 +++++++++------ kapitan/inventory/__init__.py | 8 ++++++++ kapitan/resources.py | 14 ++++++++------ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/kapitan/cli.py b/kapitan/cli.py index e83cee203..f0bda931b 100644 --- a/kapitan/cli.py +++ b/kapitan/cli.py @@ -21,6 +21,7 @@ from kapitan import cached, defaults, setup_logging from kapitan.initialiser import initialise_skeleton from kapitan.inputs.jsonnet import jsonnet_file +from kapitan.inventory import AVAILABLE_BACKENDS from kapitan.lint import start_lint from kapitan.refs.base import RefController, Revealer from kapitan.refs.cmd_parser import handle_refs_command @@ -103,12 +104,12 @@ def build_parser(): subparser = parser.add_subparsers(help="commands", dest="subparser_name") inventory_backend_parser = argparse.ArgumentParser(add_help=False) - inventory_backend_group = inventory_backend_parser.add_argument_group("inventory_backend") - inventory_backend_group.add_argument( - "--reclass", - action="store_true", - default=from_dot_kapitan("inventory_backend", "reclass", False), - help="use reclass as inventory backend (default)", + inventory_backend_parser.add_argument( + "--inventory-backend", + action="store", + default=from_dot_kapitan("inventory_backend", "inventory-backend", "reclass"), + choices=AVAILABLE_BACKENDS.keys(), + help="Select the inventory backend to use (default=reclass)", ) eval_parser = subparser.add_parser("eval", aliases=["e"], help="evaluate jsonnet file") @@ -663,6 +664,8 @@ def main(): # cache args where key is subcommand assert "name" in args, "All cli commands must have provided default name" cached.args[args.name] = args + if "inventory_backend" in args: + cached.args["inventory-backend"] = args.inventory_backend if hasattr(args, "verbose") and args.verbose: setup_logging(level=logging.DEBUG, force=True) diff --git a/kapitan/inventory/__init__.py b/kapitan/inventory/__init__.py index 16268e391..c3c0f662d 100644 --- a/kapitan/inventory/__init__.py +++ b/kapitan/inventory/__init__.py @@ -1,2 +1,10 @@ +from typing import Type + from .inv_reclass import ReclassInventory from .inventory import Inventory + +# Dict mapping values for command line flag `--inventory-backend` to the +# associated `Inventory` subclass. +AVAILABLE_BACKENDS: dict[str, Type[Inventory]] = { + "reclass": ReclassInventory, +} diff --git a/kapitan/resources.py b/kapitan/resources.py index f5aad353c..7079fa34d 100644 --- a/kapitan/resources.py +++ b/kapitan/resources.py @@ -22,7 +22,7 @@ import kapitan.cached as cached from kapitan import __file__ as kapitan_install_path from kapitan.errors import CompileError, InventoryError, KapitanError -from kapitan.inventory import Inventory, ReclassInventory +from kapitan.inventory import Inventory, ReclassInventory, AVAILABLE_BACKENDS from kapitan.utils import PrettyDumper, deep_get, flatten_dict, render_jinja2_file, sha256_string logger = logging.getLogger(__name__) @@ -316,13 +316,15 @@ def get_inventory(inventory_path) -> Inventory: if cached.inv and cached.inv.targets: return cached.inv - inventory_backend: Inventory = None - # select inventory backend - if cached.args.get("inventory-backend") == "my-new-inventory": - logger.debug("Using my-new-inventory as inventory backend") + backend_id = cached.args.get("inventory-backend") + backend = AVAILABLE_BACKENDS.get(backend_id) + inventory_backend: Inventory = None + if backend != None: + logger.debug(f"Using {backend_id} as inventory backend") + inventory_backend = backend(inventory_path) else: - logger.debug("Using reclass as inventory backend") + logger.debug(f"Backend {backend_id} is unknown, falling back to reclass as inventory backend") inventory_backend = ReclassInventory(inventory_path) inventory_backend.search_targets() From 0c13d117471ef138e37908405ea7e70c927c3b44 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Sat, 27 Jan 2024 19:29:48 +0100 Subject: [PATCH 3/3] Update documentation --- docs/pages/commands/kapitan_compile.md | 36 ++++++++++++++------------ docs/pages/commands/kapitan_dotfile.md | 11 ++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/docs/pages/commands/kapitan_compile.md b/docs/pages/commands/kapitan_compile.md index 7d5c7393c..aaf410f13 100644 --- a/docs/pages/commands/kapitan_compile.md +++ b/docs/pages/commands/kapitan_compile.md @@ -148,24 +148,28 @@ The `--embed-refs` flags tells **Kapitan** to embed these references on compile, ``` ??? example "click to expand output" + ```shell - usage: kapitan compile [-h] [--search-paths JPATH [JPATH ...]] - [--jinja2-filters FPATH] [--verbose] [--prune] - [--quiet] [--output-path PATH] [--fetch] - [--force-fetch] [--force] [--validate] - [--parallelism INT] [--indent INT] - [--refs-path REFS_PATH] [--reveal] [--embed-refs] - [--inventory-path INVENTORY_PATH] [--cache] - [--cache-paths PATH [PATH ...]] - [--ignore-version-check] [--use-go-jsonnet] - [--compose-node-name] [--schemas-path SCHEMAS_PATH] - [--yaml-multiline-string-style STYLE] - [--yaml-dump-null-as-empty] - [--targets TARGET [TARGET ...] | --labels - [key=value ...]] - - optional arguments: + usage: kapitan compile [-h] [--inventory-backend {reclass}] + [--search-paths JPATH [JPATH ...]] + [--jinja2-filters FPATH] [--verbose] [--prune] + [--quiet] [--output-path PATH] [--fetch] + [--force-fetch] [--force] [--validate] + [--parallelism INT] [--indent INT] + [--refs-path REFS_PATH] [--reveal] [--embed-refs] + [--inventory-path INVENTORY_PATH] [--cache] + [--cache-paths PATH [PATH ...]] + [--ignore-version-check] [--use-go-jsonnet] + [--compose-node-name] [--schemas-path SCHEMAS_PATH] + [--yaml-multiline-string-style STYLE] + [--yaml-dump-null-as-empty] + [--targets TARGET [TARGET ...] | --labels + [key=value ...]] + + options: -h, --help show this help message and exit + --inventory-backend {reclass} + Select the inventory backend to use (default=reclass) --search-paths JPATH [JPATH ...], -J JPATH [JPATH ...] set search paths, default is ["."] --jinja2-filters FPATH, -J2F FPATH diff --git a/docs/pages/commands/kapitan_dotfile.md b/docs/pages/commands/kapitan_dotfile.md index cc4fac8c7..c2b5408db 100644 --- a/docs/pages/commands/kapitan_dotfile.md +++ b/docs/pages/commands/kapitan_dotfile.md @@ -58,3 +58,14 @@ global: ``` which would be equivalent to running any command with `--inventory-path=./some_path`. + +Another flag that you may want to set in the `global` section is `inventory-backend` to select a non-default inventory backend implementation. + +```yaml +global: + inventory-backend: reclass +``` + +which would be equivalent to always running **Kapitan** with `--inventory-backend=reclass`. + +Please note that the `inventory-backend` flag currently can't be set through the command-specific sections of the **Kapitan** config file.