diff --git a/README.md b/README.md index 59e1be4..0636ebe 100644 --- a/README.md +++ b/README.md @@ -44,13 +44,15 @@ Initial setup of a GAMADV-XTD3 project and GYB project is required to provide ne ```bash $ compiler-admin init -h -usage: compiler-admin init [-h] username +usage: compiler-admin init [-h] [--gam] [--gyb] username positional arguments: - username The user's account name, sans domain. + username A Compiler user account name, sans domain. options: -h, --help show this help message and exit + --gam If provided, initialize a new GAM project. + --gyb If provided, initialize a new GYB project. ``` The `init` commands follows the steps in the [GAMADV-XTD3 Wiki](https://github.com/taers232c/GAMADV-XTD3/wiki/#requirements). diff --git a/compiler_admin/commands/init.py b/compiler_admin/commands/init.py index 862ac8a..d9f6f29 100644 --- a/compiler_admin/commands/init.py +++ b/compiler_admin/commands/init.py @@ -7,44 +7,56 @@ from compiler_admin.services.google import USER_ARCHIVE, CallGAMCommand -CONFIG_DIR = os.environ.get("GAMCFGDIR", "./.config") -CONFIG_PATH = Path(CONFIG_DIR) -CONFIG_PATH_NAME = str(CONFIG_PATH) +GAM_CONFIG_DIR = os.environ.get("GAMCFGDIR", "./.config/gam") +GAM_CONFIG_PATH = Path(GAM_CONFIG_DIR) +GYB_CONFIG_PATH = GAM_CONFIG_PATH.parent / "gyb" -def _clean_config_dir(): - for path in CONFIG_PATH.glob("**/*"): +def _clean_config_dir(config_dir: Path) -> None: + config_dir.mkdir(parents=True, exist_ok=True) + for path in config_dir.glob("**/*"): if path.is_file(): path.unlink() elif path.is_dir(): rmtree(path) -def init(admin_user: str) -> int: +def init(admin_user: str, gam: bool = False, gyb: bool = False) -> int: """Initialize a new GAM project. See https://github.com/taers232c/GAMADV-XTD3/wiki/How-to-Install-Advanced-GAM Args: admin_user (str): The Compiler admin with which to initialize a new project. + + gam (bool): If True, initialize a new GAM project. + + gyb (bool): If True, initialize a new GYB project. + Returns: A value indicating if the operation succeeded or failed. """ - if CONFIG_PATH.exists(): - _clean_config_dir() - - res = CallGAMCommand(("config", "drive_dir", CONFIG_PATH_NAME, "verify")) - res += CallGAMCommand(("create", "project")) - res += CallGAMCommand(("oauth", "create")) - res += CallGAMCommand(("user", admin_user, "check", "serviceaccount")) - - # download GYB installer to config directory - gyb = CONFIG_PATH / "gyb-install.sh" - with gyb.open("w+") as dest: - res += subprocess.call(("curl", "-s", "-S", "-L", "https://gyb-shortn.jaylee.us/gyb-install"), stdout=dest) - - # install, giving values to options that prompt by default - # https://github.com/GAM-team/got-your-back/blob/main/install-gyb.sh - res += subprocess.call((gyb, "-u", admin_user, "-r", USER_ARCHIVE)) + res = RESULT_SUCCESS + + if gam: + _clean_config_dir(GAM_CONFIG_PATH) + # GAM is already installed via pyproject.toml + res += CallGAMCommand(("config", "drive_dir", str(GAM_CONFIG_PATH), "verify")) + res += CallGAMCommand(("create", "project")) + res += CallGAMCommand(("oauth", "create")) + res += CallGAMCommand(("user", admin_user, "check", "serviceaccount")) + + if gyb: + _clean_config_dir(GYB_CONFIG_PATH) + # download GYB installer to config directory + gyb = GYB_CONFIG_PATH / "gyb-install.sh" + with gyb.open("w+") as dest: + res += subprocess.call(("curl", "-s", "-S", "-L", "https://gyb-shortn.jaylee.us/gyb-install"), stdout=dest) + + # install, giving values to some options + # https://github.com/GAM-team/got-your-back/blob/main/install-gyb.sh + # + # use GYB_CONFIG_PATH.parent for the install directory option, otherwise we get a .config/gyb/gyb directory structure + res += subprocess.call((gyb, "-u", admin_user, "-r", USER_ARCHIVE, "-d", str(GYB_CONFIG_PATH.parent))) return RESULT_SUCCESS if res == RESULT_SUCCESS else RESULT_FAILURE diff --git a/compiler_admin/main.py b/compiler_admin/main.py index a45141a..1b24b31 100644 --- a/compiler_admin/main.py +++ b/compiler_admin/main.py @@ -35,10 +35,12 @@ def _subcmd(name, help, add_username_arg=True) -> argparse.ArgumentParser: _subcmd("info", help="Print configuration and debugging information.", add_username_arg=False) - _subcmd( + init_parser = _subcmd( "init", help="Initialize a new admin project. This command should be run once before any others.", ) + init_parser.add_argument("--gam", action="store_true", help="If provided, initialize a new GAM project.") + init_parser.add_argument("--gyb", action="store_true", help="If provided, initialize a new GYB project.") _subcmd("create", help="Create a new user in the Compiler domain.") @@ -70,7 +72,7 @@ def _subcmd(name, help, add_username_arg=True) -> argparse.ArgumentParser: elif args.command == "delete": return delete(args.username) elif args.command == "init": - return init(args.username) + return init(args.username, gam=args.gam, gyb=args.gyb) elif args.command == "offboard": return offboard(args.username, args.alias) elif args.command == "restore": diff --git a/compose.yaml b/compose.yaml index ff29f72..5a23c22 100644 --- a/compose.yaml +++ b/compose.yaml @@ -5,9 +5,9 @@ services: dockerfile: .devcontainer/Dockerfile image: compiler_admin:dev environment: - GAMCFGDIR: /home/compiler/.config/gam + GAMCFGDIR: /home/compiler/.config/compiler-admin/gam entrypoint: sleep infinity volumes: - - ./:/home/compiler/admin - - ./.config/:/home/compiler/.config/gam - - ./.downloads/:/home/compiler/Downloads + - .:/home/compiler/admin + - ./.config:/home/compiler/.config/compiler-admin + - ./.downloads:/home/compiler/Downloads diff --git a/tests/commands/test_init.py b/tests/commands/test_init.py index 311dfda..88f84dc 100644 --- a/tests/commands/test_init.py +++ b/tests/commands/test_init.py @@ -1,11 +1,21 @@ import pytest -from compiler_admin.commands.init import init, __name__ as MODULE +from compiler_admin.commands.init import _clean_config_dir, init, __name__ as MODULE @pytest.fixture -def mock_CONFIG_PATH(mocker): - return mocker.patch(f"{MODULE}.CONFIG_PATH") +def mock_rmtree(mocker): + return mocker.patch(f"{MODULE}.rmtree") + + +@pytest.fixture +def mock_GAM_CONFIG_PATH(mocker): + return mocker.patch(f"{MODULE}.GAM_CONFIG_PATH") + + +@pytest.fixture +def mock_GYB_CONFIG_PATH(mocker): + return mocker.patch(f"{MODULE}.GYB_CONFIG_PATH") @pytest.fixture @@ -23,23 +33,43 @@ def mock_subprocess_call(mocker): return mocker.patch(f"{MODULE}.subprocess.call") -def test_init_config_path_exists(mock_CONFIG_PATH, mock_clean_config_dir, mock_google_CallGAMCommand, mock_subprocess_call): - mock_CONFIG_PATH.exists.return_value = True +def test_clean_config_dir(mocker, mock_GAM_CONFIG_PATH, mock_rmtree): + mock_file = mocker.Mock(is_file=mocker.Mock(return_value=True)) + mock_dir = mocker.Mock(is_file=mocker.Mock(return_value=False), is_dir=mocker.Mock(return_value=True)) - init("username") + mock_GAM_CONFIG_PATH.glob.return_value = [mock_file, mock_dir] - mock_clean_config_dir.assert_called_once() - assert mock_google_CallGAMCommand.call_count > 0 - assert mock_subprocess_call.call_count > 0 + _clean_config_dir(mock_GAM_CONFIG_PATH) + mock_GAM_CONFIG_PATH.mkdir.assert_called_once() + mock_GAM_CONFIG_PATH.glob.assert_called_once() + mock_file.is_file.assert_called_once() + mock_file.unlink.assert_called_once() + mock_dir.is_file.assert_called_once() + mock_dir.is_dir.assert_called_once() + mock_rmtree.assert_called_once() + assert mock_dir in mock_rmtree.call_args.args -def test_init_config_path_does_not_exist( - mock_CONFIG_PATH, mock_clean_config_dir, mock_google_CallGAMCommand, mock_subprocess_call -): - mock_CONFIG_PATH.exists.return_value = False +def test_init_default(mock_clean_config_dir, mock_google_CallGAMCommand, mock_subprocess_call): init("username") assert mock_clean_config_dir.call_count == 0 + assert mock_google_CallGAMCommand.call_count == 0 + assert mock_subprocess_call.call_count == 0 + + +def test_init_gam(mock_GAM_CONFIG_PATH, mock_clean_config_dir, mock_google_CallGAMCommand): + init("username", gam=True, gyb=False) + + mock_clean_config_dir.assert_called_once() + assert mock_GAM_CONFIG_PATH in mock_clean_config_dir.call_args.args assert mock_google_CallGAMCommand.call_count > 0 + + +def test_init_gyb(mock_GYB_CONFIG_PATH, mock_clean_config_dir, mock_subprocess_call): + init("username", gam=False, gyb=True) + + mock_clean_config_dir.assert_called_once() + assert mock_GYB_CONFIG_PATH in mock_clean_config_dir.call_args.args assert mock_subprocess_call.call_count > 0 diff --git a/tests/test_main.py b/tests/test_main.py index 3109e39..4b1b870 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -108,12 +108,28 @@ def test_main_info_default(mock_commands_info): mock_commands_info.assert_called_once() -def test_main_init(mock_commands_init): +def test_main_init_default(mock_commands_init): main(argv=["init", "username"]) mock_commands_init.assert_called_once() - call_args = mock_commands_init.call_args.args - assert "username" in call_args + assert mock_commands_init.call_args.args == ("username",) + assert mock_commands_init.call_args.kwargs == {"gam": False, "gyb": False} + + +def test_main_init_gam(mock_commands_init): + main(argv=["init", "username", "--gam"]) + + mock_commands_init.assert_called_once() + assert mock_commands_init.call_args.args == ("username",) + assert mock_commands_init.call_args.kwargs == {"gam": True, "gyb": False} + + +def test_main_init_gyb(mock_commands_init): + main(argv=["init", "username", "--gyb"]) + + mock_commands_init.assert_called_once() + assert mock_commands_init.call_args.args == ("username",) + assert mock_commands_init.call_args.kwargs == {"gam": False, "gyb": True} def test_main_init_no_username(mock_commands_init):