feat: notify user on instance creation
parent
53ec0a3982
commit
f00746a36d
|
@ -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"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -12,11 +12,30 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
from django.db import models
|
from enum import Enum, unique
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
from dash.models import Instance
|
from dash.models import Instance
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class JobType(Enum):
|
||||||
|
PING = "ping"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class InstanceCreated(models.Model):
|
class InstanceCreated(models.Model):
|
||||||
instance = models.ForeignKey(Instance, on_delete=models.PROTECT)
|
instance = models.ForeignKey(Instance, on_delete=models.PROTECT)
|
||||||
created = models.BooleanField(default=False)
|
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,
|
||||||
|
)
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
import requests
|
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from urllib.parse import urlparse, urlunparse
|
from urllib.parse import urlunparse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import requests
|
||||||
from django.test import TestCase, Client, override_settings
|
from django.test import TestCase, Client, override_settings
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.management import call_command
|
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 dash.tests import create_configurations, create_instance_util
|
||||||
from infrastructure.management.commands.vm import translate_sizes
|
from infrastructure.management.commands.vm import translate_sizes
|
||||||
|
|
||||||
from .utils import Infra
|
from .utils import Infra, Worker
|
||||||
from .models import InstanceCreated
|
from .models import InstanceCreated, Job, JobType
|
||||||
|
|
||||||
|
|
||||||
def custom_config(test_name: str):
|
def custom_config(test_name: str):
|
||||||
|
@ -187,3 +187,26 @@ class InfraUtilTest(TestCase):
|
||||||
|
|
||||||
# run delete VM command to crudely check idempotency
|
# run delete VM command to crudely check idempotency
|
||||||
call_command("vm", "delete", subdomain)
|
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
|
||||||
|
)
|
||||||
|
|
|
@ -15,17 +15,65 @@
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import yaml
|
import yaml
|
||||||
|
import requests
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from threading import Thread, Event
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
from django.core.mail import send_mail
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from git import Repo, Commit
|
from git import Repo, Commit
|
||||||
from git.exc import InvalidGitRepositoryError
|
from git.exc import InvalidGitRepositoryError
|
||||||
|
|
||||||
from dash.models import Instance
|
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<no-reply@exampl.org>", # TODO read from settings.py
|
||||||
|
recipient_list=[email],
|
||||||
|
)
|
||||||
|
job.delete()
|
||||||
|
print("job deleted")
|
||||||
|
|
||||||
|
|
||||||
def create_vm_if_not_exists(instance: Instance) -> (str, Commit):
|
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()
|
infra = Infra()
|
||||||
if not InstanceCreated.objects.filter(instance=instance).exists():
|
if not InstanceCreated.objects.filter(instance=instance).exists():
|
||||||
(gitea_password, commit) = infra.add_vm(instance=instance)
|
(gitea_password, commit) = infra.add_vm(instance=instance)
|
||||||
instance = InstanceCreated.objects.create(instance=instance, created=True)
|
InstanceCreated.objects.create(instance=instance, created=True)
|
||||||
instance.save()
|
job = Job.objects.create(instance=instance, job_type=str(JobType.PING))
|
||||||
|
Worker(job=job).start()
|
||||||
return (gitea_password, commit)
|
return (gitea_password, commit)
|
||||||
else:
|
else:
|
||||||
if str.strip(infra.get_flavor(instance=instance)) != str.strip(
|
if str.strip(infra.get_flavor(instance=instance)) != str.strip(
|
||||||
infra.translate_size(instance=instance)
|
infra.translate_size(instance=instance)
|
||||||
):
|
):
|
||||||
|
# Worker.init_global()
|
||||||
return infra.add_vm(instance=instance)
|
return infra.add_vm(instance=instance)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ pynvim==0.4.3
|
||||||
pytz==2022.1
|
pytz==2022.1
|
||||||
PyYAML==6.0
|
PyYAML==6.0
|
||||||
requests==2.27.1
|
requests==2.27.1
|
||||||
|
six==1.16.0
|
||||||
smmap==5.0.0
|
smmap==5.0.0
|
||||||
sqlparse==0.4.2
|
sqlparse==0.4.2
|
||||||
stripe==3.4.0
|
stripe==3.4.0
|
||||||
|
|
Loading…
Reference in New Issue