Environments: remove environments created with SpackYAMLErrors (#40878)
This commit is contained in:
		| @@ -402,7 +402,7 @@ def env_remove(args): | |||||||
|         try: |         try: | ||||||
|             env = ev.read(env_name) |             env = ev.read(env_name) | ||||||
|             read_envs.append(env) |             read_envs.append(env) | ||||||
|         except spack.config.ConfigFormatError: |         except (spack.config.ConfigFormatError, ev.SpackEnvironmentConfigError): | ||||||
|             bad_envs.append(env_name) |             bad_envs.append(env_name) | ||||||
| 
 | 
 | ||||||
|     if not args.yes_to_all: |     if not args.yes_to_all: | ||||||
| @@ -570,8 +570,8 @@ def env_update_setup_parser(subparser): | |||||||
| def env_update(args): | def env_update(args): | ||||||
|     manifest_file = ev.manifest_file(args.update_env) |     manifest_file = ev.manifest_file(args.update_env) | ||||||
|     backup_file = manifest_file + ".bkp" |     backup_file = manifest_file + ".bkp" | ||||||
|     needs_update = not ev.is_latest_format(manifest_file) |  | ||||||
| 
 | 
 | ||||||
|  |     needs_update = not ev.is_latest_format(manifest_file) | ||||||
|     if not needs_update: |     if not needs_update: | ||||||
|         tty.msg('No update needed for the environment "{0}"'.format(args.update_env)) |         tty.msg('No update needed for the environment "{0}"'.format(args.update_env)) | ||||||
|         return |         return | ||||||
|   | |||||||
| @@ -339,6 +339,7 @@ | |||||||
| from .environment import ( | from .environment import ( | ||||||
|     TOP_LEVEL_KEY, |     TOP_LEVEL_KEY, | ||||||
|     Environment, |     Environment, | ||||||
|  |     SpackEnvironmentConfigError, | ||||||
|     SpackEnvironmentError, |     SpackEnvironmentError, | ||||||
|     SpackEnvironmentViewError, |     SpackEnvironmentViewError, | ||||||
|     activate, |     activate, | ||||||
| @@ -372,6 +373,7 @@ | |||||||
| __all__ = [ | __all__ = [ | ||||||
|     "TOP_LEVEL_KEY", |     "TOP_LEVEL_KEY", | ||||||
|     "Environment", |     "Environment", | ||||||
|  |     "SpackEnvironmentConfigError", | ||||||
|     "SpackEnvironmentError", |     "SpackEnvironmentError", | ||||||
|     "SpackEnvironmentViewError", |     "SpackEnvironmentViewError", | ||||||
|     "activate", |     "activate", | ||||||
|   | |||||||
| @@ -342,7 +342,7 @@ def create_in_dir( | |||||||
| 
 | 
 | ||||||
|         manifest.flush() |         manifest.flush() | ||||||
| 
 | 
 | ||||||
|     except spack.config.ConfigFormatError as e: |     except (spack.config.ConfigFormatError, SpackEnvironmentConfigError) as e: | ||||||
|         shutil.rmtree(manifest_dir) |         shutil.rmtree(manifest_dir) | ||||||
|         raise e |         raise e | ||||||
| 
 | 
 | ||||||
| @@ -396,7 +396,13 @@ def all_environments(): | |||||||
| 
 | 
 | ||||||
| def _read_yaml(str_or_file): | def _read_yaml(str_or_file): | ||||||
|     """Read YAML from a file for round-trip parsing.""" |     """Read YAML from a file for round-trip parsing.""" | ||||||
|  |     try: | ||||||
|         data = syaml.load_config(str_or_file) |         data = syaml.load_config(str_or_file) | ||||||
|  |     except syaml.SpackYAMLError as e: | ||||||
|  |         raise SpackEnvironmentConfigError( | ||||||
|  |             f"Invalid environment configuration detected: {e.message}" | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|     filename = getattr(str_or_file, "name", None) |     filename = getattr(str_or_file, "name", None) | ||||||
|     default_data = spack.config.validate(data, spack.schema.env.schema, filename) |     default_data = spack.config.validate(data, spack.schema.env.schema, filename) | ||||||
|     return data, default_data |     return data, default_data | ||||||
| @@ -2960,3 +2966,7 @@ class SpackEnvironmentError(spack.error.SpackError): | |||||||
| 
 | 
 | ||||||
| class SpackEnvironmentViewError(SpackEnvironmentError): | class SpackEnvironmentViewError(SpackEnvironmentError): | ||||||
|     """Class for errors regarding view generation.""" |     """Class for errors regarding view generation.""" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SpackEnvironmentConfigError(SpackEnvironmentError): | ||||||
|  |     """Class for Spack environment-specific configuration errors.""" | ||||||
|   | |||||||
| @@ -1006,21 +1006,7 @@ def test_env_with_included_configs_precedence(tmp_path): | |||||||
|     assert any(x.satisfies("libelf@0.8.10") for x in specs) |     assert any(x.satisfies("libelf@0.8.10") for x in specs) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_bad_env_yaml_format(environment_from_manifest): | @pytest.mark.regression("39248") | ||||||
|     with pytest.raises(spack.config.ConfigFormatError) as e: |  | ||||||
|         environment_from_manifest( |  | ||||||
|             """\ |  | ||||||
| spack: |  | ||||||
|   spacks: |  | ||||||
|     - mpileaks |  | ||||||
| """ |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         assert "'spacks' was unexpected" in str(e) |  | ||||||
| 
 |  | ||||||
|     assert "test" not in env("list") |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def test_bad_env_yaml_format_remove(mutable_mock_env_path): | def test_bad_env_yaml_format_remove(mutable_mock_env_path): | ||||||
|     badenv = "badenv" |     badenv = "badenv" | ||||||
|     env("create", badenv) |     env("create", badenv) | ||||||
| @@ -1037,6 +1023,55 @@ def test_bad_env_yaml_format_remove(mutable_mock_env_path): | |||||||
|     assert badenv not in env("list") |     assert badenv not in env("list") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @pytest.mark.regression("39248") | ||||||
|  | @pytest.mark.parametrize( | ||||||
|  |     "error,message,contents", | ||||||
|  |     [ | ||||||
|  |         ( | ||||||
|  |             spack.config.ConfigFormatError, | ||||||
|  |             "not of type", | ||||||
|  |             """\ | ||||||
|  | spack: | ||||||
|  |   specs: mpi@2.0 | ||||||
|  | """, | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |             ev.SpackEnvironmentConfigError, | ||||||
|  |             "duplicate key", | ||||||
|  |             """\ | ||||||
|  | spack: | ||||||
|  |   packages: | ||||||
|  |     all: | ||||||
|  |       providers: | ||||||
|  |         mpi: [mvapich2] | ||||||
|  |         mpi: [mpich] | ||||||
|  | """, | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |             spack.config.ConfigFormatError, | ||||||
|  |             "'specks' was unexpected", | ||||||
|  |             """\ | ||||||
|  | spack: | ||||||
|  |   specks: | ||||||
|  |     - libdwarf | ||||||
|  | """, | ||||||
|  |         ), | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  | def test_bad_env_yaml_create_fails(tmp_path, mutable_mock_env_path, error, message, contents): | ||||||
|  |     """Ensure creation with invalid yaml does NOT create or leave the environment.""" | ||||||
|  |     filename = tmp_path / ev.manifest_name | ||||||
|  |     filename.write_text(contents) | ||||||
|  |     env_name = "bad_env" | ||||||
|  |     with pytest.raises(error, match=message): | ||||||
|  |         env("create", env_name, str(filename)) | ||||||
|  | 
 | ||||||
|  |     assert env_name not in env("list") | ||||||
|  |     manifest = mutable_mock_env_path / env_name / ev.manifest_name | ||||||
|  |     assert not os.path.exists(str(manifest)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.mark.regression("39248") | ||||||
| @pytest.mark.parametrize("answer", ["-y", ""]) | @pytest.mark.parametrize("answer", ["-y", ""]) | ||||||
| def test_multi_env_remove(mutable_mock_env_path, monkeypatch, answer): | def test_multi_env_remove(mutable_mock_env_path, monkeypatch, answer): | ||||||
|     """Test removal (or not) of a valid and invalid environment""" |     """Test removal (or not) of a valid and invalid environment""" | ||||||
| @@ -1048,7 +1083,7 @@ def test_multi_env_remove(mutable_mock_env_path, monkeypatch, answer): | |||||||
|         env("create", e) |         env("create", e) | ||||||
| 
 | 
 | ||||||
|     # Ensure the bad environment contains invalid yaml |     # Ensure the bad environment contains invalid yaml | ||||||
|     filename = mutable_mock_env_path / environments[1] / "spack.yaml" |     filename = mutable_mock_env_path / environments[1] / ev.manifest_name | ||||||
|     filename.write_text( |     filename.write_text( | ||||||
|         """\ |         """\ | ||||||
|     - libdwarf |     - libdwarf | ||||||
| @@ -1064,7 +1099,7 @@ def test_multi_env_remove(mutable_mock_env_path, monkeypatch, answer): | |||||||
|     if remove_environment is True: |     if remove_environment is True: | ||||||
|         # Successfully removed (and reported removal) of *both* environments |         # Successfully removed (and reported removal) of *both* environments | ||||||
|         assert not all(e in env("list") for e in environments) |         assert not all(e in env("list") for e in environments) | ||||||
|         assert output.count("Successfully removed") == 2 |         assert output.count("Successfully removed") == len(environments) | ||||||
|     else: |     else: | ||||||
|         # Not removing any of the environments |         # Not removing any of the environments | ||||||
|         assert all(e in env("list") for e in environments) |         assert all(e in env("list") for e in environments) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Tamara Dahlgren
					Tamara Dahlgren