diff --git a/mpsd-software.py b/mpsd-software.py index 1962a139a9a8230b209a0ddec36b806cfc74e396..438dccc4f1ec39abc6c1163ac6468d3dd9f7d8e6 100755 --- a/mpsd-software.py +++ b/mpsd-software.py @@ -15,6 +15,7 @@ import time from pathlib import Path from typing import List, Tuple, Union import re +import shutil # If 'rich' is available ("pip install rich" or "apt-get install python3-rich"), # then use coloured output, otherwise proceed as before @@ -979,13 +980,40 @@ def install_environment( ) -def remove_environment(release, package_sets, target_dir): +def remove_environment(mpsd_release, root_dir, package_sets="NONE", force_remove=False): """Remove release from installation.""" msg = ( - f"Removing release {release} with package_sets {package_sets} from {target_dir}" + f"Removing release {mpsd_release}" + f" with package_sets {package_sets} from {root_dir}" ) - logging.info(msg) - raise NotImplementedError(msg) + logging.warning(msg) + if package_sets == "NONE": + logging.warning( + "Please specify package_sets to remove, or 'ALL' to remove all toolchains" + ) + sys.exit(1) + if "ALL" in package_sets: + # we need to remove the entire release folder + logging.warning( + f"Removing release {mpsd_release} from {root_dir}" + "do you want to continue? [y/n]" + ) + if force_remove or input().lower() == "y": + folders_to_remove = os.listdir(root_dir / mpsd_release) + # skip logs folder + if "logs" in folders_to_remove: + folders_to_remove.remove("logs") + for folder in folders_to_remove: + shutil.rmtree(root_dir / mpsd_release / folder) + sys.exit(0) + for package_set in package_sets: + # we load the spack environment and remove the package_set + spack_env = "" + commands_to_execute = [ + f"source {spack_env}", + f"spack env remove -y {package_set}", + ] + run(" && ".join(commands_to_execute), shell=True, check=True) def start_new_environment(release, from_release, target_dir): @@ -1179,7 +1207,7 @@ def main(): # Check the command and run related function if args.action == "remove": - remove_environment(args.release, args.package_set, root_dir) + remove_environment(args.release, root_dir, args.package_set) elif args.action == "start-new": start_new_environment(args.from_release, args.to_release, root_dir) elif args.action == "install": diff --git a/test_mpsd-software.py b/test_mpsd-software.py index fd0434adaf4f6b583d017cafdcde8f8c1202c687..ce31308efe3dfd5ed377aeb8c7837bc5122ffc5c 100644 --- a/test_mpsd-software.py +++ b/test_mpsd-software.py @@ -344,6 +344,14 @@ def test_install_environment_zlib(): ) assert len(build_log) == 4 + # test that the removal now works + # mod.remove_environment( + # mpsd_release=mpsd_release_to_test, + # package_sets=[package_set_to_test], + # root_dir=root_dir, + # ) + # # ensure that the module files are removed + def test_metadata_logging(tmp_path): """Test that metadata is logged and read correctly.""" @@ -446,32 +454,68 @@ def test_create_log_file_names(): assert build_log_file_name is None -def test_environment_status(tmp_path): - """Test that the environment status is correct.""" - toolchain_map = mod.environment_status("fake-release", tmp_path) - assert toolchain_map is None - # create a fake environment - mpsd_release = "dev-23a" - test_microarch = mod.get_native_microarchitecture() - expected_toolchain_map = {test_microarch: ["foss2021a", "intel2021a"]} +def create_fake_environment(tmp_path, mpsd_release, expected_toolchain_map=None): + """Create a fake environment with toolchains for testing.""" + if not expected_toolchain_map: + test_microarch = mod.get_native_microarchitecture() + expected_toolchain_map = {test_microarch: ["foss2021a", "intel2021a"]} + for microarch in expected_toolchain_map.keys(): toolchain_lmod_folder = ( tmp_path / mpsd_release / microarch / "lmod" / "Core" / "toolchains" ) - toolchain_lmod_folder.mkdir(parents=True) + toolchain_lmod_folder.mkdir(parents=True, exist_ok=True) spack_folder = tmp_path / mpsd_release / microarch / "spack" - spack_folder.mkdir(parents=True) + spack_folder.mkdir(parents=True, exist_ok=True) + logs_folder = tmp_path / mpsd_release / microarch / "logs" + logs_folder.mkdir(parents=True, exist_ok=True) for toolchain in expected_toolchain_map[microarch]: - toolchain_file = toolchain_lmod_folder / f"{toolchain}.lua" - toolchain_file.touch() + toolchain_lua_file = toolchain_lmod_folder / f"{toolchain}.lua" + toolchain_lua_file.touch() - # check that the environment status is correct + return expected_toolchain_map + + +def test_environment_status(tmp_path): + """Test that the environment status is correct.""" + toolchain_map = mod.environment_status("fake-release", tmp_path) + assert toolchain_map is None + mpsd_release = "dev-23a" + expected_toolchain_map = create_fake_environment(tmp_path, mpsd_release) + # check that the environment statuxis is correct toolchain_map = mod.environment_status(mpsd_release, tmp_path) # convert each list to a set to ensure that the order doesn't matter for microarch in expected_toolchain_map.keys(): assert set(toolchain_map[microarch]) == set(expected_toolchain_map[microarch]) +@pytest.mark.skip(reason="not implemented yet") +def test_remove_environment(tmp_path): + """Test that the remove_environment works as expected.""" + mpsd_release = "dev-23a" + # create a fake environment + create_fake_environment(tmp_path, mpsd_release) + # check that the environment status is correct + toolchain_map = mod.environment_status(mpsd_release, tmp_path) + assert toolchain_map is not None + + # test removal without arguments (should sys.exit(1)) + create_fake_environment(tmp_path, mpsd_release) + with pytest.raises(SystemExit): + mod.remove_environment(mpsd_release, tmp_path, force_remove=True) + + # test removal of the complete environment + mod.remove_environment(mpsd_release, tmp_path, ["ALL"], force_remove=True) + toolchain_map = mod.environment_status(mpsd_release, tmp_path) + assert toolchain_map is None + # ensure that logs folder remains + logs_folder = tmp_path / mpsd_release / "logs" + assert logs_folder.exists() + + # test removal of a single toolchain + # done in test_install_environment_zlib + + def test_interface(tmp_path): """Test other things (not implemented yet).""" pass