1111
1212from django .conf import settings
1313from django .db import models
14- from django . core . exceptions import ValidationError
14+ from rest_framework . exceptions import ValidationError , NotFound
1515from jsonfield import JSONField
1616
1717from deis import __version__ as deis_version
18- from api .models import UuidAuditedModel , log_event , AlreadyExists
18+ from api .models import UuidAuditedModel , log_event , AlreadyExists , \
19+ DeisException , ServiceUnavailable
1920
2021from api .utils import generate_app_name , app_build_type
2122from api .models .release import Release
@@ -43,7 +44,7 @@ def validate_app_structure(value):
4344 if any (int (v ) < 0 for v in value .values ()):
4445 raise ValueError ("Must be greater than or equal to zero" )
4546 except ValueError as err :
46- raise ValidationError (err )
47+ raise ValidationError (str ( err ) )
4748
4849
4950def validate_reserved_names (value ):
@@ -152,7 +153,10 @@ def create(self, *args, **kwargs):
152153 )
153154
154155 # create required minimum resources in k8s for the application
155- self ._scheduler .create (self .id )
156+ try :
157+ self ._scheduler .create (self .id )
158+ except KubeException as e :
159+ raise ServiceUnavailable (str (e )) from e
156160
157161 # Attach the platform specific application sub domain to the k8s service
158162 # Only attach it on first release in case a customer has remove the app domain
@@ -164,8 +168,8 @@ def delete(self, *args, **kwargs):
164168 try :
165169 # attempt to remove application from kubernetes
166170 self ._scheduler .destroy (self .id )
167- except KubeException :
168- pass
171+ except KubeException as e :
172+ raise ServiceUnavailable ( str ( e )) from e
169173
170174 self ._clean_app_logs ()
171175 return super (App , self ).delete (* args , ** kwargs )
@@ -213,7 +217,7 @@ def restart(self, **kwargs): # noqa
213217 while True :
214218 # timed out
215219 if elapsed >= timeout :
216- raise KubeException ('timeout - 5 minutes have passed and pods are not up' )
220+ raise DeisException ('timeout - 5 minutes have passed and pods are not up' )
217221
218222 # restarting a single pod behaves differently, fetch the *newest* pod
219223 # and hope it is the right one. Comes back sorted
@@ -234,7 +238,6 @@ def restart(self, **kwargs): # noqa
234238
235239 elapsed += 5
236240 time .sleep (5 )
237-
238241 except Exception as e :
239242 err = "warning, some pods failed to start:\n {}" .format (str (e ))
240243 log_event (self , err , logging .WARNING )
@@ -261,18 +264,26 @@ def scale(self, user, structure): # noqa
261264 self .create ()
262265
263266 if self .release_set .latest ().build is None :
264- raise EnvironmentError ('No build associated with this release' )
267+ raise DeisException ('No build associated with this release' )
265268
266269 release = self .release_set .latest ()
267270
271+ # Validate structure
272+ try :
273+ for target , count in structure .copy ().items ():
274+ structure [target ] = int (count )
275+ validate_app_structure (structure )
276+ except (TypeError , ValueError ) as e :
277+ raise DeisException ('Invalid scaling format: {}' .format (e ))
278+
268279 # test for available process types
269280 available_process_types = release .build .procfile or {}
270281 for container_type in structure :
271282 if container_type == 'cmd' :
272283 continue # allow docker cmd types in case we don't have the image source
273284
274285 if container_type not in available_process_types :
275- raise EnvironmentError (
286+ raise DeisException (
276287 'Container type {} does not exist in application' .format (container_type ))
277288
278289 # merge current structure and the new items together
@@ -323,16 +334,15 @@ def _scale_pods(self, scale_types):
323334 command = command ,
324335 ** kwargs
325336 )
326-
327337 except Exception as e :
328338 err = '{} (scale): {}' .format (self ._get_job_id (scale_type ), e )
329339 log_event (self , err , logging .ERROR )
330- raise
340+ raise ServiceUnavailable ( e ) from e
331341
332342 def deploy (self , release ):
333343 """Deploy a new release to this application"""
334344 if release .build is None :
335- raise EnvironmentError ('No build associated with this release' )
345+ raise DeisException ('No build associated with this release' )
336346
337347 # use create to make sure minimum resources are created
338348 self .create ()
@@ -386,7 +396,7 @@ def deploy(self, release):
386396 except Exception as e :
387397 err = '{} (app::deploy): {}' .format (self ._get_job_id (scale_type ), e )
388398 log_event (self , err , logging .ERROR )
389- raise
399+ raise ServiceUnavailable ( err ) from e
390400
391401 # cleanup old releases from kubernetes
392402 release .cleanup_old ()
@@ -508,19 +518,20 @@ def logs(self, log_lines=str(settings.LOG_LINES)):
508518 r = requests .get (url )
509519 # Handle HTTP request errors
510520 except requests .exceptions .RequestException as e :
511- logger .error ("Error accessing deis-logger using url '{}': {}" .format (url , e ))
512- raise e
521+ msg = "Error accessing deis-logger using url '{}': {}" .format (url , e )
522+ logger .error (msg )
523+ raise ServiceUnavailable (msg ) from e
513524
514525 # Handle logs empty or not found
515526 if r .status_code == 204 or r .status_code == 404 :
516527 logger .info ("GET {} returned a {} status code" .format (url , r .status_code ))
517- raise EnvironmentError ('Could not locate logs' )
528+ raise NotFound ('Could not locate logs' )
518529
519530 # Handle unanticipated status codes
520531 if r .status_code != 200 :
521532 logger .error ("Error accessing deis-logger: GET {} returned a {} status code"
522533 .format (url , r .status_code ))
523- raise EnvironmentError ('Error accessing deis-logger' )
534+ raise ServiceUnavailable ('Error accessing deis-logger' )
524535
525536 # cast content to string since it comes as bytes via the requests object
526537 return str (r .content )
@@ -532,7 +543,7 @@ def pod_name(size=5, chars=string.ascii_lowercase + string.digits):
532543 """Run a one-off command in an ephemeral app container."""
533544 release = self .release_set .latest ()
534545 if release .build is None :
535- raise EnvironmentError ('No build associated with this release to run this command' )
546+ raise DeisException ('No build associated with this release to run this command' )
536547
537548 # TODO: add support for interactive shell
538549 # SECURITY: shell-escape user input
@@ -576,7 +587,7 @@ def pod_name(size=5, chars=string.ascii_lowercase + string.digits):
576587 except Exception as e :
577588 err = '{} (run): {}' .format (name , e )
578589 log_event (self , err , logging .ERROR )
579- raise
590+ raise ServiceUnavailable ( str ( e )) from e
580591
581592 def list_pods (self , * args , ** kwargs ):
582593 """Used to list basic information about pods running for a given application"""
@@ -627,7 +638,7 @@ def list_pods(self, *args, **kwargs):
627638 except Exception as e :
628639 err = '(list pods): {}' .format (e )
629640 log_event (self , err , logging .ERROR )
630- raise
641+ raise ServiceUnavailable ( err ) from e
631642
632643 def _scheduler_filter (self , ** kwargs ):
633644 labels = {'app' : self .id }
0 commit comments