<s> """<STR_LIT>""" <EOL> from __future__ import unicode_literals <EOL> import base64 <EOL> from datetime import datetime <EOL> import etcd <EOL> import importlib <EOL> import logging <EOL> import re <EOL> import time <EOL> from threading import Thread <EOL> from django . conf import settings <EOL> from django . contrib . auth import get_user_model <EOL> from django . core . exceptions import ValidationError , SuspiciousOperation <EOL> from django . db import models <EOL> from django . db . models import Count <EOL> from django . db . models import Max <EOL> from django . db . models . signals import post_delete , post_save <EOL> from django . dispatch import receiver <EOL> from django . utils . encoding import python_2_unicode_compatible <EOL> from docker . utils import utils as dockerutils <EOL> from json_field . fields import JSONField <EOL> from OpenSSL import crypto <EOL> import requests <EOL> from rest_framework . authtoken . models import Token <EOL> from api import fields , utils , exceptions <EOL> from registry import publish_release <EOL> from utils import dict_diff , fingerprint <EOL> logger = logging . getLogger ( __name__ ) <EOL> def close_db_connections ( func , * args , ** kwargs ) : <EOL> """<STR_LIT>""" <EOL> def _close_db_connections ( * args , ** kwargs ) : <EOL> ret = None <EOL> try : <EOL> ret = func ( * args , ** kwargs ) <EOL> finally : <EOL> from django . db import connections <EOL> for conn in connections . all ( ) : <EOL> conn . close ( ) <EOL> return ret <EOL> return _close_db_connections <EOL> def log_event ( app , msg , level = logging . INFO ) : <EOL> logger . log ( level , "<STR_LIT>" . format ( app . id , msg ) ) <EOL> app . log ( msg , level ) <EOL> def validate_base64 ( value ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> base64 . b64decode ( value . split ( ) [ <NUM_LIT:1> ] ) <EOL> except Exception as e : <EOL> raise ValidationError ( e ) <EOL> def validate_id_is_docker_compatible ( value ) : <EOL> """<STR_LIT>""" <EOL> match = re . match ( r'<STR_LIT>' , value ) <EOL> if not match : <EOL> raise ValidationError ( "<STR_LIT>" ) <EOL> def validate_app_structure ( value ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> if any ( int ( v ) < <NUM_LIT:0> for v in value . viewvalues ( ) ) : <EOL> raise ValueError ( "<STR_LIT>" ) <EOL> except ValueError , err : <EOL> raise ValidationError ( err ) <EOL> def validate_reserved_names ( value ) : <EOL> """<STR_LIT>""" <EOL> if value in settings . DEIS_RESERVED_NAMES : <EOL> raise ValidationError ( '<STR_LIT>' . format ( value ) ) <EOL> def validate_comma_separated ( value ) : <EOL> """<STR_LIT>""" <EOL> if not re . search ( r'<STR_LIT>' , value ) : <EOL> raise ValidationError ( <EOL> "<STR_LIT>" . format ( value ) ) <EOL> def validate_domain ( value ) : <EOL> """<STR_LIT>""" <EOL> if not re . search ( r'<STR_LIT>' , value ) : <EOL> raise ValidationError ( '<STR_LIT>' . format ( value ) ) <EOL> def validate_certificate ( value ) : <EOL> try : <EOL> crypto . load_certificate ( crypto . FILETYPE_PEM , value ) <EOL> except crypto . Error as e : <EOL> raise ValidationError ( '<STR_LIT>' . format ( e ) ) <EOL> def validate_common_name ( value ) : <EOL> if '<STR_LIT:*>' in value : <EOL> raise ValidationError ( '<STR_LIT>' ) <EOL> def get_etcd_client ( ) : <EOL> if not hasattr ( get_etcd_client , "<STR_LIT>" ) : <EOL> try : <EOL> get_etcd_client . client = etcd . Client ( <EOL> host = settings . ETCD_HOST , <EOL> port = int ( settings . ETCD_PORT ) ) <EOL> get_etcd_client . client . get ( '<STR_LIT>' ) <EOL> except etcd . EtcdException : <EOL> logger . log ( logging . WARNING , '<STR_LIT>' ) <EOL> get_etcd_client . client = None <EOL> return get_etcd_client . client <EOL> class AuditedModel ( models . Model ) : <EOL> """<STR_LIT>""" <EOL> created = models . DateTimeField ( auto_now_add = True ) <EOL> updated = models . DateTimeField ( auto_now = True ) <EOL> class Meta : <EOL> """<STR_LIT>""" <EOL> abstract = True <EOL> def select_app_name ( ) : <EOL> """<STR_LIT>""" <EOL> name = utils . generate_app_name ( ) <EOL> while App . objects . filter ( id = name ) . exists ( ) : <EOL> name = utils . generate_app_name ( ) <EOL> return name <EOL> class UuidAuditedModel ( AuditedModel ) : <EOL> """<STR_LIT>""" <EOL> uuid = fields . UuidField ( '<STR_LIT>' , primary_key = True ) <EOL> class Meta : <EOL> """<STR_LIT>""" <EOL> abstract = True <EOL> @ python_2_unicode_compatible <EOL> class App ( UuidAuditedModel ) : <EOL> """<STR_LIT>""" <EOL> owner = models . ForeignKey ( settings . AUTH_USER_MODEL ) <EOL> id = models . SlugField ( max_length = <NUM_LIT:64> , unique = True , default = select_app_name , <EOL> validators = [ validate_id_is_docker_compatible , <EOL> validate_reserved_names ] ) <EOL> structure = JSONField ( default = { } , blank = True , validators = [ validate_app_structure ] ) <EOL> class Meta : <EOL> permissions = ( ( '<STR_LIT>' , '<STR_LIT>' ) , ) <EOL> @ property <EOL> def _scheduler ( self ) : <EOL> mod = importlib . import_module ( settings . SCHEDULER_MODULE ) <EOL> return mod . SchedulerClient ( settings . SCHEDULER_TARGET , <EOL> settings . SCHEDULER_AUTH , <EOL> settings . SCHEDULER_OPTIONS , <EOL> settings . SSH_PRIVATE_KEY ) <EOL> def __str__ ( self ) : <EOL> return self . id <EOL> @ property <EOL> def url ( self ) : <EOL> return self . id + '<STR_LIT:.>' + settings . DEIS_DOMAIN <EOL> def _get_job_id ( self , container_type ) : <EOL> app = self . id <EOL> release = self . release_set . latest ( ) <EOL> version = "<STR_LIT>" . format ( release . version ) <EOL> job_id = "<STR_LIT>" . format ( ** locals ( ) ) <EOL> return job_id <EOL> def _get_command ( self , container_type ) : <EOL> try : <EOL> release = self . release_set . latest ( ) <EOL> if release . build . dockerfile or not release . build . sha : <EOL> return "<STR_LIT>" . format ( release . build . procfile [ container_type ] ) <EOL> else : <EOL> return '<STR_LIT>' . format ( container_type ) <EOL> except ( KeyError , TypeError , AttributeError ) : <EOL> return '<STR_LIT>' if container_type == '<STR_LIT>' else '<STR_LIT>' . format ( container_type ) <EOL> def log ( self , message , level = logging . INFO ) : <EOL> """<STR_LIT>""" <EOL> logger . log ( level , "<STR_LIT>" . format ( self . id , message ) ) <EOL> def create ( self , * args , ** kwargs ) : <EOL> """<STR_LIT>""" <EOL> config = Config . objects . create ( owner = self . owner , app = self ) <EOL> Release . objects . create ( version = <NUM_LIT:1> , owner = self . owner , app = self , config = config , build = None ) <EOL> def delete ( self , * args , ** kwargs ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> self . _destroy_containers ( [ c for c in self . container_set . exclude ( type = '<STR_LIT>' ) ] ) <EOL> except RuntimeError : <EOL> pass <EOL> self . _clean_app_logs ( ) <EOL> return super ( App , self ) . delete ( * args , ** kwargs ) <EOL> def restart ( self , ** kwargs ) : <EOL> to_restart = self . container_set . all ( ) <EOL> if kwargs . get ( '<STR_LIT:type>' ) : <EOL> to_restart = to_restart . filter ( type = kwargs . get ( '<STR_LIT:type>' ) ) <EOL> if kwargs . get ( '<STR_LIT>' ) : <EOL> to_restart = to_restart . filter ( num = kwargs . get ( '<STR_LIT>' ) ) <EOL> self . _restart_containers ( to_restart ) <EOL> return to_restart <EOL> def _clean_app_logs ( self ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> url = '<STR_LIT>' . format ( settings . LOGGER_HOST , settings . LOGGER_PORT , self . id ) <EOL> requests . delete ( url ) <EOL> except Exception as e : <EOL> err = '<STR_LIT>' . format ( e ) <EOL> log_event ( self , err , logging . WARNING ) <EOL> def scale ( self , user , structure ) : <EOL> """<STR_LIT>""" <EOL> if self . release_set . latest ( ) . build is None : <EOL> raise EnvironmentError ( '<STR_LIT>' ) <EOL> requested_structure = structure . copy ( ) <EOL> release = self . release_set . latest ( ) <EOL> available_process_types = release . build . procfile or { } <EOL> for container_type in requested_structure : <EOL> if container_type == '<STR_LIT>' : <EOL> continue <EOL> if container_type not in available_process_types : <EOL> raise EnvironmentError ( <EOL> '<STR_LIT>' . format ( container_type ) ) <EOL> msg = '<STR_LIT>' . format ( user . username ) + '<STR_LIT:U+0020>' . join ( <EOL> "<STR_LIT>" . format ( k , v ) for k , v in requested_structure . items ( ) ) <EOL> log_event ( self , msg ) <EOL> changed = False <EOL> to_add , to_remove = [ ] , [ ] <EOL> scale_types = { } <EOL> for container_type in requested_structure . keys ( ) : <EOL> containers = list ( self . container_set . filter ( type = container_type ) . order_by ( '<STR_LIT>' ) ) <EOL> results = self . container_set . filter ( type = container_type ) . aggregate ( Max ( '<STR_LIT>' ) ) <EOL> container_num = ( results . get ( '<STR_LIT>' ) or <NUM_LIT:0> ) + <NUM_LIT:1> <EOL> requested = requested_structure . pop ( container_type ) <EOL> diff = requested - len ( containers ) <EOL> if diff == <NUM_LIT:0> : <EOL> continue <EOL> changed = True <EOL> scale_types [ container_type ] = requested <EOL> while diff < <NUM_LIT:0> : <EOL> c = containers . pop ( ) <EOL> to_remove . append ( c ) <EOL> diff += <NUM_LIT:1> <EOL> while diff > <NUM_LIT:0> : <EOL> c = Container . objects . create ( owner = self . owner , <EOL> app = self , <EOL> release = release , <EOL> type = container_type , <EOL> num = container_num ) <EOL> to_add . append ( c ) <EOL> container_num += <NUM_LIT:1> <EOL> diff -= <NUM_LIT:1> <EOL> if changed : <EOL> if "<STR_LIT>" in dir ( self . _scheduler ) : <EOL> self . _scale_containers ( scale_types , to_remove ) <EOL> else : <EOL> if to_add : <EOL> self . _start_containers ( to_add ) <EOL> if to_remove : <EOL> self . _destroy_containers ( to_remove ) <EOL> vals = self . container_set . exclude ( type = '<STR_LIT>' ) . values ( <EOL> '<STR_LIT:type>' ) . annotate ( Count ( '<STR_LIT>' ) ) . order_by ( ) <EOL> new_structure = structure . copy ( ) <EOL> new_structure . update ( { v [ '<STR_LIT:type>' ] : v [ '<STR_LIT>' ] for v in vals } ) <EOL> self . structure = new_structure <EOL> self . save ( ) <EOL> return changed <EOL> def _scale_containers ( self , scale_types , to_remove ) : <EOL> release = self . release_set . latest ( ) <EOL> for scale_type in scale_types : <EOL> image = release . image <EOL> version = "<STR_LIT>" . format ( release . version ) <EOL> kwargs = { '<STR_LIT>' : release . config . memory , <EOL> '<STR_LIT>' : release . config . cpu , <EOL> '<STR_LIT>' : release . config . tags , <EOL> '<STR_LIT:version>' : version , <EOL> '<STR_LIT>' : self . id , <EOL> '<STR_LIT>' : scale_types [ scale_type ] } <EOL> job_id = self . _get_job_id ( scale_type ) <EOL> command = self . _get_command ( scale_type ) <EOL> try : <EOL> self . _scheduler . scale ( <EOL> name = job_id , <EOL> image = image , <EOL> command = command , <EOL> ** kwargs ) <EOL> except Exception as e : <EOL> err = '<STR_LIT>' . format ( job_id , e ) <EOL> log_event ( self , err , logging . ERROR ) <EOL> raise <EOL> [ c . delete ( ) for c in to_remove ] <EOL> def _start_containers ( self , to_add ) : <EOL> """<STR_LIT>""" <EOL> if not to_add : <EOL> return <EOL> create_threads = [ Thread ( target = c . create ) for c in to_add ] <EOL> start_threads = [ Thread ( target = c . start ) for c in to_add ] <EOL> [ t . start ( ) for t in create_threads ] <EOL> [ t . join ( ) for t in create_threads ] <EOL> if any ( c . state != '<STR_LIT>' for c in to_add ) : <EOL> err = '<STR_LIT>' <EOL> log_event ( self , err , logging . ERROR ) <EOL> self . _destroy_containers ( to_add ) <EOL> raise RuntimeError ( err ) <EOL> [ t . start ( ) for t in start_threads ] <EOL> [ t . join ( ) for t in start_threads ] <EOL> if set ( [ c . state for c in to_add ] ) != set ( [ '<STR_LIT>' ] ) : <EOL> err = '<STR_LIT>' <EOL> log_event ( self , err , logging . WARNING ) <EOL> try : <EOL> config = self . config_set . latest ( ) <EOL> if '<STR_LIT>' in config . values . keys ( ) : <EOL> self . _healthcheck ( to_add , config . values ) <EOL> except Config . DoesNotExist : <EOL> pass <EOL> def _healthcheck ( self , containers , config ) : <EOL> intervals = [ <NUM_LIT:1.0> , <NUM_LIT:0.1> , <NUM_LIT:0.5> , <NUM_LIT:1.0> ] <EOL> time . sleep ( <NUM_LIT:20> ) <EOL> for i in xrange ( len ( intervals ) ) : <EOL> delay = int ( config . get ( '<STR_LIT>' , <NUM_LIT:0> ) ) <EOL> try : <EOL> if delay > <NUM_LIT:0> : <EOL> time . sleep ( delay * intervals [ i ] ) <EOL> to_healthcheck = [ c for c in containers if c . type in [ '<STR_LIT>' , '<STR_LIT>' ] ] <EOL> self . _do_healthcheck ( to_healthcheck , config ) <EOL> break <EOL> except exceptions . HealthcheckException as e : <EOL> try : <EOL> next_delay = delay * intervals [ i + <NUM_LIT:1> ] <EOL> msg = "<STR_LIT>" . format ( e , next_delay ) <EOL> log_event ( self , msg , logging . WARNING ) <EOL> except IndexError : <EOL> log_event ( self , e , logging . WARNING ) <EOL> else : <EOL> self . _destroy_containers ( containers ) <EOL> msg = "<STR_LIT>" <EOL> log_event ( self , msg , logging . ERROR ) <EOL> raise RuntimeError ( msg ) <EOL> def _do_healthcheck ( self , containers , config ) : <EOL> path = config . get ( '<STR_LIT>' , '<STR_LIT:/>' ) <EOL> timeout = int ( config . get ( '<STR_LIT>' , <NUM_LIT:1> ) ) <EOL> if not _etcd_client : <EOL> raise exceptions . HealthcheckException ( '<STR_LIT>' ) <EOL> for container in containers : <EOL> try : <EOL> key = "<STR_LIT>" . format ( ** locals ( ) ) <EOL> url = "<STR_LIT>" . format ( _etcd_client . get ( key ) . value , path ) <EOL> response = requests . get ( url , timeout = timeout ) <EOL> if response . status_code != requests . codes . OK : <EOL> raise exceptions . HealthcheckException ( <EOL> "<STR_LIT>" . format ( <EOL> response . status_code ) ) <EOL> except ( requests . Timeout , requests . ConnectionError , KeyError ) as e : <EOL> raise exceptions . HealthcheckException ( <EOL> '<STR_LIT>' . format ( e ) ) <EOL> def _restart_containers ( self , to_restart ) : <EOL> """<STR_LIT>""" <EOL> if not to_restart : <EOL> return <EOL> stop_threads = [ Thread ( target = c . stop ) for c in to_restart ] <EOL> start_threads = [ Thread ( target = c . start ) for c in to_restart ] <EOL> [ t . start ( ) for t in stop_threads ] <EOL> [ t . join ( ) for t in stop_threads ] <EOL> if any ( c . state != '<STR_LIT>' for c in to_restart ) : <EOL> err = '<STR_LIT>' <EOL> log_event ( self , err , logging . WARNING ) <EOL> [ t . start ( ) for t in start_threads ] <EOL> [ t . join ( ) for t in start_threads ] <EOL> if any ( c . state != '<STR_LIT>' for c in to_restart ) : <EOL> err = '<STR_LIT>' <EOL> log_event ( self , err , logging . WARNING ) <EOL> def _destroy_containers ( self , to_destroy ) : <EOL> """<STR_LIT>""" <EOL> if not to_destroy : <EOL> return <EOL> destroy_threads = [ Thread ( target = c . destroy ) for c in to_destroy ] <EOL> [ t . start ( ) for t in destroy_threads ] <EOL> [ t . join ( ) for t in destroy_threads ] <EOL> [ c . delete ( ) for c in to_destroy if c . state == '<STR_LIT>' ] <EOL> if any ( c . state != '<STR_LIT>' for c in to_destroy ) : <EOL> err = '<STR_LIT>' <EOL> log_event ( self , err , logging . ERROR ) <EOL> raise RuntimeError ( err ) <EOL> def deploy ( self , user , release ) : <EOL> """<STR_LIT>""" <EOL> existing = self . container_set . exclude ( type = '<STR_LIT>' ) <EOL> new = [ ] <EOL> scale_types = set ( ) <EOL> for e in existing : <EOL> n = e . clone ( release ) <EOL> n . save ( ) <EOL> new . append ( n ) <EOL> scale_types . add ( e . type ) <EOL> if new and "<STR_LIT>" in dir ( self . _scheduler ) : <EOL> self . _deploy_app ( scale_types , release , existing ) <EOL> else : <EOL> self . _start_containers ( new ) <EOL> if existing : <EOL> self . _destroy_containers ( existing ) <EOL> if self . structure == { } and release . build is not None : <EOL> self . _default_scale ( user , release ) <EOL> def _deploy_app ( self , scale_types , release , existing ) : <EOL> for scale_type in scale_types : <EOL> image = release . image <EOL> version = "<STR_LIT>" . format ( release . version ) <EOL> kwargs = { '<STR_LIT>' : release . config . memory , <EOL> '<STR_LIT>' : release . config . cpu , <EOL> '<STR_LIT>' : release . config . tags , <EOL> '<STR_LIT>' : self . id , <EOL> '<STR_LIT>' : <NUM_LIT:0> , <EOL> '<STR_LIT:version>' : version } <EOL> job_id = self . _get_job_id ( scale_type ) <EOL> command = self . _get_command ( scale_type ) <EOL> try : <EOL> self . _scheduler . deploy ( <EOL> name = job_id , <EOL> image = image , <EOL> command = command , <EOL> ** kwargs ) <EOL> except Exception as e : <EOL> err = '<STR_LIT>' . format ( job_id , e ) <EOL> log_event ( self , err , logging . ERROR ) <EOL> raise <EOL> [ c . delete ( ) for c in existing ] <EOL> def _default_scale ( self , user , release ) : <EOL> """<STR_LIT>""" <EOL> if not release . build . sha : <EOL> structure = { '<STR_LIT>' : <NUM_LIT:1> } <EOL> elif release . build . dockerfile and not release . build . procfile : <EOL> structure = { '<STR_LIT>' : <NUM_LIT:1> } <EOL> elif release . build . procfile and '<STR_LIT>' not in release . build . procfile : <EOL> structure = { '<STR_LIT>' : <NUM_LIT:1> } <EOL> else : <EOL> structure = { '<STR_LIT>' : <NUM_LIT:1> } <EOL> self . scale ( user , structure ) <EOL> def logs ( self , log_lines = str ( settings . LOG_LINES ) ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> url = "<STR_LIT>" . format ( settings . LOGGER_HOST , settings . LOGGER_PORT , <EOL> self . id , log_lines ) <EOL> r = requests . get ( url ) <EOL> except requests . exceptions . RequestException as e : <EOL> logger . error ( "<STR_LIT>" . format ( url , e ) ) <EOL> raise e <EOL> if r . status_code == <NUM_LIT> or r . status_code == <NUM_LIT> : <EOL> logger . info ( "<STR_LIT>" . format ( url , r . status_code ) ) <EOL> raise EnvironmentError ( '<STR_LIT>' ) <EOL> if r . status_code != <NUM_LIT:200> : <EOL> logger . error ( "<STR_LIT>" <EOL> . format ( url , r . status_code ) ) <EOL> raise EnvironmentError ( '<STR_LIT>' ) <EOL> return r . content <EOL> def run ( self , user , command ) : <EOL> """<STR_LIT>""" <EOL> if not settings . SSH_PRIVATE_KEY : <EOL> raise EnvironmentError ( '<STR_LIT>' ) <EOL> if self . release_set . latest ( ) . build is None : <EOL> raise EnvironmentError ( '<STR_LIT>' ) <EOL> msg = "<STR_LIT>" . format ( user . username , command ) <EOL> log_event ( self , msg ) <EOL> c_num = max ( [ c . num for c in self . container_set . filter ( type = '<STR_LIT>' ) ] or [ <NUM_LIT:0> ] ) + <NUM_LIT:1> <EOL> c = Container . objects . create ( owner = self . owner , <EOL> app = self , <EOL> release = self . release_set . latest ( ) , <EOL> type = '<STR_LIT>' , <EOL> num = c_num ) <EOL> image = c . release . image <EOL> def _has_hostname ( image ) : <EOL> repo , tag = dockerutils . parse_repository_tag ( image ) <EOL> return True if '<STR_LIT:/>' in repo and '<STR_LIT:.>' in repo . split ( '<STR_LIT:/>' ) [ <NUM_LIT:0> ] else False <EOL> if not _has_hostname ( image ) : <EOL> image = '<STR_LIT>' . format ( settings . REGISTRY_HOST , <EOL> settings . REGISTRY_PORT , <EOL> image ) <EOL> escaped_command = command . replace ( "<STR_LIT:'>" , "<STR_LIT>" ) <EOL> return c . run ( escaped_command ) <EOL> @ python_2_unicode_compatible <EOL> class Container ( UuidAuditedModel ) : <EOL> """<STR_LIT>""" <EOL> owner = models . ForeignKey ( settings . AUTH_USER_MODEL ) <EOL> app = models . ForeignKey ( '<STR_LIT>' ) <EOL> release = models . ForeignKey ( '<STR_LIT>' ) <EOL> type = models . CharField ( max_length = <NUM_LIT> , blank = False ) <EOL> num = models . PositiveIntegerField ( ) <EOL> @ property <EOL> def _scheduler ( self ) : <EOL> return self . app . _scheduler <EOL> @ property <EOL> def state ( self ) : <EOL> return self . _scheduler . state ( self . job_id ) . name <EOL> def short_name ( self ) : <EOL> return "<STR_LIT>" . format ( self . app . id , self . type , self . num ) <EOL> short_name . short_description = '<STR_LIT:Name>' <EOL> def __str__ ( self ) : <EOL> return self . short_name ( ) <EOL> class Meta : <EOL> get_latest_by = '<STR_LIT>' <EOL> ordering = [ '<STR_LIT>' ] <EOL> @ property <EOL> def job_id ( self ) : <EOL> version = "<STR_LIT>" . format ( self . release . version ) <EOL> return "<STR_LIT>" . format ( ** locals ( ) ) <EOL> def _get_command ( self ) : <EOL> try : <EOL> if self . release . build . dockerfile or not self . release . build . sha : <EOL> return "<STR_LIT>" . format ( self . release . build . procfile [ self . type ] ) <EOL> else : <EOL> return '<STR_LIT>' . format ( self . type ) <EOL> except ( KeyError , TypeError , AttributeError ) : <EOL> return '<STR_LIT>' if self . type == '<STR_LIT>' else '<STR_LIT>' . format ( self . type ) <EOL> _command = property ( _get_command ) <EOL> def clone ( self , release ) : <EOL> c = Container . objects . create ( owner = self . owner , <EOL> app = self . app , <EOL> release = release , <EOL> type = self . type , <EOL> num = self . num ) <EOL> return c <EOL> @ close_db_connections <EOL> def create ( self ) : <EOL> image = self . release . image <EOL> kwargs = { '<STR_LIT>' : self . release . config . memory , <EOL> '<STR_LIT>' : self . release . config . cpu , <EOL> '<STR_LIT>' : self . release . config . tags } <EOL> try : <EOL> self . _scheduler . create ( <EOL> name = self . job_id , <EOL> image = image , <EOL> command = self . _command , <EOL> ** kwargs ) <EOL> except Exception as e : <EOL> err = '<STR_LIT>' . format ( self . job_id , e ) <EOL> log_event ( self . app , err , logging . ERROR ) <EOL> raise <EOL> @ close_db_connections <EOL> def start ( self ) : <EOL> try : <EOL> self . _scheduler . start ( self . job_id ) <EOL> except Exception as e : <EOL> err = '<STR_LIT>' . format ( self . job_id , e ) <EOL> log_event ( self . app , err , logging . WARNING ) <EOL> raise <EOL> @ close_db_connections <EOL> def stop ( self ) : <EOL> try : <EOL> self . _scheduler . stop ( self . job_id ) <EOL> except Exception as e : <EOL> err = '<STR_LIT>' . format ( self . job_id , e ) <EOL> log_event ( self . app , err , logging . ERROR ) <EOL> raise <EOL> @ close_db_connections <EOL> def destroy ( self ) : <EOL> try : <EOL> self . _scheduler . destroy ( self . job_id ) <EOL> except Exception as e : <EOL> err = '<STR_LIT>' . format ( self . job_id , e ) <EOL> log_event ( self . app , err , logging . ERROR ) <EOL> raise <EOL> def run ( self , command ) : <EOL> """<STR_LIT>""" <EOL> if self . release . build is None : <EOL> raise EnvironmentError ( '<STR_LIT>' <EOL> '<STR_LIT>' ) <EOL> image = self . release . image <EOL> entrypoint = '<STR_LIT>' <EOL> if self . release . build . procfile and self . release . build . sha and not self . release . build . dockerfile : <EOL> entrypoint = '<STR_LIT>' <EOL> command = "<STR_LIT>" . format ( command ) <EOL> else : <EOL> command = "<STR_LIT>" . format ( command ) <EOL> try : <EOL> rc , output = self . _scheduler . run ( self . job_id , image , entrypoint , command ) <EOL> return rc , output <EOL> except Exception as e : <EOL> err = '<STR_LIT>' . format ( self . job_id , e ) <EOL> log_event ( self . app , err , logging . ERROR ) <EOL> raise <EOL> @ python_2_unicode_compatible <EOL> class Push ( UuidAuditedModel ) : <EOL> """<STR_LIT>""" <EOL> owner = models . ForeignKey ( settings . AUTH_USER_MODEL ) <EOL> app = models . ForeignKey ( '<STR_LIT>' ) <EOL> sha = models . CharField ( max_length = <NUM_LIT> ) <EOL> fingerprint = models . CharField ( max_length = <NUM_LIT:255> ) <EOL> receive_user = models . CharField ( max_length = <NUM_LIT:255> ) <EOL> receive_repo = models . CharField ( max_length = <NUM_LIT:255> ) <EOL> ssh_connection = models . CharField ( max_length = <NUM_LIT:255> ) <EOL> ssh_original_command = models . CharField ( max_length = <NUM_LIT:255> ) <EOL> class Meta : <EOL> get_latest_by = '<STR_LIT>' <EOL> ordering = [ '<STR_LIT>' ] <EOL> unique_together = ( ( '<STR_LIT>' , '<STR_LIT>' ) , ) <EOL> def __str__ ( self ) : <EOL> return "<STR_LIT>" . format ( self . app . id , self . sha [ : <NUM_LIT:7> ] ) <EOL> @ python_2_unicode_compatible <EOL> class Build ( UuidAuditedModel ) : <EOL> """<STR_LIT>""" <EOL> owner = models . ForeignKey ( settings . AUTH_USER_MODEL ) <EOL> app = models . ForeignKey ( '<STR_LIT>' ) <EOL> image = models . CharField ( max_length = <NUM_LIT> ) <EOL> sha = models . CharField ( max_length = <NUM_LIT> , blank = True ) <EOL> procfile = JSONField ( default = { } , blank = True ) <EOL> dockerfile = models . TextField ( blank = True ) <EOL> class Meta : <EOL> get_latest_by = '<STR_LIT>' <EOL> ordering = [ '<STR_LIT>' ] <EOL> unique_together = ( ( '<STR_LIT>' , '<STR_LIT>' ) , ) <EOL> def create ( self , user , * args , ** kwargs ) : <EOL> latest_release = self . app . release_set . latest ( ) <EOL> source_version = '<STR_LIT>' <EOL> if self . sha : <EOL> source_version = '<STR_LIT>' . format ( self . sha ) <EOL> new_release = latest_release . new ( user , <EOL> build = self , <EOL> config = latest_release . config , <EOL> source_version = source_version ) <EOL> try : <EOL> self . app . deploy ( user , new_release ) <EOL> return new_release <EOL> except RuntimeError : <EOL> new_release . delete ( ) <EOL> raise <EOL> def save ( self , ** kwargs ) : <EOL> try : <EOL> previous_build = self . app . build_set . latest ( ) <EOL> to_destroy = [ ] <EOL> for proctype in previous_build . procfile : <EOL> if proctype not in self . procfile : <EOL> for c in self . app . container_set . filter ( type = proctype ) : <EOL> to_destroy . append ( c ) <EOL> self . app . _destroy_containers ( to_destroy ) <EOL> except Build . DoesNotExist : <EOL> pass <EOL> return super ( Build , self ) . save ( ** kwargs ) <EOL> def __str__ ( self ) : <EOL> return "<STR_LIT>" . format ( self . app . id , self . uuid [ : <NUM_LIT:7> ] ) <EOL> @ python_2_unicode_compatible <EOL> class Config ( UuidAuditedModel ) : <EOL> """<STR_LIT>""" <EOL> owner = models . ForeignKey ( settings . AUTH_USER_MODEL ) <EOL> app = models . ForeignKey ( '<STR_LIT>' ) <EOL> values = JSONField ( default = { } , blank = True ) <EOL> memory = JSONField ( default = { } , blank = True ) <EOL> cpu = JSONField ( default = { } , blank = True ) <EOL> tags = JSONField ( default = { } , blank = True ) <EOL> class Meta : <EOL> get_latest_by = '<STR_LIT>' <EOL> ordering = [ '<STR_LIT>' ] <EOL> unique_together = ( ( '<STR_LIT>' , '<STR_LIT>' ) , ) <EOL> def __str__ ( self ) : <EOL> return "<STR_LIT>" . format ( self . app . id , self . uuid [ : <NUM_LIT:7> ] ) <EOL> def save ( self , ** kwargs ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> previous_config = self . app . config_set . latest ( ) <EOL> for attr in [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] : <EOL> try : <EOL> data = getattr ( previous_config , attr ) . copy ( ) <EOL> except AttributeError : <EOL> data = { } <EOL> try : <EOL> new_data = getattr ( self , attr ) . copy ( ) <EOL> except AttributeError : <EOL> new_data = { } <EOL> data . update ( new_data ) <EOL> [ data . pop ( k ) for k , v in new_data . viewitems ( ) if v is None ] <EOL> setattr ( self , attr , data ) <EOL> except Config . DoesNotExist : <EOL> pass <EOL> return super ( Config , self ) . save ( ** kwargs ) <EOL> @ python_2_unicode_compatible <EOL> class Release ( UuidAuditedModel ) : <EOL> """<STR_LIT>""" <EOL> owner = models . ForeignKey ( settings . AUTH_USER_MODEL ) <EOL> app = models . ForeignKey ( '<STR_LIT>' ) <EOL> version = models . PositiveIntegerField ( ) <EOL> summary = models . TextField ( blank = True , null = True ) <EOL> config = models . ForeignKey ( '<STR_LIT>' ) <EOL> build = models . ForeignKey ( '<STR_LIT>' , null = True ) <EOL> class Meta : <EOL> get_latest_by = '<STR_LIT>' <EOL> ordering = [ '<STR_LIT>' ] <EOL> unique_together = ( ( '<STR_LIT>' , '<STR_LIT:version>' ) , ) <EOL> def __str__ ( self ) : <EOL> return "<STR_LIT>" . format ( self . app . id , self . version ) <EOL> @ property <EOL> def image ( self ) : <EOL> return '<STR_LIT>' . format ( self . app . id , str ( self . version ) ) <EOL> def new ( self , user , config , build , summary = None , source_version = '<STR_LIT>' ) : <EOL> """<STR_LIT>""" <EOL> new_version = self . version + <NUM_LIT:1> <EOL> release = Release . objects . create ( <EOL> owner = user , app = self . app , config = config , <EOL> build = build , version = new_version , summary = summary ) <EOL> try : <EOL> release . publish ( ) <EOL> except EnvironmentError as e : <EOL> log_event ( self . app , e ) <EOL> pass <EOL> return release <EOL> def publish ( self , source_version = '<STR_LIT>' ) : <EOL> if self . build is None : <EOL> raise EnvironmentError ( '<STR_LIT>' ) <EOL> source_image = self . build . image <EOL> if '<STR_LIT::>' not in source_image : <EOL> source_tag = '<STR_LIT>' . format ( self . build . sha ) if self . build . sha else source_version <EOL> source_image = "<STR_LIT>" . format ( source_image , source_tag ) <EOL> deis_registry = bool ( self . build . sha ) <EOL> publish_release ( source_image , self . config . values , self . image , deis_registry ) <EOL> def previous ( self ) : <EOL> """<STR_LIT>""" <EOL> releases = self . app . release_set <EOL> if self . pk : <EOL> releases = releases . exclude ( pk = self . pk ) <EOL> try : <EOL> prev_release = releases . latest ( ) <EOL> except Release . DoesNotExist : <EOL> prev_release = None <EOL> return prev_release <EOL> def rollback ( self , user , version ) : <EOL> if version < <NUM_LIT:1> : <EOL> raise EnvironmentError ( '<STR_LIT>' ) <EOL> summary = "<STR_LIT>" . format ( user , version ) <EOL> prev = self . app . release_set . get ( version = version ) <EOL> new_release = self . new ( <EOL> user , <EOL> build = prev . build , <EOL> config = prev . config , <EOL> summary = summary , <EOL> source_version = '<STR_LIT>' . format ( version ) ) <EOL> try : <EOL> self . app . deploy ( user , new_release ) <EOL> return new_release <EOL> except RuntimeError : <EOL> new_release . delete ( ) <EOL> raise <EOL> def save ( self , * args , ** kwargs ) : <EOL> if not self . summary : <EOL> self . summary = '<STR_LIT>' <EOL> prev_release = self . previous ( ) <EOL> old_build = prev_release . build if prev_release else None <EOL> old_config = prev_release . config if prev_release else None <EOL> if self . version == <NUM_LIT:1> : <EOL> self . summary += "<STR_LIT>" . format ( self . app . owner ) <EOL> elif self . build != old_build : <EOL> if self . build . sha : <EOL> self . summary += "<STR_LIT>" . format ( self . build . owner , self . build . sha [ : <NUM_LIT:7> ] ) <EOL> else : <EOL> self . summary += "<STR_LIT>" . format ( self . build . owner , self . build . image ) <EOL> if self . config != old_config : <EOL> dict1 = self . config . values <EOL> dict2 = old_config . values if old_config else { } <EOL> diff = dict_diff ( dict1 , dict2 ) <EOL> added = '<STR_LIT:U+002CU+0020>' . join ( k for k in diff . get ( '<STR_LIT>' , { } ) ) <EOL> added = '<STR_LIT>' + added if added else '<STR_LIT>' <EOL> changed = '<STR_LIT:U+002CU+0020>' . join ( k for k in diff . get ( '<STR_LIT>' , { } ) ) <EOL> changed = '<STR_LIT>' + changed if changed else '<STR_LIT>' <EOL> deleted = '<STR_LIT:U+002CU+0020>' . join ( k for k in diff . get ( '<STR_LIT>' , { } ) ) <EOL> deleted = '<STR_LIT>' + deleted if deleted else '<STR_LIT>' <EOL> changes = '<STR_LIT:U+002CU+0020>' . join ( i for i in ( added , changed , deleted ) if i ) <EOL> if changes : <EOL> if self . summary : <EOL> self . summary += '<STR_LIT>' <EOL> self . summary += "<STR_LIT>" . format ( self . config . owner , changes ) <EOL> changes = [ ] <EOL> old_mem = old_config . memory if old_config else { } <EOL> diff = dict_diff ( self . config . memory , old_mem ) <EOL> if diff . get ( '<STR_LIT>' ) or diff . get ( '<STR_LIT>' ) or diff . get ( '<STR_LIT>' ) : <EOL> changes . append ( '<STR_LIT>' ) <EOL> old_cpu = old_config . cpu if old_config else { } <EOL> diff = dict_diff ( self . config . cpu , old_cpu ) <EOL> if diff . get ( '<STR_LIT>' ) or diff . get ( '<STR_LIT>' ) or diff . get ( '<STR_LIT>' ) : <EOL> changes . append ( '<STR_LIT>' ) <EOL> if changes : <EOL> changes = '<STR_LIT>' + '<STR_LIT:U+002CU+0020>' . join ( changes ) <EOL> self . summary += "<STR_LIT>" . format ( self . config . owner , changes ) <EOL> changes = [ ] <EOL> old_tags = old_config . tags if old_config else { } <EOL> diff = dict_diff ( self . config . tags , old_tags ) <EOL> added = '<STR_LIT:U+002CU+0020>' . join ( k for k in diff . get ( '<STR_LIT>' , { } ) ) <EOL> added = '<STR_LIT>' + added if added else '<STR_LIT>' <EOL> changed = '<STR_LIT:U+002CU+0020>' . join ( k for k in diff . get ( '<STR_LIT>' , { } ) ) <EOL> changed = '<STR_LIT>' + changed if changed else '<STR_LIT>' <EOL> deleted = '<STR_LIT:U+002CU+0020>' . join ( k for k in diff . get ( '<STR_LIT>' , { } ) ) <EOL> deleted = '<STR_LIT>' + deleted if deleted else '<STR_LIT>' <EOL> changes = '<STR_LIT:U+002CU+0020>' . join ( i for i in ( added , changed , deleted ) if i ) <EOL> if changes : <EOL> if self . summary : <EOL> self . summary += '<STR_LIT>' <EOL> self . summary += "<STR_LIT>" . format ( self . config . owner , changes ) <EOL> if not self . summary : <EOL> if self . version == <NUM_LIT:1> : <EOL> self . summary = "<STR_LIT>" . format ( self . owner ) <EOL> else : <EOL> self . summary = "<STR_LIT>" . format ( self . owner ) <EOL> super ( Release , self ) . save ( * args , ** kwargs ) <EOL> @ python_2_unicode_compatible <EOL> class Domain ( AuditedModel ) : <EOL> owner = models . ForeignKey ( settings . AUTH_USER_MODEL ) <EOL> app = models . ForeignKey ( '<STR_LIT>' ) <EOL> domain = models . TextField ( blank = False , null = False , unique = True ) <EOL> def __str__ ( self ) : <EOL> return self . domain <EOL> @ python_2_unicode_compatible <EOL> class Certificate ( AuditedModel ) : <EOL> """<STR_LIT>""" <EOL> owner = models . ForeignKey ( settings . AUTH_USER_MODEL ) <EOL> certificate = models . TextField ( validators = [ validate_certificate ] ) <EOL> key = models . TextField ( ) <EOL> common_name = models . TextField ( unique = True , validators = [ validate_common_name ] ) <EOL> expires = models . DateTimeField ( ) <EOL> def __str__ ( self ) : <EOL> return self . common_name <EOL> def _get_certificate ( self ) : <EOL> try : <EOL> return crypto . load_certificate ( crypto . FILETYPE_PEM , self . certificate ) <EOL> except crypto . Error as e : <EOL> raise SuspiciousOperation ( e ) <EOL> def save ( self , * args , ** kwargs ) : <EOL> certificate = self . _get_certificate ( ) <EOL> if not self . common_name : <EOL> self . common_name = certificate . get_subject ( ) . CN <EOL> if not self . expires : <EOL> self . expires = datetime . strptime ( certificate . get_notAfter ( ) , '<STR_LIT>' ) <EOL> return super ( Certificate , self ) . save ( * args , ** kwargs ) <EOL> @ python_2_unicode_compatible <EOL> class Key ( UuidAuditedModel ) : <EOL> """<STR_LIT>""" <EOL> owner = models . ForeignKey ( settings . AUTH_USER_MODEL ) <EOL> id = models . CharField ( max_length = <NUM_LIT> ) <EOL> public = models . TextField ( unique = True , validators = [ validate_base64 ] ) <EOL> fingerprint = models . CharField ( max_length = <NUM_LIT> ) <EOL> class Meta : <EOL> verbose_name = '<STR_LIT>' <EOL> unique_together = ( ( '<STR_LIT>' , '<STR_LIT>' ) ) <EOL> def __str__ ( self ) : <EOL> return "<STR_LIT>" . format ( self . public [ : <NUM_LIT> ] , self . public [ - <NUM_LIT> : ] ) <EOL> def save ( self , * args , ** kwargs ) : <EOL> self . fingerprint = fingerprint ( self . public ) <EOL> return super ( Key , self ) . save ( * args , ** kwargs ) <EOL> def _log_build_created ( ** kwargs ) : <EOL> if kwargs . get ( '<STR_LIT>' ) : <EOL> build = kwargs [ '<STR_LIT>' ] <EOL> logger . info ( "<STR_LIT>" . format ( build . app , build ) ) <EOL> def _log_release_created ( ** kwargs ) : <EOL> if kwargs . get ( '<STR_LIT>' ) : <EOL> release = kwargs [ '<STR_LIT>' ] <EOL> logger . info ( "<STR_LIT>" . format ( release . app , release ) ) <EOL> release . app . log ( release . summary ) <EOL> def _log_config_updated ( ** kwargs ) : <EOL> config = kwargs [ '<STR_LIT>' ] <EOL> logger . info ( "<STR_LIT>" . format ( config . app , config ) ) <EOL> def _log_domain_added ( ** kwargs ) : <EOL> if kwargs . get ( '<STR_LIT>' ) : <EOL> domain = kwargs [ '<STR_LIT>' ] <EOL> msg = "<STR_LIT>" . format ( domain ) <EOL> log_event ( domain . app , msg ) <EOL> def _log_domain_removed ( ** kwargs ) : <EOL> domain = kwargs [ '<STR_LIT>' ] <EOL> msg = "<STR_LIT>" . format ( domain ) <EOL> log_event ( domain . app , msg ) <EOL> def _log_cert_added ( ** kwargs ) : <EOL> if kwargs . get ( '<STR_LIT>' ) : <EOL> cert = kwargs [ '<STR_LIT>' ] <EOL> logger . info ( "<STR_LIT>" . format ( cert ) ) <EOL> def _log_cert_removed ( ** kwargs ) : <EOL> cert = kwargs [ '<STR_LIT>' ] <EOL> logger . info ( "<STR_LIT>" . format ( cert ) ) <EOL> def _etcd_publish_key ( ** kwargs ) : <EOL> key = kwargs [ '<STR_LIT>' ] <EOL> _etcd_client . write ( '<STR_LIT>' . format ( <EOL> key . owner . username , fingerprint ( key . public ) ) , key . public ) <EOL> def _etcd_purge_key ( ** kwargs ) : <EOL> key = kwargs [ '<STR_LIT>' ] <EOL> try : <EOL> _etcd_client . delete ( '<STR_LIT>' . format ( <EOL> key . owner . username , fingerprint ( key . public ) ) ) <EOL> except KeyError : <EOL> pass <EOL> def _etcd_purge_user ( ** kwargs ) : <EOL> username = kwargs [ '<STR_LIT>' ] . username <EOL> try : <EOL> _etcd_client . delete ( <EOL> '<STR_LIT>' . format ( username ) , dir = True , recursive = True ) <EOL> except KeyError : <EOL> pass <EOL> def _etcd_publish_app ( ** kwargs ) : <EOL> appname = kwargs [ '<STR_LIT>' ] <EOL> try : <EOL> _etcd_client . write ( '<STR_LIT>' . format ( appname ) , None , dir = True ) <EOL> except KeyError : <EOL> pass <EOL> def _etcd_purge_app ( ** kwargs ) : <EOL> appname = kwargs [ '<STR_LIT>' ] <EOL> try : <EOL> _etcd_client . delete ( '<STR_LIT>' . format ( appname ) , dir = True , recursive = True ) <EOL> except KeyError : <EOL> pass <EOL> def _etcd_publish_cert ( ** kwargs ) : <EOL> cert = kwargs [ '<STR_LIT>' ] <EOL> _etcd_client . write ( '<STR_LIT>' . format ( cert ) , cert . certificate ) <EOL> _etcd_client . write ( '<STR_LIT>' . format ( cert ) , cert . key ) <EOL> def _etcd_purge_cert ( ** kwargs ) : <EOL> cert = kwargs [ '<STR_LIT>' ] <EOL> try : <EOL> _etcd_client . delete ( '<STR_LIT>' . format ( cert ) , <EOL> prevExist = True , dir = True , recursive = True ) <EOL> except KeyError : <EOL> pass <EOL> def _etcd_publish_config ( ** kwargs ) : <EOL> config = kwargs [ '<STR_LIT>' ] <EOL> try : <EOL> _etcd_client . delete ( '<STR_LIT>' . format ( config . app ) , <EOL> prevExist = True , dir = True , recursive = True ) <EOL> except KeyError : <EOL> pass <EOL> for k , v in config . values . iteritems ( ) : <EOL> _etcd_client . write ( <EOL> '<STR_LIT>' . format ( <EOL> config . app , <EOL> unicode ( k ) . encode ( '<STR_LIT:utf-8>' ) . lower ( ) ) , <EOL> unicode ( v ) . encode ( '<STR_LIT:utf-8>' ) ) <EOL> def _etcd_purge_config ( ** kwargs ) : <EOL> config = kwargs [ '<STR_LIT>' ] <EOL> try : <EOL> _etcd_client . delete ( '<STR_LIT>' . format ( config . app ) , <EOL> prevExist = True , dir = True , recursive = True ) <EOL> except KeyError : <EOL> pass <EOL> def _etcd_publish_domains ( ** kwargs ) : <EOL> domain = kwargs [ '<STR_LIT>' ] <EOL> _etcd_client . write ( '<STR_LIT>' . format ( domain ) , domain . app ) <EOL> def _etcd_purge_domains ( ** kwargs ) : <EOL> domain = kwargs [ '<STR_LIT>' ] <EOL> try : <EOL> _etcd_client . delete ( '<STR_LIT>' . format ( domain ) , <EOL> prevExist = True , dir = True , recursive = True ) <EOL> except KeyError : <EOL> pass <EOL> post_save . connect ( _log_build_created , sender = Build , dispatch_uid = '<STR_LIT>' ) <EOL> post_save . connect ( _log_release_created , sender = Release , dispatch_uid = '<STR_LIT>' ) <EOL> post_save . connect ( _log_config_updated , sender = Config , dispatch_uid = '<STR_LIT>' ) <EOL> post_save . connect ( _log_domain_added , sender = Domain , dispatch_uid = '<STR_LIT>' ) <EOL> post_save . connect ( _log_cert_added , sender = Certificate , dispatch_uid = '<STR_LIT>' ) <EOL> post_delete . connect ( _log_domain_removed , sender = Domain , dispatch_uid = '<STR_LIT>' ) <EOL> post_delete . connect ( _log_cert_removed , sender = Certificate , dispatch_uid = '<STR_LIT>' ) <EOL> @ receiver ( post_save , sender = get_user_model ( ) ) <EOL> def create_auth_token ( sender , instance = None , created = False , ** kwargs ) : <EOL> if created : <EOL> Token . objects . create ( user = instance ) <EOL> _etcd_client = get_etcd_client ( ) <EOL> if _etcd_client : <EOL> post_save . connect ( _etcd_publish_key , sender = Key , dispatch_uid = '<STR_LIT>' ) <EOL> post_delete . connect ( _etcd_purge_key , sender = Key , dispatch_uid = '<STR_LIT>' ) <EOL> post_delete . connect ( _etcd_purge_user , sender = get_user_model ( ) , dispatch_uid = '<STR_LIT>' ) <EOL> post_save . connect ( _etcd_publish_domains , sender = Domain , dispatch_uid = '<STR_LIT>' ) <EOL> post_delete . connect ( _etcd_purge_domains , sender = Domain , dispatch_uid = '<STR_LIT>' ) <EOL> post_save . connect ( _etcd_publish_app , sender = App , dispatch_uid = '<STR_LIT>' ) <EOL> post_delete . connect ( _etcd_purge_app , sender = App , dispatch_uid = '<STR_LIT>' ) <EOL> post_save . connect ( _etcd_publish_cert , sender = Certificate , dispatch_uid = '<STR_LIT>' ) <EOL> post_delete . connect ( _etcd_purge_cert , sender = Certificate , dispatch_uid = '<STR_LIT>' ) <EOL> post_save . connect ( _etcd_publish_config , sender = Config , dispatch_uid = '<STR_LIT>' ) <EOL> post_delete . connect ( _etcd_purge_config , sender = Config , dispatch_uid = '<STR_LIT>' ) </s>
