From 1e1b94239eed6d41c5e57523c9a5d17845cdd3ba Mon Sep 17 00:00:00 2001 From: David Boreham Date: Tue, 31 Mar 2026 08:24:10 -0600 Subject: [PATCH] Add info subcommand for debugging --- machine/config.py | 8 ++- machine/main.py | 3 +- machine/subcommands/info.py | 29 +++++++++ tests/test_cli_integration.py | 116 ++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 machine/subcommands/info.py diff --git a/machine/config.py b/machine/config.py index ef478cc..7552b07 100644 --- a/machine/config.py +++ b/machine/config.py @@ -36,10 +36,14 @@ class _loaded_config: c: any = None -def _load_config_data(config_file_name: str): +def resolve_config_file_path(config_file_name: str) -> str: if not config_file_name: config_file_name = constants.default_config_file_path - config_path = Path(os.path.expanduser(config_file_name)) + return str(Path(os.path.expanduser(config_file_name))) + + +def _load_config_data(config_file_name: str): + config_path = Path(resolve_config_file_path(config_file_name)) if not config_path.exists(): fatal_error(f"Error: Config file: {config_path} not found") config = _expand_env_vars(yaml().load(open(config_path, "r"))) diff --git a/machine/main.py b/machine/main.py index c1a834e..f5835a2 100644 --- a/machine/main.py +++ b/machine/main.py @@ -7,7 +7,7 @@ from machine.di import d from machine.log import output from machine.providers import create_provider -from machine.subcommands import create, destroy, list, projects, ssh_keys, domains, list_domain, types, status +from machine.subcommands import create, destroy, info, list, projects, ssh_keys, domains, list_domain, types, status from machine.types import CliOptions, MainCmdCtx from machine.util import load_session_id @@ -52,6 +52,7 @@ def version(context): main.add_command(create.command, "create") main.add_command(destroy.command, "destroy") main.add_command(domains.command, "domains") +main.add_command(info.command, "info") main.add_command(list.command, "list") main.add_command(list_domain.command, "list-domain") main.add_command(projects.command, "projects") diff --git a/machine/subcommands/info.py b/machine/subcommands/info.py new file mode 100644 index 0000000..40428d1 --- /dev/null +++ b/machine/subcommands/info.py @@ -0,0 +1,29 @@ +import click + +from machine.config import resolve_config_file_path +from machine.constants import default_session_id_file_path +from machine.log import output +from machine.providers import KNOWN_PROVIDERS +from machine.types import MainCmdCtx + + +@click.command(help="Show diagnostic information about the current configuration") +@click.pass_context +def command(context): + command_context: MainCmdCtx = context.obj + config_file_option = context.parent.params.get("config_file") + config_file = resolve_config_file_path(config_file_option) + + output(f"Config file: {config_file}") + output("") + output("Config file contents:") + with open(config_file, "r") as f: + output(f.read().rstrip()) + + output("") + output(f"Session ID file: {default_session_id_file_path}") + output(f"Session ID: {command_context.session_id}") + + output("") + output(f"Supported providers: {', '.join(KNOWN_PROVIDERS)}") + output(f"Active provider: {command_context.config.provider_name}") diff --git a/tests/test_cli_integration.py b/tests/test_cli_integration.py index 2f9c6c8..6b075cb 100644 --- a/tests/test_cli_integration.py +++ b/tests/test_cli_integration.py @@ -39,6 +39,122 @@ def test_help_runs(self): assert "Usage" in result.stdout +class TestInfoCommand: + """Tests for the info diagnostic subcommand.""" + + @pytest.fixture() + def config_dir(self, tmp_path): + return tmp_path + + def test_info_shows_config_file_path(self, config_dir): + config_file = config_dir / "config.yml" + write_config( + config_file, + """\ + digital-ocean: + access-token: fake-token + ssh-key: test-key + machine-size: s-1vcpu-1gb + image: ubuntu-22-04-x64 + region: nyc1 + project: test-project + machines: + test-machine: + new-user-name: testuser + """, + ) + result = run_machine("--config-file", str(config_file), "info") + assert result.returncode == 0 + assert f"Config file: {config_file}" in result.stdout + + def test_info_shows_config_file_contents(self, config_dir): + config_file = config_dir / "config.yml" + write_config( + config_file, + """\ + digital-ocean: + access-token: fake-token + ssh-key: test-key + machine-size: s-1vcpu-1gb + image: ubuntu-22-04-x64 + region: nyc1 + project: test-project + machines: + test-machine: + new-user-name: testuser + """, + ) + result = run_machine("--config-file", str(config_file), "info") + assert result.returncode == 0 + assert "access-token: fake-token" in result.stdout + assert "ssh-key: test-key" in result.stdout + + def test_info_shows_session_id(self, config_dir): + config_file = config_dir / "config.yml" + write_config( + config_file, + """\ + digital-ocean: + access-token: fake-token + ssh-key: test-key + machine-size: s-1vcpu-1gb + image: ubuntu-22-04-x64 + region: nyc1 + project: test-project + machines: + test-machine: + new-user-name: testuser + """, + ) + result = run_machine("--config-file", str(config_file), "info") + assert result.returncode == 0 + assert "Session ID file:" in result.stdout + assert "Session ID:" in result.stdout + + def test_info_shows_providers(self, config_dir): + config_file = config_dir / "config.yml" + write_config( + config_file, + """\ + digital-ocean: + access-token: fake-token + ssh-key: test-key + machine-size: s-1vcpu-1gb + image: ubuntu-22-04-x64 + region: nyc1 + project: test-project + machines: + test-machine: + new-user-name: testuser + """, + ) + result = run_machine("--config-file", str(config_file), "info") + assert result.returncode == 0 + assert "digital-ocean" in result.stdout + assert "vultr" in result.stdout + assert "Active provider: digital-ocean" in result.stdout + + def test_info_shows_vultr_as_active_provider(self, config_dir): + config_file = config_dir / "config.yml" + write_config( + config_file, + """\ + vultr: + api-key: fake-key + ssh-key: test-key + machine-size: vc2-1c-1gb + image: ubuntu-22-04-x64 + region: ewr + machines: + test-machine: + new-user-name: testuser + """, + ) + result = run_machine("--config-file", str(config_file), "info") + assert result.returncode == 0 + assert "Active provider: vultr" in result.stdout + + class TestEnvVarExpansionIntegration: """End-to-end tests that verify environment variable expansion works when the actual machine tool is invoked with a config file."""