Tech Quickstart

This page gives a quick and partial introduction to CoWorks Technical Microservices. Follow Installation to install CoWorks and set up a new project.

CoWorks Technical Microservices are atomic microservices, meaning that they are single atomic components (i.e: singular blobs of code with a few inputs and outputs).

A tech microservice is simply defined by a single python class which looks like this:

class SimpleMicroService(TechMicroService):

        def get(self):
                return f"Simple microservice ready.\n"

Start

To create your first complete technical microservice, create a file hello.py in the tech folder with the following content:

from coworks import TechMicroService
from coworks import entry


class HelloMicroService(TechMicroService):

    @entry(no_auth=True)
    def get(self):
        return "Hello world.\n"


app = HelloMicroService()

This first example defines the very classical hello microservice app with a simple GET entry / (see Routing for more details on entry)

We set the attribute no_auth to True to allow access without authorization. This effectively disables the token authorizer. For security reason the default value is False (see Authorization for more details on authorizer).

We now can launch the run command defined by the Flask framework. So to test this microservice locally (see Flask for more details):

(project) $ cws --app hello run
 * Stage: dev
 * Serving Flask app 'hello'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit

To test this example, open another terminal window and enter:

(project) $ curl http://127.0.0.1:5000/
Hello world.

If you remove the argument no_auth=True from our @entry decorator, you should instead receive a 403 response.

First

To add more elements, complete your first try with the following content:

from coworks import TechMicroService
from coworks import entry


class SimpleMicroService(TechMicroService):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.value = 0

    def token_authorizer(self, token):
        return token == "token"

    @entry
    def get(self):
        return f"Stored value {self.value}.\n"

    @entry
    def post(self, value=None):
        if value is not None:
            self.value = value
        return f"Value stored ({value}).\n"


app = SimpleMicroService(name="sample-first-microservice")

We have added a dedicated function token_authorizer to define an authorizer (see Authorization for more details on authorizer). For this simple try, the authorizer validates the request only if a token is defined on header : Authorization key with token as value.

Then we have defined two entries on same path : GET and POST on root path. These enable reading and writing of our attribute value.

To test this example, open another terminal window and enter:

(project) $ curl -I http://127.0.0.1:5000/
HTTP/1.0 401 UNAUTHORIZED
...

(project) $ curl -H "Authorization:token" http://127.0.0.1:5000/
Stored value 0.

(project) $ curl -X POST -d '{"value":20}' -H "Content-Type: application/json" -H "Authorization:token" http://127.0.0.1:5000/
Value stored (20).

(project) $ curl -H "Authorization:token" http://127.0.0.1:5000/
Stored value 20.

Beware : the value is stored in memory just for this example, if the lambda is redeployed or another lambda instance is used the value stored is lost.

Complete

We can create and test a more complete case by leveraging blueprints and adding middlewares. We will also use StringIO to write our output to a string buffer.

For more information on how CoWorks uses blueprints, see TechMS Blueprints. For more information on how CoWorks uses WSGI middlewares, see Middlewares.

First, ensure that aws_xray_sdk is installed in your python environment:

$ pip install aws-xray-sdk

Then, enter the following content:

from aws_xray_sdk.core import xray_recorder

from coworks import TechMicroService
from coworks import entry
from coworks.blueprint.admin_blueprint import Admin
from coworks.blueprint.profiler_blueprint import Profiler
from coworks.extension.xray import XRay


class SimpleMicroService(TechMicroService):
    DOC_MD = """
#### Microservice Documentation
You can document your CoWorks MicroService using the class attributes `DOC_MD` (markdown) or
the instance attributes `doc_md` (markdown) which gets rendered from the '/' entry of the admin blueprint.
    """

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.register_blueprint(Admin(), url_prefix='/admin')
        self.register_blueprint(Profiler(self), url_prefix='/profile')
        self.value = 0

    @entry
    def get(self):
        return f"Stored value {self.value}.\n"

    @entry
    def post(self, value=None):
        if value is not None:
            self.value = value
        return f"Value stored ({value}).\n"


app = SimpleMicroService(name="sample-complete-microservice")

XRay(app, xray_recorder)

Note : aws_xray_sdk must be installed in your python environment or you will get an ImportError. If you receive this error, follow the step above to install.

By default the token value should be defined in the TOKEN environment variable ; the simpliest way to declare it is to create a dotenv file (.env) in the project folder with this token value defined in it:

TOKEN=mytoken

The Admin blueprint adds several routes but for the purposes of this example we’re interested in the root one (/admin as prefixed):

This endpoint gives documentation and all the routes of the microservice with the signature extracted from its associated function.

We have also a WSGI middleware ProfilerMiddleware to profile the last request:

    (project) $ curl -H "Authorization:mytoken" http://127.0.0.1:5000/profile
--------------------------------------------------------------------------------
PATH: '/profile'
         441 function calls (436 primitive calls) in 0.001 seconds

   Ordered by: internal time, call count

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 {method 'getvalue' of '_io.StringIO' objects}
        1    0.000    0.000    0.000    0.000 /home/gdo/.local/share/virtualenvs/samples-G9jKBMQA/lib/python3.10/site-packages/werkzeug/routing/map.py:246(bind_to_environ)
       11    0.000    0.000    0.000    0.000 /home/gdo/.local/share/virtualenvs/samples-G9jKBMQA/lib/python3.10/site-packages/werkzeug/local.py:308(__get__)
    ...

And at last we have a CoWorks middleware to add XRay traces (available only for deployed microservices).

Deploy

And now we can upload the sources files to AWS S3 and apply predefined terraform planifications (options may be defined in project file to avoid given then on command line see Configuration ):

(project) $ cws deploy --bucket XXX --profile-name YYY --layers arn:aws:lambda:eu-west-1:935392763270:layer:coworks-ZZZ
Terraform apply (Create API routes)
Terraform apply (Deploy API and Lambda for the dev stage)
terraform output :
classical_id = "xxxxxxxx"
(project) $

Notice: To get the available coworks layer versions, just call this public microservice (source code available in samples/layers):

curl -H 'Accept:application/json' https://2kb9hn4bs4.execute-api.eu-west-1.amazonaws.com/v1

Now we can test our first deployed microservice:

(project) $ curl -H "Authorization:mytoken" https://xxxxxxxx.execute-api.eu-west-1.amazonaws.com/dev
Stored value 0.

Notice: The deploy parameters can be defined once in the project configuration file (project.cws.yml)

Notice: You can set the debug option of Flask to get more information on the deploy process (FLASK_DEBUG=1 cws deploy)