@@ -121,9 +121,16 @@ def __str__(self):
121121 def _get_job_id (self , container_type ):
122122 app = self .id
123123 release = self .release_set .latest ()
124+
125+ # see if there is a global or app specific setting to specify Deployments usage
126+ deployments = bool (release .config .values .get ('DEIS_KUBERNETES_DEPLOYMENTS' , settings .DEIS_KUBERNETES_DEPLOYMENTS )) # noqa
127+
128+ # deployments does not need version in the job
129+ if deployments :
130+ return "{app}-{container_type}" .format (** locals ())
131+
124132 version = "v{}" .format (release .version )
125- job_id = "{app}-{version}-{container_type}" .format (** locals ())
126- return job_id
133+ return "{app}-{version}-{container_type}" .format (** locals ())
127134
128135 def _get_command (self , container_type ):
129136 try :
@@ -237,35 +244,48 @@ def delete(self, *args, **kwargs):
237244
238245 def restart (self , ** kwargs ): # noqa
239246 """
240- Restart found pods by deleting them (RC will recreate).
241- Wait until they are all drained away and RC has gotten to a good state
247+ Restart found pods by deleting them (RC / Deployment will recreate).
248+ Wait until they are all drained away and RC / Deployment has gotten to a good state
242249 """
243250 try :
244- # Resolve single pod name if short form (worker-asdfg) is passed
245- if 'name' in kwargs and kwargs ['name' ].count ('-' ) == 1 :
246- if 'release' not in kwargs or kwargs ['release' ] is None :
247- release = self .release_set .latest ()
248- else :
249- release = self .release_set .get (version = kwargs ['release' ])
251+ if kwargs .get ('release' , None ) is None :
252+ release = self .release_set .latest ()
253+ else :
254+ release = self .release_set .get (version = kwargs ['release' ])
250255
251- version = "v{}" . format ( release . version )
252- kwargs [ 'name' ] = '{}-{}-{}' . format ( kwargs [ 'id' ], version , kwargs [ 'name' ])
256+ # see if there is a global or app specific setting to specify Deployments usage
257+ deployments = bool ( release . config . values . get ( 'DEIS_KUBERNETES_DEPLOYMENTS' , settings . DEIS_KUBERNETES_DEPLOYMENTS )) # noqa
253258
254- # Iterate over RCs to get total desired count if not a single item
259+ if deployments :
260+ # Resolve single pod name if short form (cmd-1269180282-1nyfz) is passed
261+ if 'name' in kwargs and kwargs ['name' ].count ('-' ) == 2 :
262+ kwargs ['name' ] = '{}-{}' .format (kwargs ['id' ], kwargs ['name' ])
263+ else :
264+ # Resolve single pod name if short form (worker-asdfg) is passed
265+ if 'name' in kwargs and kwargs ['name' ].count ('-' ) == 1 :
266+ version = "v{}" .format (release .version )
267+ kwargs ['name' ] = '{}-{}-{}' .format (kwargs ['id' ], version , kwargs ['name' ])
268+
269+ # Iterate over RCs / RSs to get total desired count if not a single item
255270 desired = 1
256271 if 'name' not in kwargs :
257272 desired = 0
258273 labels = self ._scheduler_filter (** kwargs )
259- controllers = self ._scheduler .get_rcs (kwargs ['id' ], labels = labels ).json ()['items' ]
260- for controller in controllers :
274+ # fetch RS (which represent Deployments) / RCs
275+ if deployments :
276+ controllers = self ._scheduler .get_replicasets (kwargs ['id' ], labels = labels )
277+ else :
278+ controllers = self ._scheduler .get_rcs (kwargs ['id' ], labels = labels )
279+
280+ for controller in controllers .json ()['items' ]:
261281 desired += controller ['spec' ]['replicas' ]
262282 except KubeException :
263283 # Nothing was found
264284 return []
265285
266286 try :
267287 for pod in self .list_pods (** kwargs ):
268- # This function verifies the delete. Gives pod 30 seconds
288+ # This function verifies the delete
269289 self ._scheduler .delete_pod (self .id , pod ['name' ])
270290 except Exception as e :
271291 err = "warning, some pods failed to stop:\n {}" .format (str (e ))
@@ -380,6 +400,13 @@ def scale(self, user, structure): # noqa
380400 def _scale_pods (self , scale_types ):
381401 release = self .release_set .latest ()
382402 envs = release .config .values
403+
404+ # see if the app config has deploy batch preference, otherwise use global
405+ batches = release .config .values .get ('DEIS_DEPLOY_BATCHES' , settings .DEIS_DEPLOY_BATCHES )
406+
407+ # see if there is a global or app specific setting to specify Deployments usage
408+ deployments = bool (envs .get ('DEIS_KUBERNETES_DEPLOYMENTS' , settings .DEIS_KUBERNETES_DEPLOYMENTS )) # noqa
409+
383410 for scale_type , replicas in scale_types .items ():
384411 # only web / cmd are routable
385412 # http://docs.deis.io/en/latest/using_deis/process-types/#web-vs-cmd-process-types
@@ -400,7 +427,10 @@ def _scale_pods(self, scale_types):
400427 'app_type' : scale_type ,
401428 'build_type' : release .build .type ,
402429 'healthcheck' : release .config .healthcheck ,
403- 'routable' : routable
430+ 'routable' : routable ,
431+ 'deployments' : deployments ,
432+ 'deploy_batches' : batches ,
433+ 'deploy_timeout' : 120 , # 2 minutes
404434 }
405435
406436 command = self ._get_command (scale_type )
@@ -417,8 +447,12 @@ def _scale_pods(self, scale_types):
417447 self .log (err , logging .ERROR )
418448 raise ServiceUnavailable (err ) from e
419449
420- def deploy (self , release ):
421- """Deploy a new release to this application"""
450+ def deploy (self , release , force_deploy = False ):
451+ """
452+ Deploy a new release to this application
453+
454+ force_deploy can be used when a deployment is broken, such as for Rollback
455+ """
422456 if release .build is None :
423457 raise DeisException ('No build associated with this release' )
424458
@@ -432,6 +466,11 @@ def deploy(self, release):
432466 # see if the app config has deploy batch preference, otherwise use global
433467 batches = release .config .values .get ('DEIS_DEPLOY_BATCHES' , settings .DEIS_DEPLOY_BATCHES )
434468
469+ # see if there is a global or app specific setting to specify Deployments usage
470+ deployments = bool (release .config .values .get ('DEIS_KUBERNETES_DEPLOYMENTS' , settings .DEIS_KUBERNETES_DEPLOYMENTS )) # noqa
471+
472+ deployment_history = release .config .values .get ('KUBERNETES_DEPLOYMENTS_REVISION_HISTORY_LIMIT' , settings .KUBERNETES_DEPLOYMENTS_REVISION_HISTORY_LIMIT ) # noqa
473+
435474 # deploy application to k8s. Also handles initial scaling
436475 deploys = {}
437476 envs = release .config .values
@@ -457,14 +496,23 @@ def deploy(self, release):
457496 'build_type' : release .build .type ,
458497 'healthcheck' : release .config .healthcheck ,
459498 'routable' : routable ,
460- 'deploy_batches' : batches
499+ 'deploy_batches' : batches ,
500+ 'deploy_timeout' : 120 , # 2 minutes
501+ 'deployment_history_limit' : deployment_history ,
502+ 'deployments' : deployments ,
503+ 'release_summary' : release .summary
461504 }
462505
463506 # Sort deploys so routable comes first
464507 deploys = OrderedDict (sorted (deploys .items (), key = lambda d : d [1 ].get ('routable' )))
465508
466509 for scale_type , kwargs in deploys .items ():
467510 try :
511+ # Is there an existing deployment in progress?
512+ name = self ._get_job_id (scale_type )
513+ if not force_deploy and release .deployment_in_progress (self .id , name ):
514+ raise AlreadyExists ('Deployment for {} is already in progress' .format (name ))
515+
468516 self ._scheduler .deploy (
469517 namespace = self .id ,
470518 name = self ._get_job_id (scale_type ),
@@ -484,8 +532,8 @@ def deploy(self, release):
484532 self .log (err , logging .ERROR )
485533 raise ServiceUnavailable (err ) from e
486534
487- # cleanup old releases from kubernetes
488- release .cleanup_old ()
535+ # cleanup old release objects from kubernetes
536+ release .cleanup_old (deployments )
489537
490538 def _default_structure (self , release ):
491539 """Scale to default structure based on release type"""
@@ -668,8 +716,9 @@ def list_pods(self, *args, **kwargs):
668716
669717 data = []
670718 for p in pods :
719+ labels = p ['metadata' ]['labels' ]
671720 # specifically ignore run pods
672- if p [ 'metadata' ][ ' labels' ] ['type' ] == 'run' :
721+ if labels ['type' ] == 'run' :
673722 continue
674723
675724 state = str (self ._scheduler .pod_state (p ))
@@ -685,8 +734,8 @@ def list_pods(self, *args, **kwargs):
685734 item = Pod ()
686735 item ['name' ] = p ['metadata' ]['name' ]
687736 item ['state' ] = state
688- item ['release' ] = p [ 'metadata' ][ ' labels' ] ['version' ]
689- item ['type' ] = p [ 'metadata' ][ ' labels' ] ['type' ]
737+ item ['release' ] = labels ['version' ]
738+ item ['type' ] = labels ['type' ]
690739 if 'startTime' in p ['status' ]:
691740 started = p ['status' ]['startTime' ]
692741 else :
@@ -697,7 +746,6 @@ def list_pods(self, *args, **kwargs):
697746
698747 # sorting so latest start date is first
699748 data .sort (key = lambda x : x ['started' ], reverse = True )
700-
701749 return data
702750 except KubeHTTPException as e :
703751 pass
@@ -707,7 +755,7 @@ def list_pods(self, *args, **kwargs):
707755 raise ServiceUnavailable (err ) from e
708756
709757 def _scheduler_filter (self , ** kwargs ):
710- labels = {'app' : self .id }
758+ labels = {'app' : self .id , 'heritage' : 'deis' }
711759
712760 # always supply a version, either latest or a specific one
713761 if 'release' not in kwargs or kwargs ['release' ] is None :
0 commit comments