From f00746a36d20a3f66a305c4b90b5d8c2d3746386 Mon Sep 17 00:00:00 2001 From: realaravinth Date: Thu, 30 Jun 2022 01:10:55 +0530 Subject: [PATCH] feat: notify user on instance creation --- infrastructure/migrations/0006_job.py | 36 +++++++++++++++++ infrastructure/models.py | 21 +++++++++- infrastructure/tests.py | 31 +++++++++++++-- infrastructure/utils.py | 56 +++++++++++++++++++++++++-- requirements.txt | 1 + 5 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 infrastructure/migrations/0006_job.py diff --git a/infrastructure/migrations/0006_job.py b/infrastructure/migrations/0006_job.py new file mode 100644 index 0000000..4561f62 --- /dev/null +++ b/infrastructure/migrations/0006_job.py @@ -0,0 +1,36 @@ +# Generated by Django 4.0.3 on 2022-06-29 18:30 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("dash", "0006_auto_20220619_0800"), + ("infrastructure", "0005_remove_instancecreated_gitea_password"), + ] + + operations = [ + migrations.CreateModel( + name="Job", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("job_type", models.CharField(max_length=10, verbose_name="Job Type")), + ( + "instance", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="dash.instance" + ), + ), + ], + ), + ] diff --git a/infrastructure/models.py b/infrastructure/models.py index 1e0673d..c71b26c 100644 --- a/infrastructure/models.py +++ b/infrastructure/models.py @@ -12,11 +12,30 @@ # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from django.db import models +from enum import Enum, unique +from django.db import models from dash.models import Instance +@unique +class JobType(Enum): + PING = "ping" + + def __str__(self): + return self.name + + class InstanceCreated(models.Model): instance = models.ForeignKey(Instance, on_delete=models.PROTECT) created = models.BooleanField(default=False) + + +class Job(models.Model): + instance = models.ForeignKey(Instance, on_delete=models.CASCADE) + + job_type = models.CharField( + "Job Type", + max_length=10, + null=False, + ) diff --git a/infrastructure/tests.py b/infrastructure/tests.py index cb52910..cb59015 100644 --- a/infrastructure/tests.py +++ b/infrastructure/tests.py @@ -15,11 +15,11 @@ import shutil import time import os -import requests from io import StringIO -from urllib.parse import urlparse, urlunparse +from urllib.parse import urlunparse from pathlib import Path +import requests from django.test import TestCase, Client, override_settings from django.conf import settings from django.core.management import call_command @@ -31,8 +31,8 @@ from accounts.tests import register_util, login_util from dash.tests import create_configurations, create_instance_util from infrastructure.management.commands.vm import translate_sizes -from .utils import Infra -from .models import InstanceCreated +from .utils import Infra, Worker +from .models import InstanceCreated, Job, JobType def custom_config(test_name: str): @@ -187,3 +187,26 @@ class InfraUtilTest(TestCase): # run delete VM command to crudely check idempotency call_command("vm", "delete", subdomain) + + def test_worker(self): + subdomain = "gitea" # yes, gitea.hostea.org exists. will use it till I + # figure out how to use requests_mock within django + c = Client() + login_util(self, c, "accounts.home") + create_instance_util( + t=self, c=c, instance_name=subdomain, config=self.instance_config[0] + ) + + instance = Instance.objects.get(name=subdomain) + job = Job.objects.create(instance=instance, job_type=JobType.PING) + gitea_uri = Infra.get_gitea_uri(instance=instance) + print(f"mocking {gitea_uri}") + + w = Worker(job=job) + w.start() + time.sleep(5) + self.assertEqual(w.is_alive(), False) + w.join() + self.assertEqual( + Job.objects.filter(instance=instance, job_type=JobType.PING).exists(), True + ) diff --git a/infrastructure/utils.py b/infrastructure/utils.py index 68ef92d..b66f741 100644 --- a/infrastructure/utils.py +++ b/infrastructure/utils.py @@ -15,17 +15,65 @@ import os import shutil import yaml +import requests from pathlib import Path +from threading import Thread, Event +from time import sleep from django.utils.crypto import get_random_string from django.template.loader import render_to_string +from django.core.mail import send_mail from django.conf import settings from git import Repo, Commit from git.exc import InvalidGitRepositoryError from dash.models import Instance -from .models import InstanceCreated +from infrastructure.models import InstanceCreated, JobType, Job + + +class Worker(Thread): + def __init__(self, job: Job): + self.job = job + super().__init__() + + ######### self.daemon = True + + def run(self): + gitea_uri = Infra.get_gitea_uri(instance=self.job.instance) + woodpecker = Infra.get_woodpecker_hostname(instance=self.job.instance) + while True: + try: + print(f"[ping] Trying to reach {gitea_uri}") + resp = requests.get(gitea_uri) + print(resp.status_code) + if resp.status_code == 200: + break + except Exception: + return False + sleep(10) + + print("sending email") + job = self.job + self.job = None + email = job.instance.owned_by.email + send_mail( + subject="[Hostea] Your Hostea instance is now online!", + message=f""" +Hello, + +The deployment job has run to completion and your Hostea instance is now online! +Credentials to admin account was sent in an earlier email, please contact +support if didn't receive it. + +Gitea: {gitea_uri} +Woodpecker CI: {woodpecker} +""", + from_email="No reply Hostea", # TODO read from settings.py + recipient_list=[email], + ) + job.delete() + print("job deleted") def create_vm_if_not_exists(instance: Instance) -> (str, Commit): @@ -35,13 +83,15 @@ def create_vm_if_not_exists(instance: Instance) -> (str, Commit): infra = Infra() if not InstanceCreated.objects.filter(instance=instance).exists(): (gitea_password, commit) = infra.add_vm(instance=instance) - instance = InstanceCreated.objects.create(instance=instance, created=True) - instance.save() + InstanceCreated.objects.create(instance=instance, created=True) + job = Job.objects.create(instance=instance, job_type=str(JobType.PING)) + Worker(job=job).start() return (gitea_password, commit) else: if str.strip(infra.get_flavor(instance=instance)) != str.strip( infra.translate_size(instance=instance) ): + # Worker.init_global() return infra.add_vm(instance=instance) return None diff --git a/requirements.txt b/requirements.txt index 896b2a5..ccdcb32 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,6 +37,7 @@ pynvim==0.4.3 pytz==2022.1 PyYAML==6.0 requests==2.27.1 +six==1.16.0 smmap==5.0.0 sqlparse==0.4.2 stripe==3.4.0