From b2fc391677ae4633b648b6afb37f6cb3aaa758ac Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Thu, 5 Sep 2019 09:47:43 -0700 Subject: [PATCH] lifted: Add delete_profile function and tests Also adds a helper to providers, _get_profile_path, so that the code doesn't need to be repeated in all the functions. --- src/lifted/providers.py | 67 ++++++++++++++++++++++++---------- tests/lifted/test_providers.py | 30 ++++++++++++++- 2 files changed, 77 insertions(+), 20 deletions(-) diff --git a/src/lifted/providers.py b/src/lifted/providers.py index e4688da2..729616aa 100644 --- a/src/lifted/providers.py +++ b/src/lifted/providers.py @@ -23,6 +23,35 @@ import stat import pylorax.api.toml as toml +def _get_profile_path(ucfg, provider_name, profile, exists=True): + """Helper to return the directory and path for a provider's profile file + + :param ucfg: upload config + :type ucfg: object + :param provider_name: the name of the cloud provider, e.g. "azure" + :type provider_name: str + :param profile: the name of the profile to save + :type profile: str != "" + :returns: Full path of the profile .toml file + :rtype: str + :raises: ValueError when passed invalid settings or an invalid profile name + :raises: RuntimeError when the provider or profile couldn't be found + """ + if not profile: + raise ValueError("Profile name cannot be empty!") + if not provider_name: + raise ValueError("Provider name cannot be empty!") + + directory = os.path.join(ucfg["settings_dir"], provider_name) + # create the settings directory if it doesn't exist + os.makedirs(directory, exist_ok=True) + + path = os.path.join(directory, f"{profile}.toml") + if exists and not os.path.isfile(path): + raise RuntimeError(f'Couldn\'t find profile "{profile}"!') + + return path + def resolve_provider(ucfg, provider_name): """Get information about the specified provider as defined in that provider's `provider.toml`, including the provider's display name and expected @@ -149,16 +178,9 @@ def save_settings(ucfg, provider_name, profile, settings): :type settings: dict :raises: ValueError when passed invalid settings or an invalid profile name """ - if not profile: - raise ValueError("Profile name cannot be empty!") + path = _get_profile_path(ucfg, provider_name, profile, exists=False) validate_settings(ucfg, provider_name, settings, image_name=None) - directory = os.path.join(ucfg["settings_dir"], provider_name) - - # create the settings directory if it doesn't exist - os.makedirs(directory, exist_ok=True) - - path = os.path.join(directory, f"{profile}.toml") # touch the TOML file if it doesn't exist if not os.path.isfile(path): open(path, "a").close() @@ -189,19 +211,26 @@ def load_settings(ucfg, provider_name, profile): This also calls validate_settings on the loaded settings, potentially raising an error if the saved settings are invalid. """ - if not profile: - raise ValueError("Profile name cannot be empty!") - if not provider_name: - raise ValueError("Provider name cannot be empty!") - directory = os.path.join(ucfg["settings_dir"], provider_name) - if not os.path.isdir(directory): - raise RuntimeError(f'Couldn\'t find provider "{provider_name}"!') - - path = os.path.join(directory, f"{profile}.toml") - if not os.path.isfile(path): - raise RuntimeError(f'Couldn\'t find provider "{provider_name}"!') + path = _get_profile_path(ucfg, provider_name, profile) with open(path) as file: settings = toml.load(file) validate_settings(ucfg, provider_name, settings) return settings + +def delete_profile(ucfg, provider_name, profile): + """Delete a provider's profile settings file + + :param ucfg: upload config + :type ucfg: object + :param provider_name: the name of the cloud provider, e.g. "azure" + :type provider_name: str + :param profile: the name of the profile to save + :type profile: str != "" + :raises: ValueError when passed invalid settings or an invalid profile name + :raises: RuntimeError when the provider or profile couldn't be found + """ + path = _get_profile_path(ucfg, provider_name, profile) + + if os.path.exists(path): + os.unlink(path) diff --git a/tests/lifted/test_providers.py b/tests/lifted/test_providers.py index 0e79b61e..d644a20b 100644 --- a/tests/lifted/test_providers.py +++ b/tests/lifted/test_providers.py @@ -21,7 +21,7 @@ import unittest import lifted.config from lifted.providers import list_providers, resolve_provider, resolve_playbook_path, save_settings -from lifted.providers import load_profiles, validate_settings, load_settings +from lifted.providers import load_profiles, validate_settings, load_settings, delete_profile import pylorax.api.config from pylorax.sysutils import joinpaths @@ -118,3 +118,31 @@ class ProvidersTestCase(unittest.TestCase): for p in list_providers(self.config["upload"]): settings = load_settings(self.config["upload"], p, test_profiles[p][0]) self.assertEqual(settings, test_profiles[p][1]) + + # This *must* run after all the save and load tests, but *before* the actual delete test + # _zz_ ensures this happens + def test_zz_delete_settings_errors(self): + """Test raising the correct errors when deleting""" + with self.assertRaises(ValueError): + delete_profile(self.config["upload"], "", "") + + with self.assertRaises(ValueError): + delete_profile(self.config["upload"], "", "default") + + with self.assertRaises(ValueError): + delete_profile(self.config["upload"], "azure", "") + + with self.assertRaises(RuntimeError): + delete_profile(self.config["upload"], "azure", "missing-test") + + # This *must* run after all the save and load tests, _zzz_ ensures this happens + def test_zzz_delete_settings(self): + """Test raising the correct errors when deleting""" + # Ensure the profile is really there + settings = load_settings(self.config["upload"], "azure", test_profiles["azure"][0]) + self.assertEqual(settings, test_profiles["azure"][1]) + + delete_profile(self.config["upload"], "azure", test_profiles["azure"][0]) + + with self.assertRaises(RuntimeError): + load_settings(self.config["upload"], "azure", test_profiles["azure"][0])