@@ -134,7 +134,9 @@ def __str__(self):
134134 def create (self , * args , ** kwargs ):
135135 config = Config .objects .create (owner = self .owner , app = self , values = {})
136136 build = Build .objects .create (owner = self .owner , app = self , image = settings .DEFAULT_BUILD )
137- Release .objects .create (version = 1 , owner = self .owner , app = self , config = config , build = build )
137+ limit = Limit .objects .create (owner = self .owner , app = self , memory = {}, cpu = {})
138+ Release .objects .create (version = 1 , owner = self .owner , app = self ,
139+ config = config , build = build , limit = limit )
138140
139141 def delete (self , * args , ** kwargs ):
140142 for c in self .container_set .all ():
@@ -301,10 +303,13 @@ def _command_announceable(self):
301303 @transition (field = state , source = INITIALIZED , target = CREATED )
302304 def create (self ):
303305 image = self .release .image
306+ kwargs = {'memory' : self .release .limit .memory ,
307+ 'cpu' : self .release .limit .cpu }
304308 self ._scheduler .create (name = self ._job_id ,
305309 image = image ,
306310 command = self ._command ,
307- use_announcer = self ._command_announceable ())
311+ use_announcer = self ._command_announceable (),
312+ ** kwargs )
308313
309314 @close_db_connections
310315 @transition (field = state ,
@@ -327,10 +332,13 @@ def deploy(self, release):
327332 new_job_id = self ._job_id
328333 image = self .release .image
329334 c_type = self .type
335+ kwargs = {'memory' : self .release .limit .memory ,
336+ 'cpu' : self .release .limit .cpu }
330337 self ._scheduler .create (name = new_job_id ,
331338 image = image ,
332339 command = self ._command .format (** locals ()),
333- use_announcer = self ._command_announceable ())
340+ use_announcer = self ._command_announceable (),
341+ ** kwargs )
334342 self ._scheduler .start (new_job_id , self ._command_announceable ())
335343 # destroy old container
336344 self ._scheduler .destroy (old_job_id , self ._command_announceable ())
@@ -426,6 +434,27 @@ def __str__(self):
426434 return "{}-{}" .format (self .app .id , self .uuid [:7 ])
427435
428436
437+ @python_2_unicode_compatible
438+ class Limit (UuidAuditedModel ):
439+ """
440+ Set of resource limits applied by the scheduler
441+ during runtime execution of the Application.
442+ """
443+
444+ owner = models .ForeignKey (settings .AUTH_USER_MODEL )
445+ app = models .ForeignKey ('App' )
446+ memory = JSONField (default = '{}' , blank = True )
447+ cpu = JSONField (default = '{}' , blank = True )
448+
449+ class Meta :
450+ get_latest_by = 'created'
451+ ordering = ['-created' ]
452+ unique_together = (('app' , 'uuid' ),)
453+
454+ def __str__ (self ):
455+ return "{}-{}" .format (self .app .id , self .uuid [:7 ])
456+
457+
429458@python_2_unicode_compatible
430459class Release (UuidAuditedModel ):
431460 """
@@ -441,6 +470,7 @@ class Release(UuidAuditedModel):
441470
442471 config = models .ForeignKey ('Config' )
443472 build = models .ForeignKey ('Build' )
473+ limit = models .ForeignKey ('Limit' , null = True )
444474 # NOTE: image contains combined build + config, ready to run
445475 image = models .CharField (max_length = 256 , default = settings .DEFAULT_BUILD )
446476
@@ -452,7 +482,8 @@ class Meta:
452482 def __str__ (self ):
453483 return "{0}-v{1}" .format (self .app .id , self .version )
454484
455- def new (self , user , config = None , build = None , summary = None , source_version = 'latest' ):
485+ def new (self , user , config = None , build = None , limit = None ,
486+ summary = None , source_version = 'latest' ):
456487 """
457488 Create a new application release using the provided Build and Config
458489 on behalf of a user.
@@ -463,6 +494,8 @@ def new(self, user, config=None, build=None, summary=None, source_version='lates
463494 config = self .config
464495 if not build :
465496 build = self .build
497+ if not limit :
498+ limit = self .limit
466499 # always create a release off the latest image
467500 source_image = '{}:{}' .format (build .image , source_version )
468501 # construct fully-qualified target image
@@ -472,8 +505,8 @@ def new(self, user, config=None, build=None, summary=None, source_version='lates
472505 target_image = '{}' .format (self .app .id )
473506 # create new release and auto-increment version
474507 release = Release .objects .create (
475- owner = user , app = self .app , config = config ,
476- build = build , version = new_version , image = target_image , summary = summary )
508+ owner = user , app = self .app , config = config , build = build , limit = limit ,
509+ version = new_version , image = target_image , summary = summary )
477510 # IOW, this image did not come from the builder
478511 if not build .sha :
479512 # we assume that the image is not present on our registry,
@@ -507,12 +540,14 @@ def previous(self):
507540 prev_release = None
508541 return prev_release
509542
510- def save (self , * args , ** kwargs ):
543+ def save (self , * args , ** kwargs ): # noqa
511544 if not self .summary :
512545 self .summary = ''
513546 prev_release = self .previous ()
514547 # compare this build to the previous build
515548 old_build = prev_release .build if prev_release else None
549+ old_config = prev_release .config if prev_release else None
550+ old_limit = prev_release .limit if prev_release else None
516551 # if the build changed, log it and who pushed it
517552 if self .version == 1 :
518553 self .summary += "{} created initial release" .format (self .app .owner )
@@ -521,10 +556,8 @@ def save(self, *args, **kwargs):
521556 self .summary += "{} deployed {}" .format (self .build .owner , self .build .sha [:7 ])
522557 else :
523558 self .summary += "{} deployed {}" .format (self .build .owner , self .build .image )
524- # compare this config to the previous config
525- old_config = prev_release .config if prev_release else None
526559 # if the config data changed, log the dict diff
527- if self .config != old_config :
560+ elif self .config != old_config :
528561 dict1 = self .config .values
529562 dict2 = old_config .values if old_config else {}
530563 diff = dict_diff (dict1 , dict2 )
@@ -540,11 +573,25 @@ def save(self, *args, **kwargs):
540573 if self .summary :
541574 self .summary += ' and '
542575 self .summary += "{} {}" .format (self .config .owner , changes )
543- if not self .summary :
544- if self .version == 1 :
545- self .summary = "{} created the initial release" .format (self .owner )
546- else :
547- self .summary = "{} changed nothing" .format (self .owner )
576+ # if the limit changes, log the dict diff
577+ elif self .limit != old_limit :
578+ changes = []
579+ old_mem = old_limit .memory if old_limit else {}
580+ diff = dict_diff (self .limit .memory , old_mem )
581+ if diff .get ('added' ) or diff .get ('changed' ) or diff .get ('deleted' ):
582+ changes .append ('memory' )
583+ old_cpu = old_limit .cpu if old_limit else {}
584+ diff = dict_diff (self .limit .cpu , old_cpu )
585+ if diff .get ('added' ) or diff .get ('changed' ) or diff .get ('deleted' ):
586+ changes .append ('cpu' )
587+ if changes :
588+ changes = 'changed limits for ' + ', ' .join (changes )
589+ self .summary += "{} {}" .format (self .config .owner , changes )
590+ if not self .summary :
591+ if self .version == 1 :
592+ self .summary = "{} created the initial release" .format (self .owner )
593+ else :
594+ self .summary = "{} changed nothing" .format (self .owner )
548595 super (Release , self ).save (* args , ** kwargs )
549596
550597
0 commit comments