@ -0,0 +1,13 @@ | |||
FROM python:3.7.2-stretch | |||
MAINTAINER Noah Pederson noah@packetlostandfound.us | |||
EXPOSE 8080 | |||
COPY ./requirements.txt ./requirements.txt | |||
RUN pip install -r requirements.txt | |||
COPY . . | |||
ENTRYPOINT python app.py |
@ -0,0 +1,55 @@ | |||
#!/usr/bin/env python3 | |||
import connexion | |||
import datetime | |||
import logging | |||
import os | |||
from connexion import NoContent | |||
from nameko.standalone.rpc import ClusterRpcProxy | |||
# our memory-only pet storage | |||
PETS = {} | |||
config = { | |||
'AMQP_URI': os.environ["AMQP_URI"] #"pyamqp://guest:guest@localhost" | |||
} | |||
def get_pets(limit, animal_type=None): | |||
with ClusterRpcProxy(config) as cluster_rpc: | |||
pets = cluster_rpc.pet_service.get_pets(limit, animal_type=animal_type) | |||
return {"pets": pets} | |||
def get_pet(pet_id): | |||
with ClusterRpcProxy(config) as cluster_rpc: | |||
pet = cluster_rpc.pet_service.retrieve_pet(pet_id) | |||
return pet or ('Not found', 404) | |||
def put_pet(pet_id, pet): | |||
with ClusterRpcProxy(config) as cluster_rpc: | |||
exists = cluster_rpc.pet_service.put_pet(pet_id, pet) | |||
return NoContent, (200 if exists else 201) | |||
def delete_pet(pet_id): | |||
with ClusterRpcProxy(config) as cluster_rpc: | |||
exists = cluster_rpc.pet_service.delete_pet(pet_id) | |||
if exists: | |||
return NoContent, 204 | |||
else: | |||
return NoContent, 404 | |||
logging.basicConfig(level=logging.INFO) | |||
app = connexion.App(__name__) | |||
app.add_api('swagger.yaml') | |||
# set the WSGI application callable to allow using uWSGI: | |||
# uwsgi --http :8080 -w app | |||
application = app.app | |||
if __name__ == '__main__': | |||
app.run(port=8080) |
@ -0,0 +1,34 @@ | |||
amqp==2.4.1 | |||
certifi==2018.11.29 | |||
chardet==3.0.4 | |||
Click==7.0 | |||
clickclick==1.2.2 | |||
connexion==2.2.0 | |||
dnspython==1.16.0 | |||
eventlet==0.24.1 | |||
Flask==1.0.2 | |||
greenlet==0.4.15 | |||
idna==2.8 | |||
importlib-metadata==0.8 | |||
inflection==0.3.1 | |||
itsdangerous==1.1.0 | |||
Jinja2==2.10 | |||
jsonschema==2.6.0 | |||
kombu==4.3.0 | |||
MarkupSafe==1.1.1 | |||
mock==2.0.0 | |||
monotonic==1.5 | |||
nameko==2.11.0 | |||
openapi-spec-validator==0.2.6 | |||
path.py==11.5.0 | |||
pathlib==1.0.1 | |||
pbr==5.1.3 | |||
PyYAML==3.13 | |||
requests==2.21.0 | |||
six==1.12.0 | |||
swagger-ui-bundle==0.0.3 | |||
urllib3==1.24.1 | |||
vine==1.2.0 | |||
Werkzeug==0.14.1 | |||
wrapt==1.11.1 | |||
zipp==0.3.3 |
@ -0,0 +1,119 @@ | |||
swagger: '2.0' | |||
info: | |||
title: Pet Shop Example API | |||
version: "0.1" | |||
description: Simple example API to store and retrieve pets | |||
consumes: | |||
- application/json | |||
produces: | |||
- application/json | |||
paths: | |||
/pets: | |||
get: | |||
tags: [Pets] | |||
operationId: app.get_pets | |||
summary: Get all pets | |||
parameters: | |||
- name: animal_type | |||
in: query | |||
type: string | |||
pattern: "^[a-zA-Z0-9]*$" | |||
- name: limit | |||
in: query | |||
type: integer | |||
format: int32 | |||
minimum: 0 | |||
default: 100 | |||
responses: | |||
200: | |||
description: Return pets | |||
schema: | |||
type: object | |||
properties: | |||
pets: | |||
type: array | |||
items: | |||
$ref: '#/definitions/Pet' | |||
/pets/{pet_id}: | |||
get: | |||
tags: [Pets] | |||
operationId: app.get_pet | |||
summary: Get a single pet | |||
parameters: | |||
- $ref: '#/parameters/pet_id' | |||
responses: | |||
200: | |||
description: Return pet | |||
schema: | |||
$ref: '#/definitions/Pet' | |||
404: | |||
description: Pet does not exist | |||
put: | |||
tags: [Pets] | |||
operationId: app.put_pet | |||
summary: Create or update a pet | |||
parameters: | |||
- $ref: '#/parameters/pet_id' | |||
- name: pet | |||
in: body | |||
schema: | |||
$ref: '#/definitions/Pet' | |||
responses: | |||
200: | |||
description: Pet updated | |||
201: | |||
description: New pet created | |||
delete: | |||
tags: [Pets] | |||
operationId: app.delete_pet | |||
summary: Remove a pet | |||
parameters: | |||
- $ref: '#/parameters/pet_id' | |||
responses: | |||
204: | |||
description: Pet was deleted | |||
404: | |||
description: Pet does not exist | |||
parameters: | |||
pet_id: | |||
name: pet_id | |||
description: Pet's Unique identifier | |||
in: path | |||
type: string | |||
required: true | |||
pattern: "^[a-zA-Z0-9-]+$" | |||
definitions: | |||
Pet: | |||
type: object | |||
required: | |||
- name | |||
- animal_type | |||
properties: | |||
id: | |||
type: string | |||
description: Unique identifier | |||
example: "123" | |||
readOnly: true | |||
name: | |||
type: string | |||
description: Pet's name | |||
example: "Susie" | |||
minLength: 1 | |||
maxLength: 100 | |||
animal_type: | |||
type: string | |||
description: Kind of animal | |||
example: "cat" | |||
minLength: 1 | |||
tags: | |||
type: object | |||
description: Custom tags | |||
created: | |||
type: string | |||
format: date-time | |||
description: Creation time | |||
example: "2015-07-07T15:49:51.230+02:00" | |||
readOnly: true |
@ -0,0 +1,54 @@ | |||
--- | |||
version: "3.7" | |||
services: | |||
rabbitmq: | |||
image: "rabbitmq:3.7.12-management" | |||
expose: | |||
- 5672 | |||
- 15672 | |||
restart: always | |||
networks: | |||
- nameko | |||
- default | |||
connexion: | |||
build: | |||
dockerfile: Dockerfile | |||
context: ./connexion_example | |||
expose: | |||
- 8080 | |||
ports: | |||
- "8080:8080" | |||
environment: | |||
AMQP_URI: "pyamqp://guest:guest@rabbitmq" | |||
restart: always | |||
networks: | |||
- nameko | |||
depends_on: | |||
- rabbitmq | |||
pet_service: | |||
build: | |||
dockerfile: Dockerfile.pet_service | |||
context: ./nameko_example | |||
environment: | |||
AMQP_URI: "pyamqp://guest:guest@rabbitmq" | |||
restart: always | |||
networks: | |||
- nameko | |||
depends_on: | |||
- rabbitmq | |||
logger_service: | |||
build: | |||
dockerfile: Dockerfile.logger_service | |||
context: ./nameko_example | |||
environment: | |||
AMQP_URI: "pyamqp://guest:guest@rabbitmq" | |||
restart: always | |||
networks: | |||
- nameko | |||
depends_on: | |||
- rabbitmq | |||
networks: | |||
nameko: | |||
external: false |
@ -0,0 +1,12 @@ | |||
FROM python:3.7.2-stretch | |||
MAINTAINER Noah Pederson noah@packetlostandfound.us | |||
EXPOSE 8080 | |||
COPY ./requirements.txt ./requirements.txt | |||
RUN pip install -r requirements.txt | |||
COPY . . | |||
ENTRYPOINT nameko run --config config.yaml logger_service |
@ -0,0 +1,12 @@ | |||
FROM python:3.7.2-stretch | |||
MAINTAINER Noah Pederson noah@packetlostandfound.us | |||
EXPOSE 8080 | |||
COPY ./requirements.txt ./requirements.txt | |||
RUN pip install -r requirements.txt | |||
COPY . . | |||
ENTRYPOINT nameko run --config config.yaml pet_service |
@ -0,0 +1,14 @@ | |||
AMQP_URI: ${AMQP_URI} | |||
WEB_SERVER_ADDRESS: '0.0.0.0:8000' | |||
rpc_exchange: 'nameko-rpc' | |||
max_workers: 10 | |||
parent_calls_tracked: 10 | |||
LOGGING: | |||
version: 1 | |||
handlers: | |||
console: | |||
class: logging.StreamHandler | |||
root: | |||
level: DEBUG | |||
handlers: [console] |
@ -0,0 +1,10 @@ | |||
from nameko.events import event_handler, SINGLETON | |||
class LoggerService: | |||
name = "system_logger" | |||
@event_handler("pet_service", "log", handler_type=SINGLETON, reliable_delivery=True) | |||
def handle_pet_service_log(self, message): | |||
logging.info(message) | |||
print(message) |
@ -0,0 +1,70 @@ | |||
from nameko.rpc import rpc | |||
from nameko.events import EventDispatcher, event_handler, SINGLETON | |||
import datetime | |||
import logging | |||
pets = { | |||
"1": { | |||
"id": "1", | |||
"name": "Freya", | |||
"animal_type": "cat", | |||
"tags": None, | |||
"created": datetime.datetime.utcnow() | |||
}, | |||
"2": { | |||
"id": "2", | |||
"name": "Satyr", | |||
"animal_type": "cat", | |||
"tags": None, | |||
"created": datetime.datetime.utcnow() | |||
}, | |||
"3": { | |||
"id": "3", | |||
"name": "Bogie", | |||
"animal_type": "dog", | |||
"tags": None, | |||
"created": datetime.datetime.utcnow() | |||
} | |||
} | |||
class PetService: | |||
name = "pet_service" | |||
dispatch = EventDispatcher() | |||
@rpc | |||
def get_pets(self, limit, animal_type=None): | |||
self.dispatch("log", f"Recevied request to list all pets with anime_type {animal_type}") | |||
return [pet for pet in pets.values() if not animal_type or pet['animal_type'] == animal_type][:limit] | |||
@rpc | |||
def retrieve_pet(self, pet_id): | |||
self.dispatch("log", f"Received request to retrieve pet with id {pet_id}") | |||
if pet_id in pets: | |||
return pets[pet_id] | |||
else: | |||
msg = f"Pet with pet_id {pet_id} not found" | |||
self.dispatch("log", msg) | |||
# Explicitly raise the exception here so we can control the message | |||
raise KeyError(msg) | |||
@rpc | |||
def put_pet(self, pet_id, pet): | |||
self.dispatch("log", f"Inserting new pet with id {pet_id}") | |||
if pet_id in pets: | |||
self.dispatch("log", f"Updating pet with id {pet_id}") | |||
pets[pet_id].update(pet) | |||
return True | |||
else: | |||
self.dispatch("log", f"Creating pet with id {pet_id}") | |||
pets[pet_id] = pet | |||
return False | |||
@rpc | |||
def delete_pet(self, pet_id): | |||
self.dispatch("log", f"Deleting pet with id {pet_id}") | |||
exists = pet_id in pets | |||
del pets[pet_id] | |||
return exists |
@ -0,0 +1,22 @@ | |||
amqp==2.4.1 | |||
certifi==2018.11.29 | |||
chardet==3.0.4 | |||
dnspython==1.16.0 | |||
eventlet==0.24.1 | |||
greenlet==0.4.15 | |||
idna==2.8 | |||
importlib-metadata==0.8 | |||
kombu==4.3.0 | |||
mock==2.0.0 | |||
monotonic==1.5 | |||
nameko==2.11.0 | |||
path.py==11.5.0 | |||
pbr==5.1.3 | |||
PyYAML==3.13 | |||
requests==2.21.0 | |||
six==1.12.0 | |||
urllib3==1.24.1 | |||
vine==1.2.0 | |||
Werkzeug==0.14.1 | |||
wrapt==1.11.1 | |||
zipp==0.3.3 |