1212import logging
1313import re
1414import time
15+ import json
1516from threading import Thread
1617
1718from django .conf import settings
3031
3132from api import fields , utils , exceptions
3233from registry import publish_release
33- from utils import dict_diff , fingerprint
34+ from utils import dict_diff , dict_merge , fingerprint
3435
3536
3637logger = logging .getLogger (__name__ )
@@ -325,7 +326,13 @@ def _scale_containers(self, scale_types, to_remove):
325326 name = job_id ,
326327 image = image ,
327328 command = command ,
328- ** kwargs )
329+ ** kwargs
330+ )
331+
332+ # Attach the platform specific application sub domain
333+ # scheduler.scale creates the required service on apps:create
334+ if not Domain .objects .filter (owner = self .owner , app = self , domain = self ).exists ():
335+ Domain (owner = self .owner , app = self , domain = str (self )).save ()
329336 except Exception as e :
330337 err = '{} (scale): {}' .format (job_id , e )
331338 log_event (self , err , logging .ERROR )
@@ -614,7 +621,8 @@ def create(self):
614621 name = self .job_id ,
615622 image = image ,
616623 command = self ._command ,
617- ** kwargs )
624+ ** kwargs
625+ )
618626 except Exception as e :
619627 err = '{} (create): {}' .format (self .job_id , e )
620628 log_event (self .app , err , logging .ERROR )
@@ -965,6 +973,69 @@ class Domain(AuditedModel):
965973 app = models .ForeignKey ('App' )
966974 domain = models .TextField (blank = False , null = False , unique = True )
967975
976+ @property
977+ def _scheduler (self ):
978+ mod = importlib .import_module (settings .SCHEDULER_MODULE )
979+ return mod .SchedulerClient (settings .SCHEDULER_URL ,
980+ settings .SCHEDULER_AUTH ,
981+ settings .SCHEDULER_OPTIONS )
982+
983+ def _load_service_config (self , app ):
984+ # Get the service from k8s to attach the domain correctly
985+ svc = self ._scheduler ._get_service (app , app ).json ()
986+ # Get minimum structure going if it is missing on the service
987+ if 'metadata' not in svc or 'annotations' not in svc ['metadata' ]:
988+ default = {'metadata' : {'annotations' : {}}}
989+ svc = dict_merge (svc , default )
990+
991+ # Check if any config has been set
992+ if 'deis.io/routerConfig' not in svc ['metadata' ]['annotations' ]:
993+ config = {}
994+ else :
995+ config = json .loads (svc ['metadata' ]['annotations' ]['deis.io/routerConfig' ])
996+
997+ # See if domains are available
998+ if 'domains' not in config :
999+ config ['domains' ] = []
1000+
1001+ return svc , config
1002+
1003+ def save (self , * args , ** kwargs ):
1004+ app = str (self .app )
1005+ domain = str (self .domain )
1006+
1007+ # setup the service and config dict
1008+ svc , config = self ._load_service_config (app )
1009+ if domain not in config ['domains' ]:
1010+ config ['domains' ].append (domain )
1011+
1012+ # save as a JSON string since annotations don't take a structure on its keys
1013+ svc ['metadata' ]['annotations' ]['deis.io/routerConfig' ] = json .dumps (config )
1014+
1015+ # Update the k8s service for the application with new domain information
1016+ self ._scheduler ._update_service (app , app , svc )
1017+
1018+ # Save to DB
1019+ return super (Domain , self ).save (* args , ** kwargs )
1020+
1021+ def delete (self , * args , ** kwargs ):
1022+ app = str (self .app )
1023+ domain = str (self .domain )
1024+
1025+ # setup the service and config dict
1026+ svc , config = self ._load_service_config (app )
1027+ if domain in config ['domains' ]:
1028+ config ['domains' ].remove (domain )
1029+
1030+ # save as a JSON string since annotations don't take a structure on its keys
1031+ svc ['metadata' ]['annotations' ]['deis.io/routerConfig' ] = json .dumps (config )
1032+
1033+ # Update the k8s service for the application with new domain information
1034+ self ._scheduler ._update_service (app , app , svc )
1035+
1036+ # Delete from DB
1037+ return super (Domain , self ).delete (* args , ** kwargs )
1038+
9681039 def __str__ (self ):
9691040 return self .domain
9701041
@@ -1154,21 +1225,6 @@ def _etcd_purge_config(**kwargs):
11541225 except KeyError :
11551226 pass
11561227
1157-
1158- def _etcd_publish_domains (** kwargs ):
1159- domain = kwargs ['instance' ]
1160- _etcd_client .write ('/deis/domains/{}' .format (domain ), domain .app )
1161-
1162-
1163- def _etcd_purge_domains (** kwargs ):
1164- domain = kwargs ['instance' ]
1165- try :
1166- _etcd_client .delete ('/deis/domains/{}' .format (domain ),
1167- prevExist = True , dir = True , recursive = True )
1168- except KeyError :
1169- pass
1170-
1171-
11721228# Log significant app-related events
11731229post_save .connect (_log_build_created , sender = Build , dispatch_uid = 'api.models.log' )
11741230post_save .connect (_log_release_created , sender = Release , dispatch_uid = 'api.models.log' )
@@ -1193,8 +1249,6 @@ def create_auth_token(sender, instance=None, created=False, **kwargs):
11931249 post_save .connect (_etcd_publish_key , sender = Key , dispatch_uid = 'api.models' )
11941250 post_delete .connect (_etcd_purge_key , sender = Key , dispatch_uid = 'api.models' )
11951251 post_delete .connect (_etcd_purge_user , sender = get_user_model (), dispatch_uid = 'api.models' )
1196- post_save .connect (_etcd_publish_domains , sender = Domain , dispatch_uid = 'api.models' )
1197- post_delete .connect (_etcd_purge_domains , sender = Domain , dispatch_uid = 'api.models' )
11981252 post_save .connect (_etcd_publish_app , sender = App , dispatch_uid = 'api.models' )
11991253 post_delete .connect (_etcd_purge_app , sender = App , dispatch_uid = 'api.models' )
12001254 post_save .connect (_etcd_publish_cert , sender = Certificate , dispatch_uid = 'api.models' )
0 commit comments