From d655c0ceb53e0cbda1cec32cbdfa3d1959b869df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 30 Aug 2023 11:27:43 +0200 Subject: [PATCH 1/3] clan-cli/direnv: also watch default.nix --- pkgs/clan-cli/.envrc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/clan-cli/.envrc b/pkgs/clan-cli/.envrc index 00f84d526..53d6aa325 100644 --- a/pkgs/clan-cli/.envrc +++ b/pkgs/clan-cli/.envrc @@ -4,7 +4,9 @@ source_up if type nix_direnv_watch_file &>/dev/null; then nix_direnv_watch_file flake-module.nix + nix_direnv_watch_file default.nix else direnv watch flake-module.nix + direnv watch default.nix fi use flake .#clan-cli --builders '' From 54d855a6cdbcd9f5e6b8c467d6e32cbfba572e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 30 Aug 2023 11:28:07 +0200 Subject: [PATCH 2/3] clan-cli: Document how to run single-threaded --- pkgs/clan-cli/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkgs/clan-cli/README.md b/pkgs/clan-cli/README.md index 538916486..df9c50907 100644 --- a/pkgs/clan-cli/README.md +++ b/pkgs/clan-cli/README.md @@ -27,3 +27,18 @@ To start a local developement environment instead, use the `--dev` flag: ``` This will spawn two webserver, a python one to for the api and a nodejs one that rebuilds the ui on the fly. + +## Run locally single-threaded for debugging + +By default tests run in parallel using pytest-parallel. +pytest-parallel however breaks `breakpoint()`. To disable it, use this: + +```console +pytest --workers "" -s +``` + +You can also run a single test like this: + +```console +pytest --workers "" -s tests/test_secrets_cli.py::test_users +``` From af38408a3e55684b089bbd9c2d344d889f080220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 30 Aug 2023 11:30:57 +0200 Subject: [PATCH 3/3] secret cli: add get command that returns the key of users/machines --- pkgs/clan-cli/clan_cli/secrets/machines.py | 21 ++++++++++++++++----- pkgs/clan-cli/clan_cli/secrets/users.py | 14 +++++++++++++- pkgs/clan-cli/tests/test_secrets_cli.py | 7 ++++++- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/secrets/machines.py b/pkgs/clan-cli/clan_cli/secrets/machines.py index 80a07e6d9..811e88367 100644 --- a/pkgs/clan-cli/clan_cli/secrets/machines.py +++ b/pkgs/clan-cli/clan_cli/secrets/machines.py @@ -3,11 +3,8 @@ import argparse from ..machines.types import machine_name_type, validate_hostname from . import secrets from .folders import list_objects, remove_object, sops_machines_folder -from .sops import write_key -from .types import ( - public_or_private_age_key_type, - secret_name_type, -) +from .sops import read_key, write_key +from .types import public_or_private_age_key_type, secret_name_type def add_machine(name: str, key: str, force: bool) -> None: @@ -18,6 +15,10 @@ def remove_machine(name: str) -> None: remove_object(sops_machines_folder(), name) +def get_machine(name: str) -> str: + return read_key(sops_machines_folder() / name) + + def list_machines() -> list[str]: return list_objects(sops_machines_folder(), lambda x: validate_hostname(x)) @@ -42,6 +43,10 @@ def add_command(args: argparse.Namespace) -> None: add_machine(args.machine, args.key, args.force) +def get_command(args: argparse.Namespace) -> None: + print(get_machine(args.machine)) + + def remove_command(args: argparse.Namespace) -> None: remove_machine(args.machine) @@ -82,6 +87,12 @@ def register_machines_parser(parser: argparse.ArgumentParser) -> None: ) add_parser.set_defaults(func=add_command) + get_parser = subparser.add_parser("get", help="get a machine public key") + get_parser.add_argument( + "machine", help="the name of the machine", type=machine_name_type + ) + get_parser.set_defaults(func=get_command) + remove_parser = subparser.add_parser("remove", help="remove a machine") remove_parser.add_argument( "machine", help="the name of the machine", type=machine_name_type diff --git a/pkgs/clan-cli/clan_cli/secrets/users.py b/pkgs/clan-cli/clan_cli/secrets/users.py index 25cf28ae2..760218af8 100644 --- a/pkgs/clan-cli/clan_cli/secrets/users.py +++ b/pkgs/clan-cli/clan_cli/secrets/users.py @@ -2,7 +2,7 @@ import argparse from . import secrets from .folders import list_objects, remove_object, sops_users_folder -from .sops import write_key +from .sops import read_key, write_key from .types import ( VALID_SECRET_NAME, public_or_private_age_key_type, @@ -19,6 +19,10 @@ def remove_user(name: str) -> None: remove_object(sops_users_folder(), name) +def get_user(name: str) -> str: + return read_key(sops_users_folder() / name) + + def list_users() -> list[str]: return list_objects( sops_users_folder(), lambda n: VALID_SECRET_NAME.match(n) is not None @@ -43,6 +47,10 @@ def add_command(args: argparse.Namespace) -> None: add_user(args.user, args.key, args.force) +def get_command(args: argparse.Namespace) -> None: + print(get_user(args.user)) + + def remove_command(args: argparse.Namespace) -> None: remove_user(args.user) @@ -77,6 +85,10 @@ def register_users_parser(parser: argparse.ArgumentParser) -> None: ) add_parser.set_defaults(func=add_command) + get_parser = subparser.add_parser("get", help="get a user public key") + get_parser.add_argument("user", help="the name of the user", type=user_name_type) + get_parser.set_defaults(func=get_command) + remove_parser = subparser.add_parser("remove", help="remove a user") remove_parser.add_argument("user", help="the name of the user", type=user_name_type) remove_parser.set_defaults(func=remove_command) diff --git a/pkgs/clan-cli/tests/test_secrets_cli.py b/pkgs/clan-cli/tests/test_secrets_cli.py index d614c7737..ccdffd799 100644 --- a/pkgs/clan-cli/tests/test_secrets_cli.py +++ b/pkgs/clan-cli/tests/test_secrets_cli.py @@ -36,8 +36,13 @@ def _test_identities( age_keys[0].privkey, ] ) - capsys.readouterr() # empty the buffer + capsys.readouterr() # empty the buffer + cli.run(["secrets", what, "get", "foo"]) + out = capsys.readouterr() # empty the buffer + assert age_keys[0].pubkey in out.out + + capsys.readouterr() # empty the buffer cli.run(["secrets", what, "list"]) out = capsys.readouterr() # empty the buffer assert "foo" in out.out