hobo/hobo/applications/models.py

105 lines
4.1 KiB
Python

# hobo - portal to configure and deploy applications
# Copyright (C) 2015-2022 Entr'ouvert
#
# 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 urllib.parse
from django.conf import settings
from django.contrib.postgres.fields import JSONField
from django.db import models
from django.utils.text import slugify
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from hobo.environment.utils import get_installed_services
from hobo.signature import sign_url
from .utils import Requests
requests = Requests()
class Application(models.Model):
SUPPORTED_MODULES = ('wcs',)
name = models.CharField(max_length=100, verbose_name=_('Name'))
slug = models.SlugField(max_length=100)
description = models.TextField(verbose_name=_('Description'), blank=True)
editable = models.BooleanField(default=True)
elements = models.ManyToManyField('Element', blank=True, through='Relation')
creation_timestamp = models.DateTimeField(default=now)
last_update_timestamp = models.DateTimeField(auto_now=True)
def __repr__(self):
return '<Application %s>' % self.slug
def save(self, *args, **kwargs):
if not self.slug:
base_slug = slugify(self.name)[:95]
slug = base_slug
i = 1
while Application.objects.filter(slug=slug).exists():
slug = '%s-%s' % (base_slug, i)
i += 1
self.slug = slug
super().save(*args, **kwargs)
class Element(models.Model):
type = models.CharField(max_length=25, verbose_name=_('Type'))
slug = models.SlugField(max_length=500, verbose_name=_('Slug'))
name = models.CharField(max_length=500, verbose_name=_('Name'))
cache = JSONField(blank=True, default=dict)
def __repr__(self):
return '<Element %s/%s>' % (self.type, self.slug)
class Relation(models.Model):
application = models.ForeignKey(Application, on_delete=models.CASCADE)
element = models.ForeignKey(Element, on_delete=models.CASCADE)
auto_dependency = models.BooleanField(default=False)
def __repr__(self):
return '<Relation %s - %s/%s>' % (self.application.slug, self.element.type, self.element.slug)
class Version(models.Model):
application = models.ForeignKey(Application, on_delete=models.CASCADE)
bundle = models.FileField(upload_to='applications', blank=True, null=True)
creation_timestamp = models.DateTimeField(default=now)
last_update_timestamp = models.DateTimeField(auto_now=True)
deployment_status = JSONField(blank=True, default=dict)
def __repr__(self):
return '<Version %s>' % self.application.slug
def deploy(self):
bundle_content = self.bundle.read()
for service_id, services in getattr(settings, 'KNOWN_SERVICES', {}).items():
if service_id not in Application.SUPPORTED_MODULES:
continue
service_objects = {x.get_base_url_path(): x for x in get_installed_services(types=[service_id])}
for service in services.values():
if service_objects[service['url']].secondary:
continue
url = urllib.parse.urljoin(service['url'], 'api/export-import/bundle-import/')
response = requests.put(sign_url(url, service['secret']), data=bundle_content)
if not response.ok:
# TODO: report failures
continue
# TODO: look at response content for afterjob URLs to display a progress bar
pass