From 2b7bcb11864988fd62d73f193f6ca5c396a29f12 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 9 Sep 2015 09:28:15 +0200 Subject: [PATCH] add new agent task to provision objects to tenants (fixes #8217) First use is to connect it to post_save, post_delete signal on Role model of authentic, to propagate roles to tenants. --- hobo/agent/authentic2/apps.py | 58 +++++++++++++++++++++++++++++++++++++++++++ hobo/agent/common/__init__.py | 25 +++++++++++++++++++ hobo/agent/worker/celery.py | 6 +++++ hobo/agent/worker/services.py | 18 ++++++++++++++ 4 files changed, 107 insertions(+) diff --git a/hobo/agent/authentic2/apps.py b/hobo/agent/authentic2/apps.py index 0996c09..e41e7cb 100644 --- a/hobo/agent/authentic2/apps.py +++ b/hobo/agent/authentic2/apps.py @@ -1,6 +1,64 @@ from django.apps import AppConfig +from django.db.models.signals import post_save, post_delete, Q +from django.db.models import Q + +from django_rbac.utils import get_role_model + +from hobo.agent.common import notify_agents +from authentic2.utils import to_list +from authentic2.saml.models import LibertyProvider + + +def get_ou(role_or_through): + is hasattr(role_or_through, 'ou'): + return role_or_through.ou + else: + return role_or_through.role.ou + + +def get_audience(role_or_through): + ou = get_ou(role_or_through) + if ou: + qs = LibertyProvider.objects.filter(ou=ou) + else: + qs = LibertyProvider.objects.filter(ou__isnull=True) + return qs.values_list('entity_id', flat=True) + + +def get_related_roles(role_or_through) + Role = get_role_model() + qs = Role.objects.filter(admin_scope_id__isnull=True) + if ou: + return qs.filter(Q(ou=ou)|Q(ou__isnull=True)) + else: + return qs.filter(ou__isnull=True) + + +def notify_roles(sender, instance, **kwargs): + notify_agents({ + '@type': 'provision', + 'audience': get_audience(instance), + 'full': True, + 'objects': [ + { + '@type': 'role', + 'uuid': role.uuid, + 'name': role.name, + 'slug': role.slug, + 'description': role.description, + } for role in get_related_roles(instance), + ] + }) + class Authentic2AgentConfig(AppConfig): name = 'hobo.agent.authentic2' label = 'authentic2_agent' verbose_name = 'Authentic2 Agent' + + def ready(self): + Role = get_role_model() + post_save.connect(notify_roles, Role) + post_delete.connect(notify_roles, Role) + post_save.connect(notify_roles, Role.members.through) + post_delete.connect(notify_roles, Role.members.through) diff --git a/hobo/agent/common/__init__.py b/hobo/agent/common/__init__.py index e69de29..5b50cc6 100644 --- a/hobo/agent/common/__init__.py +++ b/hobo/agent/common/__init__.py @@ -0,0 +1,25 @@ +from celery import Celery +from kombu.common import Broadcast + +from django.conf import settings +from django.db import connection + + +def notify_agents(data): + '''Send notifications to all other tenants''' + notification = { + 'tenant': connection.get_tenant().domain_url, + 'data': data, + } + with Celery('hobo', broker=settings.BROKER_URL) as app: + app.conf.update( + CELERY_TASK_SERIALIZER='json', + CELERY_ACCEPT_CONTENT=['json'], + CELERY_RESULT_SERIALIZER='json', + CELERY_QUEUES=(Broadcast('broadcast_tasks'), ) + ) + # see called method in hobo.agent.worker.celery + app.send_task('hobo-notify', + (notification,), + expires=settings.BROKER_TASK_EXPIRES, + queue='broadcast_tasks') diff --git a/hobo/agent/worker/celery.py b/hobo/agent/worker/celery.py index 3db4b0d..25f816f 100644 --- a/hobo/agent/worker/celery.py +++ b/hobo/agent/worker/celery.py @@ -13,6 +13,12 @@ app.conf.update( CELERY_QUEUES=(Broadcast('broadcast_tasks'), ) ) + @app.task(name='hobo-deploy', bind=True) def deploy(self, environment): services.deploy(environment) + + +@app.task(name='hobo-notify', bind=True, acks_late=True) +def hobo_notify(self, data): + services.notify(data) diff --git a/hobo/agent/worker/services.py b/hobo/agent/worker/services.py index 01cfff9..8a61bb4 100644 --- a/hobo/agent/worker/services.py +++ b/hobo/agent/worker/services.py @@ -55,6 +55,18 @@ class BaseService(object): shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) stdout = cmd_process.communicate(input=json.dumps(environment)) + @classmethod + def notify(cls, data): + cmd = settings.service_manage_cmd + ' hobo_notify -' + try: + cmd_process = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + except OSError: + return + cmd_process.communicate(input=json.dumps(data)) + if cmd_process.returncode != 0: + raise RuntimeError('command "%s" failed' % what) + class Passerelle(BaseService): service_id = 'passerelle' @@ -113,3 +125,9 @@ def deploy(environment): logger.debug('skipping uptodate site: %r', service_obj) continue service_obj.execute(environment) + +def notify(data): + for klassname, service in globals().items(): + if not hasattr(service, 'notify'): + continue + service.notify(data) -- 2.1.4