diff --git a/infrastructure/management/commands/vm.py b/infrastructure/management/commands/vm.py index 540e53d..f92ac94 100644 --- a/infrastructure/management/commands/vm.py +++ b/infrastructure/management/commands/vm.py @@ -23,10 +23,23 @@ from oauth2_provider.models import get_application_model from oauth2_provider.generators import generate_client_id, generate_client_secret from dash.models import InstanceConfiguration, Instance -from dash.utils import create_instance +from dash.utils import create_instance, VmException, VmErrors from infrastructure.utils import create_vm_if_not_exists +def translate_sizes(flavor: str): + if flavor == "small": + size = "s1-2" + elif flavor == "medium": + size = "s1-4" + elif flavor == "large": + size = "s1-8" + else: + print("flavour no match") + size = flavor + return size + + @unique class Actions(Enum): CREATE = "create" @@ -73,22 +86,26 @@ class Command(BaseCommand): flavor = options[self.flavor_key] vm_name = options[self.vm_name_key] - if flavor == "small": - size = "s1-2" - elif flavor == "medium": - size = "s1-4" - elif flavor == "large": - size = "s1-8" - else: - self.stdout.write(self.style.WARN("flavour no match")) - size = flavor + size = translate_sizes(flavor) + user = get_user_model().objects.get(username=owner) try: instance = create_instance( vm_name=vm_name, configuration_name=size, user=user ) - create_vm_if_not_exists(instance) + gitea_password = create_vm_if_not_exists(instance) print("Instance created") + print(f"Gitea admin password: {gitea_password}") + except VmException as e: + if e.code == VmErrors.NAME_EXISTS: + instance = Instance.objects.get(name=vm_name) + if instance.configuration_id.name != size: + instance.configuration_id = InstanceConfiguration.objects.get( + name=size + ) + instance.save() + else: + self.stderr.write(self.style.ERROR(f"error: {str(e)}")) except Exception as e: self.stderr.write(self.style.ERROR(f"error: {str(e)}")) @@ -96,8 +113,9 @@ class Command(BaseCommand): from infrastructure.utils import delete_vm vm_name = options[self.vm_name_key] - instance = Instance.objects.get(name=vm_name) - delete_vm(instance=instance, owner=instance.owned_by.username) + if Instance.objects.filter(name=vm_name).exists(): + instance = Instance.objects.get(name=vm_name) + delete_vm(instance=instance, owner=instance.owned_by.username) def handle(self, *args, **options): for i in [self.action_key, self.vm_name_key]: diff --git a/infrastructure/tests.py b/infrastructure/tests.py index f4a748e..d3320d2 100644 --- a/infrastructure/tests.py +++ b/infrastructure/tests.py @@ -29,9 +29,10 @@ from git import Repo from dash.models import Instance, InstanceConfiguration from accounts.tests import register_util, login_util from dash.tests import create_configurations, create_instance_util -from infrastructure.models import InstanceCreated +from infrastructure.management.commands.vm import translate_sizes from .utils import Infra +from .models import InstanceCreated def custom_config(test_name: str): @@ -140,9 +141,30 @@ class InfraUtilTest(TestCase): self.assertEqual(instance_created.created, True) + # run create vm command again with same configuration to crudely check idempotency + call_command( + "vm", "create", vm_name, f"--owner={self.username}", "--flavor=medium" + ) + + # run create vm command again with different configuration but same name + # to crudely check idempotency + old_size = instance.configuration_id + call_command( + "vm", "create", vm_name, f"--owner={self.username}", "--flavor=large" + ) + instance.refresh_from_db() + self.assertEqual( + str.strip(instance.configuration_id.name) + == str.strip(translate_sizes("large")), + True, + ) + call_command("vm", "delete", vm_name) out = stdout.getvalue() self.assertEqual(Instance.objects.filter(name=vm_name).exists(), False) host_vars_dir = infra._host_vars_dir(subdomain) self.assertEqual(host_vars_dir.exists(), False) + + # run delete VM command to crudely check idempotency + call_command("vm", "delete", vm_name) diff --git a/infrastructure/utils.py b/infrastructure/utils.py index 301e210..79db0b3 100644 --- a/infrastructure/utils.py +++ b/infrastructure/utils.py @@ -27,14 +27,16 @@ from dash.models import Instance from .models import InstanceCreated -def create_vm_if_not_exists(instance: Instance): +def create_vm_if_not_exists(instance: Instance) -> str: + """ + Create VM utility. Gitea password is returned + """ infra = Infra() if not InstanceCreated.objects.filter(instance=instance).exists(): gitea_password = infra.add_vm(instance=instance) - instance = InstanceCreated.objects.create( - instance=instance, gitea_password=gitea_password, created=True - ) + instance = InstanceCreated.objects.create(instance=instance, created=True) instance.save() + return gitea_password def delete_vm(instance: Instance, owner: str): @@ -101,8 +103,7 @@ class Infra: """ utility method: hostscript file for a subdomain """ - path = self.repo_path.joinpath(f"hosts-scripts/{subdomain}-host.sh") - return path + return self.repo_path.joinpath(f"hosts-scripts/{subdomain}-host.sh") def write_hostscript(self, subdomain: str, content: str): """ @@ -246,14 +247,18 @@ class Infra: self._pull() subdomain = instance.name - host_vars_dir = self._host_vars_dir(subdomain) - shutil.rmtree(host_vars_dir) + try: - backup = self._backup_path(subdomain) - os.remove(backup) + host_vars_dir = self._host_vars_dir(subdomain) + shutil.rmtree(host_vars_dir) - service = self._service_path(subdomain) - os.remove(service) + backup = self._backup_path(subdomain) + os.remove(backup) + + service = self._service_path(subdomain) + os.remove(service) + except FileNotFoundError: + pass hostscript = self._hostscript_path(subdomain) with open(hostscript, "w", encoding="utf-8") as f: