ozwillo: keep deployment request state (#23885)

Fields added to OzwilloInstance:
* state
* deploy_data
* created
* modified

First migration initialize all instances with the state DEPLOYED but new
instance will get the state NEW (change done in second migration).

OzwilloInstance was registered in the admin for managing deployments.
This commit is contained in:
Benjamin Dauvergne 2018-05-17 12:42:08 +02:00
parent abfdd3d528
commit bfe041158c
9 changed files with 455 additions and 195 deletions

View File

@ -12,7 +12,7 @@ Install on Debian
hobo ALL=(ALL:ALL) NOPASSWD: ALL
4. set the following variables in `/etc/hobo/settings.py`:
4. set the following variables in `/etc/hobo/settings.d/10_ozwillo.py`:
- OZWILLO_SECRET
- OZWILLO_ENV_DOMAIN (e.g: sictiam.dev.entrouvert.org)
@ -54,7 +54,7 @@ Install on Debian
"passerelle-",
"passerelle-manage"
],
"wcs-au-quotidien": [
"wcs": [
"demarches-",
"wcsctl"
]
@ -94,5 +94,12 @@ OZWILLO_SERVICES is a dict following this pattern: 'service_user':
"fargo": ["porte-documents-", "fargo-manage"],
"hobo": ["hobo-", "hobo-manage"],
"passerelle": ["passerelle-", "passerelle-manage"],
"wcs-au-quotidien": ["demarches-", "wcsctl"]
"wcs": ["demarches-", "wcsctl"]
}
Destruction
===========
For the complete destruction of w.c.s. instances it's necessary that in the
default skeleton (`/var/lib/wcs/skeletons/export.zip`) the `config.pck` file
contains a key `postgresql.createdb-connection-params`.

View File

@ -0,0 +1,10 @@
from django.contrib import admin
from .models import OzwilloInstance
class OzwilloInstanceAdmin(admin.ModelAdmin):
list_display = ['id', 'domain_slug', 'external_ozwillo_id', 'state', 'created', 'modified']
list_filter = ['state', 'created', 'modified']
admin.site.register(OzwilloInstance, OzwilloInstanceAdmin)

View File

@ -0,0 +1,68 @@
# Ozwillo plugin to deploy Publik
# Copyright (C) 2017 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 logging
from hobo.contrib.ozwillo.models import OzwilloInstance
from django.db.transaction import atomic
from django.core.management.base import BaseCommand
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('args', nargs='*')
@atomic
def handle(self, *args, **options):
qs = OzwilloInstance.objects.select_for_update()
# deployment
if args:
# allow deploying manually failed instances
to_deploy = qs.fiter(
domain_slug__in=args,
state__in=[OzwilloInstance.STATE_TO_DEPLOY, OzwilloInstance.STATE_DEPLOY_ERROR])
else:
# deploy everything to be deployed
to_deploy = qs.filter(
state=OzwilloInstance.STATE_TO_DEPLOY)
for instance in to_deploy:
try:
with atomic():
instance.deploy()
except Exception:
logger.exception(u'ozwillo: deploying %s from CRON failed', instance)
instance.deploy_error()
# destruction
if args:
# allow deploying manually failed instances
to_destroy = qs.filter(
domain_slug__in=args,
state__in=[OzwilloInstance.STATE_TO_DESTROY, OzwilloInstance.STATE_DESTROY_ERROR])
else:
# destroy everything to be destroyed
to_destroy = qs.filter(state=OzwilloInstance.STATE_TO_DESTROY)
for instance in to_destroy:
try:
with atomic():
instance.destroy()
except Exception:
logger.exception(u'ozwillo: destroying %s from CRON failed', instance)
instance.destroy_error()
logger = logging.getLogger(__name__)

View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-05-17 10:47
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('ozwillo', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='ozwilloinstance',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='ozwilloinstance',
name='deploy_data',
field=models.TextField(null=True),
),
migrations.AddField(
model_name='ozwilloinstance',
name='modified',
field=models.DateTimeField(auto_now=True, default=django.utils.timezone.now),
),
migrations.AddField(
model_name='ozwilloinstance',
name='state',
field=models.CharField(choices=[(b'new', b'new'), (b'to_deploy', b'to_deploy'), (b'deployed', b'deployed'), (b'deploy_error', b'deploy_error'), (b'to_destroy', b'to_destroy'), (b'destroy_error', b'destroy_error'), (b'destroyed', b'destroyed')], default=b'deployed', max_length=16),
),
migrations.AlterField(
model_name='ozwilloinstance',
name='external_ozwillo_id',
field=models.CharField(max_length=450, unique=True),
),
migrations.AlterField(
model_name='ozwilloinstance',
name='modified',
field=models.DateTimeField(auto_now=True),
),
migrations.AlterField(
model_name='ozwilloinstance',
name='state',
field=models.CharField(choices=[(b'new', b'new'), (b'to_deploy', b'to_deploy'), (b'deployed', b'deployed'), (b'deploy_error', b'deploy_error'), (b'to_destroy', b'to_destroy'), (b'destroy_error', b'destroy_error'), (b'destroyed', b'destroyed')], default=b'new', max_length=16),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-05-24 08:28
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ozwillo', '0002_auto_20180517_1047'),
]
operations = [
migrations.AlterField(
model_name='ozwilloinstance',
name='domain_slug',
field=models.CharField(max_length=250),
),
]

View File

@ -14,13 +14,272 @@
# 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 logging
import os
import subprocess
import tempfile
import json
import requests
from django.conf import settings
from django.db import models
from django.db.transaction import atomic
from django.utils.text import slugify
from django.core.management import call_command
from django.db import connection
from tenant_schemas.utils import tenant_context
class OzwilloInstance(models.Model):
domain_slug = models.CharField(max_length=250, unique=True)
external_ozwillo_id = models.CharField(max_length=450)
STATE_NEW = 'new'
STATE_TO_DEPLOY = 'to_deploy'
STATE_DEPLOY_ERROR = 'deploy_error'
STATE_DEPLOYED = 'deployed'
STATE_TO_DESTROY = 'to_destroy'
STATE_DESTROY_ERROR = 'destroy_error'
STATE_DESTROYED = 'destroyed'
STATES = [
(STATE_NEW, 'new'),
(STATE_TO_DEPLOY, 'to_deploy'),
(STATE_DEPLOYED, 'deployed'),
(STATE_DEPLOY_ERROR, 'deploy_error'),
(STATE_TO_DESTROY, 'to_destroy'),
(STATE_DESTROY_ERROR, 'destroy_error'),
(STATE_DESTROYED, 'destroyed'),
]
state = models.CharField(max_length=16, default=STATE_NEW, choices=STATES)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
domain_slug = models.CharField(max_length=250)
external_ozwillo_id = models.CharField(max_length=450, unique=True)
deploy_data = models.TextField(null=True)
def __unicode__(self):
return 'external ozwillo id: %s, domain slug: %s' % (self.external_ozwillo_id,
self.domain_slug)
return self.domain_slug
def __repr__(self):
return '<OzwilloInstance external_ozwillo_id: %r domain_slug: %r state: %r>' % (
self.external_ozwillo_id, self.domain_slug, self.state)
@property
def data(self):
return json.loads(self.deploy_data) if self.deploy_data else None
def to_deploy(self):
assert self.state == self.STATE_NEW
try:
with atomic():
# lock the new instance to prevent collision with the CRON job
OzwilloInstance.objects.select_for_update().filter(id=self.id)
# instance starts with the NEW state, to prevent the CRON from
# deploying instance juste created
# only instance in the state STATE_TO_DEPLOY are deployed.
self.state = self.STATE_TO_DEPLOY
self.save()
self.deploy()
except Exception:
# something failed, still make the instance to be deployed
# an reraise exception
self.state = self.STATE_TO_DEPLOY
self.save()
raise
def to_destroy(self):
assert self.state == self.STATE_DEPLOYED
self.state = self.STATE_TO_DESTROY
self.save()
def deploy_error(self):
assert self.state in [self.STATE_DEPLOY_ERROR, self.STATE_TO_DEPLOY]
if self.state == self.STATE_TO_DEPLOY:
self.state = self.STATE_DEPLOY_ERROR
self.save()
def deploy(self):
logger.info(u'ozwillo: deploy start for %s', self)
data = self.data
if not data:
logger.warning(u'ozwillo: unable to deploy, no data')
return
# Request parsing
client_id = data['client_id']
client_secret = data['client_secret']
instance_id = data['instance_id']
instance_name = data['organization_name']
instance_name = slugify(instance_name)
registration_uri = data['instance_registration_uri']
user = data['user']
# Cook new platform
template_recipe = json.load(open('/etc/hobo/ozwillo/template_recipe.json', 'rb'))
var = template_recipe['variables']
for key, value in var.items():
var[key] = value.replace('instance_name', instance_name)
template_recipe['variables'] = var
domain = var['combo']
domain_agent = var['combo_agent']
domain_passerelle = var['passerelle']
logger.info(u'ozwillo: cooking %s', template_recipe)
with tempfile.NamedTemporaryFile() as recipe_file:
json.dump(template_recipe, recipe_file)
recipe_file.flush()
# cook play with the tenant context, we must protect from that
with tenant_context(connection.tenant):
call_command('cook', recipe_file.name, timeout=1000, verbosity=0)
# Load user portal template
logger.info(u'ozwillo: loading combo template')
run_command([
'sudo', '-u', 'combo',
'combo-manage', 'tenant_command', 'import_site',
'/etc/hobo/ozwillo/import-site-template.json',
'-d', domain
])
# Load agent portal template
logger.info(u'ozwillo: loading combo agent template')
run_command([
'sudo', '-u', 'combo',
'combo-manage', 'tenant_command', 'import_site',
'/etc/hobo/ozwillo/import-site-agents.json',
'-d', domain_agent
])
# Configure OIDC Ozwillo authentication
logger.info(u'ozwillo: configuring OIDC ozwillo authentication')
domain_name = 'connexion-%s.%s' % (instance_name, settings.OZWILLO_ENV_DOMAIN)
if run_command([
'sudo', '-u', 'authentic-multitenant',
'authentic2-multitenant-manage', 'tenant_command', 'oidc-register-issuer',
'-d', domain_name,
'--scope', 'profile',
'--scope', 'email',
'--issuer', settings.OZWILLO_PLATEFORM,
'--client-id', client_id,
'--client-secret', client_secret,
'--claim-mapping', 'given_name first_name always_verified',
'--claim-mapping', 'family_name last_name always_verified',
'--ou-slug', 'default',
'--claim-mapping', 'email email required',
'Ozwillo'
]):
# creation of the admin user depends upon the creation of provider
logger.info(u'ozwillo: creating admin user')
create_user_script = os.path.dirname(__file__) + '/scripts/create_user_ozwillo.py'
run_command([
'sudo', '-u', 'authentic-multitenant',
'authentic2-multitenant-manage', 'tenant_command', 'runscript', '-d', domain_name,
create_user_script, user['email_address'], user['id'], user['name']
])
# Load passerelle template
logger.info(u'ozwillo: loading passerelle template')
run_command([
'sudo', '-u', 'passerelle',
'passerelle-manage', 'tenant_command', 'import_site',
'/etc/hobo/ozwillo/import-site-passerelle.json',
'--import-user', '-d', domain_passerelle
])
# Sending done event to Ozwillo
services = {
'services': [{
'local_id': 'publik',
'name': 'Publik - %s' % (instance_name),
'service_uri': 'https://connexion-%s.%s/accounts/oidc/login?iss=%s'
% (instance_name, settings.OZWILLO_ENV_DOMAIN,
settings.OZWILLO_PLATEFORM),
'description': 'Gestion de la relation usagers',
'tos_uri': 'https://publik.entrouvert.com/',
'policy_uri': 'https://publik.entrouvert.com/',
'icon': 'https://publik.entrouvert.com/static/img/logo-publik-64x64.png',
'payment_option': 'FREE',
'target_audience': ['PUBLIC_BODIES',
'CITIZENS',
'COMPANIES'],
'contacts': ['https://publik.entrouvert.com/'],
'redirect_uris': ['https://connexion-%s.%s/accounts/oidc/callback/'
% (instance_name, settings.OZWILLO_ENV_DOMAIN)],
}],
'instance_id': instance_id,
'destruction_uri': settings.OZWILLO_DESTRUCTION_URI,
'destruction_secret': settings.OZWILLO_DESTRUCTION_SECRET,
'needed_scopes': []
}
logger.info(u'ozwillo: sending registration request, %r', services)
headers = {'Content-type': 'application/json', 'Accept': 'application/json'}
response = requests.post(
registration_uri,
data=json.dumps(services),
auth=(client_id, client_secret),
headers=headers)
logger.info(u'ozwillo: registration response, status=%s content=%r',
response.status_code, response.content)
self.state = self.STATE_DEPLOYED
self.save()
logger.info(u'ozwillo: deploy finished')
def destroy_error(self):
assert self.state in [self.STATE_DESTROY_ERROR, self.STATE_TO_DESTROY]
if self.state == self.STATE_TO_DESTROY:
self.state = self.STATE_DESTROY_ERROR
self.save()
def destroy(self):
logger.info(u'ozwillo: destroy start for %s', self)
instance_slug = self.domain_slug
services = settings.OZWILLO_SERVICES.copy()
# w.c.s. is handled differently
wcs = services.pop('wcs')
for s, infos in services.items():
# to get the two combo instances which have same service
service_user = s.split('_')[0]
tenant = '%s%s.%s' % (infos[0], instance_slug, settings.OZWILLO_ENV_DOMAIN)
run_command([
'sudo', '-u', service_user, infos[1],
'delete_tenant', '--force-drop', tenant
])
tenant = '%s%s.%s' % (wcs[0], instance_slug, settings.OZWILLO_ENV_DOMAIN)
run_command([
'sudo', '-u', 'wcs',
wcs[1], '-f', '/etc/wcs/wcs-au-quotidien.cfg',
'delete_tenant', '--force-drop', tenant
])
logger.info(u'ozwillo: destroy finished')
self.state = self.STATE_DESTROYED
self.save()
def run_command(args):
try:
process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError as e:
logger.error('ozwillo: launching subprocess %s raised error %s', args, e)
return False
logger.info('ozwillo: launching subprocess with pid %s : %s', process.pid, args)
stdoutdata, stderrdata = process.communicate()
if process.returncode != 0:
logger.error('ozwillo: subprocess %s failed returncode=%s stdout=%r stderr=%r',
process.pid, process.returncode, stdoutdata, stderrdata)
return False
logger.info('ozwillo: subprocess terminated')
return True
logger = logging.getLogger(__name__)

View File

@ -14,44 +14,25 @@
# 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 os
import logging
import requests
import json
import subprocess
import hmac
import threading
import tempfile
from hashlib import sha1
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings
from django.http import (HttpResponseForbidden, HttpResponseBadRequest,
HttpResponseNotFound, HttpResponse)
from django.core.management import call_command
from django.utils.text import slugify
from django.db import DatabaseError
from django.db.transaction import atomic
from .models import OzwilloInstance
logger = logging.getLogger(__name__)
def run_command(args):
try:
process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError as e:
logger.error('ozwillo: launching subprocess %s raised error %s', args, e)
return False
logger.debug('ozwillo: launching subprocess with pid %s : %s', process.pid, args)
stdoutdata, stderrdata = process.communicate()
if process.returncode != 0:
logger.error('ozwillo: subprocess %s failed returncode=%s stdout=%r stderr=%r',
process.pid, process.returncode, stdoutdata, stderrdata)
return False
logger.debug('ozwillo: subprocess terminated')
return True
def valid_signature_required(setting):
'''Validate Ozwillo signatures'''
signature_header_name = 'HTTP_X_HUB_SIGNATURE'
@ -97,154 +78,46 @@ def create_publik_instance(request):
logger.warning(u'ozwillo: received non JSON request')
return HttpResponseBadRequest('invalid JSON content')
logger.debug(u'ozwillo: create publik instance request, %r', data)
logger.info(u'ozwillo: create publik instance request, %r', data)
if 'organization_name' not in data.keys():
logger.warning(u'ozwillo: missing organization_name')
return HttpResponseBadRequest('missing parameter "organization_name"')
org_name = slugify(data['organization_name'])
if OzwilloInstance.objects.filter(domain_slug=org_name):
# forbid creation of an instance if a not destroyed previous one exists
if OzwilloInstance.objects.exclude(state=OzwilloInstance.STATE_DESTROYED).filter(domain_slug=org_name):
logger.warning(u'ozwillo: instance %s already exists', org_name)
return HttpResponseBadRequest('instance %s already exists' % org_name)
OzwilloInstance.objects.create(external_ozwillo_id=data['instance_id'], domain_slug=org_name)
try:
instance = OzwilloInstance.objects.create(
external_ozwillo_id=data['instance_id'],
state=OzwilloInstance.STATE_NEW,
domain_slug=org_name,
# deploy_data is a TextField containing JSON
deploy_data=json.dumps(data, indent=4))
except DatabaseError as e:
logger.warning(u'ozwillo: could not create instance_id %r org_name %r: %r',
data['instance_id'], org_name, e)
return HttpResponseBadRequest(u'cannot create the instance: %r', e)
# immediate deploy in a thread
def thread_function(data):
try:
ozwillo_deploy_thread(data)
instance.to_deploy()
except Exception:
logger.exception(u'ozwillo: error occured while deploying instance %s',
data['organization_name'])
logger.exception(u'ozwillo: error occured duging initial deploy request %s', org_name)
thread = threading.Thread(target=thread_function, args=(data,))
thread.start()
return HttpResponse()
def ozwillo_deploy_thread(data):
logger.debug(u'ozwillo: deploy thread start')
# Request parsing
client_id = data['client_id']
client_secret = data['client_secret']
instance_id = data['instance_id']
instance_name = data['organization_name']
instance_name = slugify(instance_name)
registration_uri = data['instance_registration_uri']
user = data['user']
# Cook new platform
template_recipe = json.load(open('/etc/hobo/ozwillo/template_recipe.json', 'rb'))
var = template_recipe['variables']
for key, value in var.items():
var[key] = value.replace('instance_name', instance_name)
template_recipe['variables'] = var
domain = var['combo']
domain_agent = var['combo_agent']
domain_passerelle = var['passerelle']
logger.debug(u'ozwillo: cooking %s', template_recipe)
with tempfile.NamedTemporaryFile() as recipe_file:
json.dump(template_recipe, recipe_file)
recipe_file.flush()
call_command('cook', recipe_file.name, timeout=1000, verbosity=0)
# Load user portal template
logger.debug(u'ozwillo: loading combo template')
run_command([
'sudo', '-u', 'combo',
'combo-manage', 'tenant_command', 'import_site',
'/etc/hobo/ozwillo/import-site-template.json',
'-d', domain
])
# Load agent portal template
logger.debug(u'ozwillo: loading combo agent template')
run_command([
'sudo', '-u', 'combo',
'combo-manage', 'tenant_command', 'import_site',
'/etc/hobo/ozwillo/import-site-agents.json',
'-d', domain_agent
])
# Configure OIDC Ozwillo authentication
logger.debug(u'ozwillo: configuring OIDC ozwillo authentication')
domain_name = 'connexion-%s.%s' % (instance_name, settings.OZWILLO_ENV_DOMAIN)
if run_command([
'sudo', '-u', 'authentic-multitenant',
'authentic2-multitenant-manage', 'tenant_command', 'oidc-register-issuer',
'-d', domain_name,
'--scope', 'profile',
'--scope', 'email',
'--issuer', settings.OZWILLO_PLATEFORM,
'--client-id', client_id,
'--client-secret', client_secret,
'--claim-mapping', 'given_name first_name always_verified',
'--claim-mapping', 'family_name last_name always_verified',
'--ou-slug', 'default',
'--claim-mapping', 'email email required',
'Ozwillo'
]):
# creation of the admin user depends upon the creation of provider
logger.debug(u'ozwillo: creating admin user')
create_user_script = os.path.dirname(__file__) + '/scripts/create_user_ozwillo.py'
run_command([
'sudo', '-u', 'authentic-multitenant',
'authentic2-multitenant-manage', 'tenant_command', 'runscript', '-d', domain_name,
create_user_script, user['email_address'], user['id'], user['name']
])
# Load passerelle template
logger.debug(u'ozwillo: loading passerelle template')
run_command([
'sudo', '-u', 'passerelle',
'passerelle-manage', 'tenant_command', 'import_site',
'/etc/hobo/ozwillo/import-site-passerelle.json',
'--import-user', '-d', domain_passerelle
])
# Sending done event to Ozwillo
services = {
'services': [{
'local_id': 'publik',
'name': 'Publik - %s' % (instance_name),
'service_uri': 'https://connexion-%s.%s/accounts/oidc/login?iss=%s'
% (instance_name, settings.OZWILLO_ENV_DOMAIN,
settings.OZWILLO_PLATEFORM),
'description': 'Gestion de la relation usagers',
'tos_uri': 'https://publik.entrouvert.com/',
'policy_uri': 'https://publik.entrouvert.com/',
'icon': 'https://publik.entrouvert.com/static/img/logo-publik-64x64.png',
'payment_option': 'FREE',
'target_audience': ['PUBLIC_BODIES',
'CITIZENS',
'COMPANIES'],
'contacts': ['https://publik.entrouvert.com/'],
'redirect_uris': ['https://connexion-%s.%s/accounts/oidc/callback/'
% (instance_name, settings.OZWILLO_ENV_DOMAIN)],
}],
'instance_id': instance_id,
'destruction_uri': settings.OZWILLO_DESTRUCTION_URI,
'destruction_secret': settings.OZWILLO_DESTRUCTION_SECRET,
'needed_scopes': []
}
logger.debug(u'ozwillo: sending registration request, %r', services)
headers = {'Content-type': 'application/json', 'Accept': 'application/json'}
response = requests.post(
registration_uri,
data=json.dumps(services),
auth=(client_id, client_secret),
headers=headers)
logger.debug(u'ozwillo: registration response, status=%s content=%r', response.status_code,
response.content)
logger.debug(u'ozwillo: deploy thread finished')
@csrf_exempt
@is_ozwillo_enabled
@valid_signature_required(setting='OZWILLO_DESTRUCTION_SECRET')
@atomic
def delete_publik_instance(request):
try:
data = json.loads(request.body)
@ -252,49 +125,20 @@ def delete_publik_instance(request):
logger.warning(u'ozwillo: received non JSON request')
return HttpResponseBadRequest('invalid JSON content')
logger.debug(u'ozwillo: delete publik instance request, %r', data)
logger.info(u'ozwillo: delete publik instance request (%r)', data)
try:
instance = OzwilloInstance.objects.get(external_ozwillo_id=data['instance_id'])
instance_id = data['instance_id']
except KeyError:
logger.warning(u'ozwillo: no instance id in destroy request (%r)', data)
return HttpResponseBadRequest('no instance id')
try:
instance = OzwilloInstance.objects.select_for_update().get(
external_ozwillo_id=instance_id,
# only deployed instances can be destroyed
state=OzwilloInstance.STATE_DEPLOYED)
except OzwilloInstance.DoesNotExist:
return HttpResponseBadRequest('no instance with id %s' % data['instance_id'])
def thread_function(data):
try:
ozwillo_destroy_thread(instance)
logger.debug(u'ozwillo: instance %s destroyed', instance.domain_slug)
except Exception:
logger.exception('ozwillo: error occured while destroying instance %s',
instance.domain_slug)
thread = threading.Thread(target=ozwillo_destroy_thread, args=(instance,))
thread.start()
instance.to_destroy()
logger.info(u'ozwillo: destroy registered for %s (%s)', instance.domain_slug, instance.external_ozwillo_id)
return HttpResponse(status=200)
def ozwillo_destroy_thread(instance):
logger.debug(u'ozwillo: destroy thread start')
instance_slug = instance.domain_slug
services = settings.OZWILLO_SERVICES
wcs = services['wcs-au-quotidien']
for s, infos in services.items():
# to get the two combo instances which have same service
service_user = s.split('_')[0]
tenant = '%s%s.%s' % (infos[0], instance_slug, settings.OZWILLO_ENV_DOMAIN)
run_command([
'sudo', '-u', service_user, infos[1],
'delete_tenant', '--force-drop', tenant
])
tenant = '%s%s.%s' % (wcs[0], instance_slug, settings.OZWILLO_ENV_DOMAIN)
run_command([
'sudo', '-u', 'wcs',
wcs[1], '-f', '/etc/wcs/wcs-au-quotidien.cfg',
'delete_tenant', '--force-drop', tenant
])
instance.delete()
logger.debug(u'ozwillo: destroy thread finished')