forked from Hostea/dashboard
feat: vm create management command
parent
d84021915f
commit
0606c4ade0
|
@ -1,3 +1,5 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
from .models import InstanceCreated
|
||||
|
||||
admin.site.register(InstanceCreated)
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
# Create your tests here.
|
||||
|
||||
# Copyright © 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import time
|
||||
import os
|
||||
from io import StringIO
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
import requests
|
||||
|
||||
from django.core import mail
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.management import call_command
|
||||
from django.urls import reverse
|
||||
from django.test import TestCase, Client, override_settings
|
||||
from django.utils.http import urlencode
|
||||
from django.contrib.auth import authenticate
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
from accounts.tests import register_util
|
||||
from dash.models import Instance
|
||||
from infrastructure.models import InstanceCreated, InstanceConfiguration
|
||||
|
||||
|
||||
class VMCommands(TestCase):
|
||||
"""
|
||||
Test command: manage.py create_oidc
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.username = "hosteacustomer"
|
||||
register_util(t=self, username=self.username)
|
||||
self.vm_name = "MyHosteaVM"
|
||||
|
||||
def test_cmd(self):
|
||||
|
||||
Application = get_application_model()
|
||||
|
||||
stdout = StringIO()
|
||||
stderr = StringIO()
|
||||
|
||||
redirect_uri = "http://example.org"
|
||||
app_name = "test_cmd_oidc"
|
||||
|
||||
self.assertEqual(Instance.objects.filter(name=vm_name).exists(), False)
|
||||
# username exists
|
||||
call_command(
|
||||
"vm", "create", self.vm_name, f"--owner={self.username}", "==flavor=medium"
|
||||
)
|
||||
out = stdout.getvalue()
|
||||
|
||||
self.assertIn(f"Instance created", out)
|
||||
|
||||
instance = Instance.objects.get(name=self.vm_name)
|
||||
self.assertEqual(instance.owned_by, self.user)
|
||||
self.assertEqual(
|
||||
instance.configuration_id, InstanceConfiguration.objects.get(name="s1-4")
|
||||
)
|
||||
|
||||
instance_created = InstanceCreated.objects.get(name=self.vm_name)
|
||||
self.assertEqual(instance_created.instance, instance)
|
||||
|
||||
self.assertEqual(instance_created.created, True)
|
|
@ -0,0 +1,103 @@
|
|||
# Copyright © 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
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 infrastructure.utils import create_vm_if_not_exists
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Get user ID from username"
|
||||
action_key = "action"
|
||||
vm_name_key = "vm_name"
|
||||
flavor_key = "flavor"
|
||||
owner_key = "owner"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
self.action_key,
|
||||
type=str,
|
||||
help="VM action: create/delete",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
self.vm_name_key,
|
||||
type=str,
|
||||
help="Name of the VM",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
f"--{self.owner_key}",
|
||||
type=str,
|
||||
help="Owner username",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
f"--{self.flavor_key}",
|
||||
type=str,
|
||||
help="Name of the VM flavor: small, medium, large",
|
||||
)
|
||||
|
||||
def create_vm(self, *args, **options):
|
||||
owner = options[self.owner_key]
|
||||
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
|
||||
user = get_user_model().objects.get(username=owner)
|
||||
instance = create_instance(vm_name=vm_name, configuration_name=size, user=user)
|
||||
create_instance
|
||||
create_vm_if_not_exists(instance)
|
||||
print("Instance created")
|
||||
|
||||
def delete_vm(self, *args, **options):
|
||||
create_vm
|
||||
|
||||
def handle(self, *args, **options):
|
||||
for i in [self.action_key, self.vm_name_key]:
|
||||
if i not in options:
|
||||
self.stdout.write(self.style.ERROR(f"Please provide {i}"))
|
||||
return
|
||||
if options[self.action_key] == "create":
|
||||
for i in [self.flavor_key, self.owner_key]:
|
||||
if i not in options:
|
||||
self.stdout.write(self.style.ERROR(f"Please provide {i}"))
|
||||
return
|
||||
|
||||
self.create_vm(*args, **options)
|
||||
elif options[self.action_key] == "delete":
|
||||
self.delete_vm(*args, **options)
|
||||
|
||||
else:
|
||||
self.stdout.write(
|
||||
self.style.ERROR("Unknown action: {options[self.action_key]}")
|
||||
)
|
||||
return
|
|
@ -12,18 +12,26 @@
|
|||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import time
|
||||
import os
|
||||
import requests
|
||||
from io import StringIO
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
from pathlib import Path
|
||||
|
||||
from django.test import TestCase, Client, override_settings
|
||||
from django.conf import settings
|
||||
from django.core.management import call_command
|
||||
|
||||
from git import Repo
|
||||
|
||||
from dash.models import Instance
|
||||
from .utils import Infra
|
||||
|
||||
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 .utils import Infra
|
||||
|
||||
|
||||
def custom_config(test_name: str):
|
||||
|
@ -99,9 +107,7 @@ class InfraUtilTest(TestCase):
|
|||
|
||||
c = custom_config(test_name="test_add_vm--get-head")
|
||||
path = c["INFRA"]["HOSTEA_REPO"]["PATH"]
|
||||
repo = Repo.clone_from(
|
||||
conf["REMOTE"], path, env={"GIT_SSH_COMMAND": infra.ssh_cmd}
|
||||
)
|
||||
repo = Repo.clone_from(conf["REMOTE"], path, env=infra.env)
|
||||
self.assertEqual(repo.head.commit.hexsha == after_add, True)
|
||||
|
||||
before_rm = infra.repo.head.commit.hexsha
|
||||
|
@ -111,3 +117,26 @@ class InfraUtilTest(TestCase):
|
|||
|
||||
repo.git.pull()
|
||||
self.assertEqual(repo.head.commit.hexsha == after_rm, True)
|
||||
|
||||
vm_name = "cmd_vm"
|
||||
|
||||
stdout = StringIO()
|
||||
stderr = StringIO()
|
||||
|
||||
self.assertEqual(Instance.objects.filter(name=vm_name).exists(), False)
|
||||
# username exists
|
||||
call_command(
|
||||
"vm", "create", vm_name, f"--owner={self.username}", "--flavor=medium"
|
||||
)
|
||||
out = stdout.getvalue()
|
||||
|
||||
instance = Instance.objects.get(name=vm_name)
|
||||
self.assertEqual(instance.owned_by, self.user)
|
||||
self.assertEqual(
|
||||
instance.configuration_id, InstanceConfiguration.objects.get(name="s1-4")
|
||||
)
|
||||
|
||||
instance_created = InstanceCreated.objects.get(instance=instance)
|
||||
self.assertEqual(instance_created.instance, instance)
|
||||
|
||||
self.assertEqual(instance_created.created, True)
|
||||
|
|
|
@ -30,10 +30,10 @@ from .models import InstanceCreated
|
|||
def create_vm_if_not_exists(instance: Instance):
|
||||
infra = Infra()
|
||||
if not InstanceCreated.objects.filter(instance=instance).exists():
|
||||
instance = InstanceCreated.objects.create(instance=instance, created=True)
|
||||
instance.save()
|
||||
gitea_password = infra.add_vm(instance=instance)
|
||||
instance.gitea_password = gitea_password
|
||||
instance = InstanceCreated.objects.create(
|
||||
instance=instance, gitea_password=gitea_password, created=True
|
||||
)
|
||||
instance.save()
|
||||
|
||||
|
||||
|
@ -48,13 +48,12 @@ class Infra:
|
|||
if not self.repo_path.exists():
|
||||
os.makedirs(self.repo_path)
|
||||
|
||||
self.ssh_cmd = f"/usr/bin/ssh -oStrictHostKeyChecking=no -i {conf['SSH_KEY']}"
|
||||
ssh_cmd = f"/usr/bin/ssh -oStrictHostKeyChecking=no -i {conf['SSH_KEY']}"
|
||||
self.env = {"GIT_SSH_COMMAND": ssh_cmd}
|
||||
try:
|
||||
self.repo = Repo(path=self.repo_path)
|
||||
except InvalidGitRepositoryError:
|
||||
self.repo = Repo.clone_from(
|
||||
conf["REMOTE"], self.repo_path, env={"GIT_SSH_COMMAND": self.ssh_cmd}
|
||||
)
|
||||
self.repo = Repo.clone_from(conf["REMOTE"], self.repo_path, env=self.env)
|
||||
|
||||
def _host_vars_dir(self, subdomain: str) -> Path:
|
||||
"""
|
||||
|
@ -120,13 +119,16 @@ class Infra:
|
|||
author="Dashboard Bot <bot@dashboard.hostea.org>",
|
||||
)
|
||||
|
||||
def _pull(self):
|
||||
self.repo.git.pull(env=self.env, rebase="true")
|
||||
|
||||
def add_vm(self, instance: Instance) -> str:
|
||||
"""
|
||||
Add new VM to infrastructure repository
|
||||
|
||||
The gitea user password is returned
|
||||
"""
|
||||
self.repo.git.pull()
|
||||
self._pull()
|
||||
|
||||
subdomain = instance.name
|
||||
host_vars_dir = self._host_vars_dir(subdomain)
|
||||
|
@ -205,14 +207,14 @@ class Infra:
|
|||
)
|
||||
|
||||
self._commit(action="add", subdomain=subdomain)
|
||||
self.repo.git.push()
|
||||
self.repo.git.push(env=self.env)
|
||||
return gitea_password
|
||||
|
||||
def remove_vm(self, instance: Instance):
|
||||
"""
|
||||
Remove a VM from infrastructure repository
|
||||
"""
|
||||
self.repo.git.pull()
|
||||
self._pull()
|
||||
subdomain = instance.name
|
||||
|
||||
host_vars_dir = self._host_vars_dir(subdomain)
|
||||
|
@ -229,4 +231,4 @@ class Infra:
|
|||
),
|
||||
)
|
||||
self._commit(action="rm", subdomain=subdomain)
|
||||
self.repo.git.push()
|
||||
self.repo.git.push(env=self.env)
|
||||
|
|
Loading…
Reference in New Issue