Skip to content
This repository has been archived by the owner on Jun 6, 2019. It is now read-only.

Commit

Permalink
Init status service
Browse files Browse the repository at this point in the history
  • Loading branch information
mwisner committed Oct 25, 2017
1 parent f903d67 commit 1843e0e
Show file tree
Hide file tree
Showing 24 changed files with 375 additions and 54 deletions.
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
KONG_MANAGEMENT=http://localhost:8000/management
KONG_USERNAME=kong
KONG_PASSWORD=kong
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
KONG_MANAGEMENT=http://localhost:8000/management
KONG_USERNAME=kong
KONG_PASSWORD=kong
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,5 @@ target/

# pyenv python configuration file
.python-version
venv
.env
5 changes: 2 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@

language: python
python:
- 3.6
- 3.5
- 3.4
- 3.3
- 2.7
- 2.6

# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
install: pip install -U tox-travis
Expand All @@ -26,4 +25,4 @@ deploy:
on:
tags: true
repo: mwisner/python_kong_management
python: 2.7
python: 3.6
15 changes: 8 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ Features

* TODO

Credits
Testing
---------

This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template.

.. _Cookiecutter: https://github.com/audreyr/cookiecutter
.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage

```bash
cp .env.example .env
python3 -m venv venv
source venv
eval $(cat .env | sed 's/^/export /')
setup.py pytest
```
File renamed without changes.
57 changes: 57 additions & 0 deletions kong/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import requests
from .errors import KongError


class Client(object):

def __init__(self, base_url, username=None, password=None):

if base_url[-1:] != '/':
base_url = base_url + '/'

self.base_url = base_url
self.username = username
self.password = password

self.rate_limit_details = {}
self.http_session = requests.Session()

@property
def _auth(self):
if self.username and self.password:
return self.username, self.password
return None

@property
def status(self):
from .service import status
return status.Status(self)

def _execute_request(self, request, params):
result = request.execute(self.base_url, self._auth, params)
return result

def get(self, path, params={}):
from . import request
req = request.Request('GET', path, self.http_session)
return self._execute_request(req, params)

def post(self, path, params):
from . import request
req = request.Request('POST', path, self.http_session)
return self._execute_request(req, params)

def put(self, path, params):
from . import request
req = request.Request('PUT', path, self.http_session)
return self._execute_request(req, params)

def patch(self, path, params):
from . import request
req = request.Request('PATCH', path, self.http_session)
return self._execute_request(req, params)

def delete(self, path, params):
from . import request
req = request.Request('DELETE', path, self.http_session)
return self._execute_request(req, params)
68 changes: 68 additions & 0 deletions kong/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@

class KongError(Exception):

def __init__(self, message=None, context=None):
super(KongError, self).__init__(message)
self.message = message
self.context = context


class ArgumentError(ValueError, KongError):
pass


class HttpError(KongError):
pass


class ResourceNotFound(KongError):
pass


class AuthenticationError(KongError):
pass


class ServerError(KongError):
pass


class BadGatewayError(KongError):
pass


class ServiceUnavailableError(KongError):
pass


class BadRequestError(KongError):
pass


class RateLimitExceeded(KongError):
pass


class MultipleMatchingUsersError(KongError):
pass


class UnexpectedError(KongError):
pass


class TokenUnauthorizedError(KongError):
pass


error_codes = {
'unauthorized': AuthenticationError,
'forbidden': AuthenticationError,
'bad_request': BadRequestError,
'action_forbidden': BadRequestError,
'missing_parameter': BadRequestError,
'parameter_invalid': BadRequestError,
'parameter_not_found': BadRequestError,
'not_found': ResourceNotFound,
'service_unavailable': ServiceUnavailableError,
}
154 changes: 154 additions & 0 deletions kong/request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
from . import errors

import certifi
import json
import logging
import os
import requests

logger = logging.getLogger('kong.request')


def configure_timeout():
"""Configure the request timeout."""
timeout = os.getenv('KONG_REQUEST_TIMEOUT', '90')
try:
return int(timeout)
except ValueError:
logger.warning('%s is not a valid timeout value.', timeout)
return 90


class Request(object):

timeout = configure_timeout()

def __init__(self, http_method, path, http_session=None):
self.http_method = http_method
self.path = path
self.http_session = http_session

def execute(self, base_url, auth, params):
return self.send_request_to_path(base_url, auth, params)

def send_request_to_path(self, base_url, auth, params=None):
""" Construct an API request, send it to the API, and parse the
response. """
from . import __version__
req_params = {}

if auth:
req_params['auth'] = auth

# full URL
url = base_url + self.path

headers = {
'User-Agent': 'python-kong/' + __version__,
'AcceptEncoding': 'gzip, deflate',
'Accept': 'application/json',
}

if self.http_method in ('POST', 'PUT', 'DELETE'):
headers['content-type'] = 'application/json'
req_params['data'] = json.dumps(params, cls=ResourceEncoder)
elif self.http_method == 'GET':
req_params['params'] = params
req_params['headers'] = headers

# request logging
if logger.isEnabledFor(logging.DEBUG):
logger.debug("Sending %s request to: %s", self.http_method, url)
logger.debug(" headers: %s", headers)
if self.http_method == 'GET':
logger.debug(" params: %s", req_params['params'])
else:
logger.debug(" params: %s", req_params['data'])

if self.http_session is None:
resp = requests.request(
self.http_method, url, timeout=self.timeout,
verify=certifi.where(), **req_params)
else:
resp = self.http_session.request(
self.http_method, url, timeout=self.timeout, verify=certifi.where(), **req_params)

# response logging
if logger.isEnabledFor(logging.DEBUG):
logger.debug("Response received from %s", url)
logger.debug(" encoding=%s status:%s",
resp.encoding, resp.status_code)
logger.debug(" content:\n%s", resp.content)

parsed_body = self.parse_body(resp)
self.raise_errors_on_failure(resp)
return parsed_body

def parse_body(self, resp):
if resp.content and resp.content.strip():
try:
# use supplied or inferred encoding to decode the
# response content
decoded_body = resp.content.decode(
resp.encoding or resp.apparent_encoding)
body = json.loads(decoded_body)
#if body.get('type') == 'error.list':
# self.raise_application_errors_on_failure(body, resp.status_code) # noqa
return body
except ValueError:
self.raise_errors_on_failure(resp)

def raise_errors_on_failure(self, resp):
if resp.status_code == 404:
raise errors.ResourceNotFound('Resource Not Found')
elif resp.status_code == 401:
raise errors.AuthenticationError('Unauthorized')
elif resp.status_code == 403:
raise errors.AuthenticationError('Forbidden')
elif resp.status_code == 500:
raise errors.ServerError('Server Error')
elif resp.status_code == 502:
raise errors.BadGatewayError('Bad Gateway Error')
elif resp.status_code == 503:
raise errors.ServiceUnavailableError('Service Unavailable')

def raise_application_errors_on_failure(self, error_list_details, http_code): # noqa
# Currently, we don't support multiple errors
error_details = error_list_details['errors'][0]
error_code = error_details.get('type')
if error_code is None:
error_code = error_details.get('code')
error_context = {
'http_code': http_code,
'application_error_code': error_code
}
error_class = errors.error_codes.get(error_code)
if error_class is None:
# unexpected error
if error_code:
message = self.message_for_unexpected_error_with_type(
error_details, http_code)
else:
message = self.message_for_unexpected_error_without_type(
error_details, http_code)
error_class = errors.UnexpectedError
else:
message = error_details.get('message')
raise error_class(message, error_context)

def message_for_unexpected_error_with_type(self, error_details, http_code): # noqa
error_type = error_details.get('type')
message = error_details.get('message')
return "The error of type '%s' is not recognized. It occurred with the message: %s and http_code: '%s'. Please contact Kong with these details." % (error_type, message, http_code) # noqa

def message_for_unexpected_error_without_type(self, error_details, http_code): # noqa
message = error_details['message']
return "An unexpected error occured. It occurred with the message: %s and http_code: '%s'. Please contact Kong with these details." % (message, http_code) # noqa


class ResourceEncoder(json.JSONEncoder):
def default(self, o):
if hasattr(o, 'attributes'):
# handle API resources
return o.attributes
return super(ResourceEncoder, self).default(o)
Empty file added kong/service/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions kong/service/base_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

class BaseService(object):

def __init__(self, client):
self.client = client
12 changes: 12 additions & 0 deletions kong/service/status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from .base_service import BaseService
from kong.errors import KongError


class Status(BaseService):
def get(self):
"""
:return:
"""
response = self.client.get('status/')
return response
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
testpaths = tests
3 changes: 0 additions & 3 deletions python_kong_management/python_kong_management.py

This file was deleted.

3 changes: 2 additions & 1 deletion requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pip==8.1.2
pip==9.0.1
bumpversion==0.5.3
wheel==0.29.0
watchdog==0.8.3
Expand All @@ -10,3 +10,4 @@ cryptography==1.7
PyYAML==3.11
pytest==2.9.2
pytest-runner==2.11.1
requests==2.18.4
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ exclude = docs

[aliases]
test = pytest
# Define setup.py command aliases here
unit = pytest te

Loading

0 comments on commit 1843e0e

Please sign in to comment.