Skip to content
This repository has been archived by the owner on Dec 4, 2024. It is now read-only.

Commit

Permalink
Added Custom Exception handling with exit status for various failure …
Browse files Browse the repository at this point in the history
…scenarios (#258)

* Added Exception handling with Custom exceptions and exit status for various failure scenarios

* added additional new line at the beginning for pep8

* Updated print traceback to format traceback to pass the exception traceback to variable

* fixed a typo for querying
  • Loading branch information
ajays20078 authored and brndnmtthws committed Jul 13, 2016
1 parent 8508975 commit 693ce4f
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 32 deletions.
3 changes: 2 additions & 1 deletion tests/test_zdd.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import zdd
import mock
import json
from zdd_exceptions import *


class Arguments:
Expand Down Expand Up @@ -332,4 +333,4 @@ def test_complete_cur_exception(self):

args = Arguments()
args.complete_cur = True
self.assertRaises(Exception, zdd.do_zdd, args)
self.assertRaises(InvalidArgException, zdd.do_zdd, args)
106 changes: 75 additions & 31 deletions zdd.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import sys
import subprocess
from utils import *
from zdd_exceptions import *
import traceback


logger = logging.getLogger('zdd')
Expand Down Expand Up @@ -57,8 +59,12 @@ def query_yes_no(question, default="yes"):

def marathon_get_request(args, path):
url = args.marathon + path
response = requests.get(url, auth=get_marathon_auth_params(args))
response.raise_for_status()
try:
response = requests.get(url, auth=get_marathon_auth_params(args))
response.raise_for_status()
except requests.exceptions.RequestException:
raise MarathonEndpointException(
"Error while querying marathon", url, traceback.format_exc())
return response


Expand Down Expand Up @@ -273,7 +279,13 @@ def max_wait_not_exceeded(max_wait, timestamp):
def find_tasks_to_kill(args, new_app, old_app, timestamp):
marathon_lb_urls = get_marathon_lb_urls(args)
haproxy_count = len(marathon_lb_urls)
listeners = fetch_app_listeners(new_app, marathon_lb_urls)
try:
listeners = fetch_app_listeners(new_app, marathon_lb_urls)
except requests.exceptions.RequestException:
raise MarathonLbEndpointException(
"Error while querying Marathon-LB",
marathon_lb_urls,
traceback.format_exc())
while max_wait_not_exceeded(args.max_wait, timestamp):
time.sleep(args.step_delay)

Expand Down Expand Up @@ -411,41 +423,57 @@ def safe_delete_app(args, app, new_app):

def delete_marathon_app(args, app):
url = args.marathon + '/v2/apps' + app['id']
response = requests.delete(url,
auth=get_marathon_auth_params(args))
response.raise_for_status()
try:
response = requests.delete(url,
auth=get_marathon_auth_params(args))
response.raise_for_status()
except requests.exceptions.RequestException:
raise AppDeleteException(
"Error while deleting the app", url, traceback.format_exc())
return response


def kill_marathon_tasks(args, ids):
data = json.dumps({'ids': ids})
url = args.marathon + "/v2/tasks/delete?scale=true"
headers = {'Content-Type': 'application/json'}
response = requests.post(url, headers=headers, data=data,
auth=get_marathon_auth_params(args))
response.raise_for_status()
try:
response = requests.post(url, headers=headers, data=data,
auth=get_marathon_auth_params(args))
response.raise_for_status()
except requests.exceptions.RequestException:
# This is App Scale Down, so raising AppScale Exception
raise AppScaleException(
"Error while scaling the app", url, data, traceback.format_exc())
return response


def scale_marathon_app_instances(args, app, instances):
url = args.marathon + "/v2/apps" + app['id']
data = json.dumps({'instances': instances})
headers = {'Content-Type': 'application/json'}

response = requests.put(url, headers=headers, data=data,
auth=get_marathon_auth_params(args))

response.raise_for_status()
try:
response = requests.put(url, headers=headers, data=data,
auth=get_marathon_auth_params(args))
response.raise_for_status()
except requests.exceptions.RequestException:
# This is App Scale Up, so raising AppScale Exception
raise AppScaleException(
"Error while scaling the app", url, data, traceback.format_exc())
return response


def deploy_marathon_app(args, app):
url = args.marathon + "/v2/apps"
data = json.dumps(app)
headers = {'Content-Type': 'application/json'}
response = requests.post(url, headers=headers, data=data,
auth=get_marathon_auth_params(args))
response.raise_for_status()
try:
response = requests.post(url, headers=headers, data=data,
auth=get_marathon_auth_params(args))
response.raise_for_status()
except requests.exceptions.RequestException:
raise AppCreateException(
"Error while creating the app", url, data, traceback.format_exc())
return response


Expand All @@ -469,16 +497,20 @@ def set_service_port(app, servicePort):

def validate_app(app):
if app['id'] is None:
raise Exception("App doesn't contain a valid App ID")
raise MissingFieldException("App doesn't contain a valid App ID",
'id')
if 'labels' not in app:
raise Exception("No labels found. Please define the"
"HAPROXY_DEPLOYMENT_GROUP label")
raise MissingFieldException("No labels found. Please define the"
" HAPROXY_DEPLOYMENT_GROUP label",
'label')
if 'HAPROXY_DEPLOYMENT_GROUP' not in app['labels']:
raise Exception("Please define the "
"HAPROXY_DEPLOYMENT_GROUP label")
raise MissingFieldException("Please define the "
"HAPROXY_DEPLOYMENT_GROUP label",
'HAPROXY_DEPLOYMENT_GROUP')
if 'HAPROXY_DEPLOYMENT_ALT_PORT' not in app['labels']:
raise Exception("Please define the "
"HAPROXY_DEPLOYMENT_ALT_PORT label")
raise MissingFieldException("Please define the "
"HAPROXY_DEPLOYMENT_ALT_PORT label",
'HAPROXY_DEPLOYMENT_ALT_PORT')


def set_app_ids(app, colour):
Expand Down Expand Up @@ -621,12 +653,12 @@ def do_zdd(args, out=sys.stdout):
previous_deploys = fetch_previous_deploys(args, app)

if len(previous_deploys) > 1:
# There is a stuck deploy
# There is a stuck deploy or hybrid deploy
return safe_resume_deploy(args, previous_deploys)

if args.complete_cur or args.complete_prev:
raise Exception("Cannot use --complete-cur, --complete-prev"
" flags when config is not hybrid")
raise InvalidArgException("Cannot use --complete-cur, --complete-prev"
" flags when config is not hybrid")

new_app = prepare_deploy(args, previous_deploys, app)

Expand Down Expand Up @@ -756,7 +788,19 @@ def process_arguments():
set_request_retries()
setup_logging(logger, args.syslog_socket, args.log_format, args.log_level)

if do_zdd(args):
sys.exit(0)
else:
sys.exit(1)
try:
if do_zdd(args):
sys.exit(0)
else:
sys.exit(1)
except Exception as e:
if hasattr(e, 'zdd_exit_status'):
if hasattr(e, 'error'):
logger.exception(str(e.error))
else:
logger.exception(traceback.print_exc())
sys.exit(e.zdd_exit_status)
else:
# For Unknown Exceptions
logger.exception(traceback.print_exc())
sys.exit(2)
81 changes: 81 additions & 0 deletions zdd_exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
""" Exit Status 1 is already used in the script.
Zdd returns with exit status 1 when app is not force
deleted either through argument or through prompt.
Exit Status 2 is used for Unknown Exceptions.
"""


class InvalidArgException(Exception):
""" This exception indicates invalid combination of arguments
passed to zdd"""
def __init__(self, msg):
super(InvalidArgException, self).__init__(msg)
self.error = msg
self.zdd_exit_status = 3


class MissingFieldException(Exception):
""" This exception indicates required fields which are missing
in JSON payload passed to zdd"""
def __init__(self, msg, field):
super(MissingFieldException, self).__init__(msg)
self.error = msg
self.missing_field = field
self.zdd_exit_status = 4


class MarathonLbEndpointException(Exception):
""" This excaption indicates issue with one of the marathonlb
endpoints specified as argument to Zdd"""
def __init__(self, msg, url, error):
super(MarathonLbEndpointException, self).__init__(msg)
self.msg = msg
self.url = url
self.error = error
self.zdd_exit_status = 5


class MarathonEndpointException(Exception):
""" This excaption indicates issue with marathon endpoint
specified as argument to Zdd"""
def __init__(self, msg, url, error):
super(MarathonEndpointException, self).__init__(msg)
self.msg = msg
self.url = url
self.error = error
self.zdd_exit_status = 6


class AppCreateException(Exception):
""" This exception indicates there was a error while creating the
new App and hence it was not created."""
def __init__(self, msg, url, payload, error):
super(AppCreateException, self).__init__(msg)
self.msg = msg
self.error = error
self.url = url
self.payload = payload
self.zdd_exit_status = 7


class AppDeleteException(Exception):
""" This exception indicates there was a error while deleting the
old App and hence it was not deleted """
def __init__(self, msg, url, appid, error):
super(AppDeleteException, self).__init__(msg)
self.msg = msg
self.error = error
self.url = url
self.zdd_exit_status = 8


class AppScaleException(Exception):
""" This exception indicated there was a error while either scaling up
new app or while scaling down old app"""
def __init__(self, msg, url, payload, error):
super(AppScaleException, self).__init__(msg)
self.msg = msg
self.error = error
self.url = url
self.payload = payload
self.zdd_exit_status = 9

0 comments on commit 693ce4f

Please sign in to comment.