This document is a hands-on guide to turning your existing cloud service into a
tsuru service.

In order to create a service you need to implement a provisioning API for your
service, which tsuru will call using HTTP protocol
when a customer creates a new instance or binds a service instance with an app.

You will also need to create a YAML document that will serve as the service
manifest. We provide a command-line tool to help you to create this manifest
and manage your service.

For new instances tsuru sends a POST to /resources with the parameters needed
for creating an instance. If the service instance is successfully created, your
API should return 201 in status code.

Let’s create the view for this action:

fromflaskimportrequest@app.route("/resources",methods=["POST"])defadd_instance():name=request.form.get("name")plan=request.form.get("plan")team=request.form.get("team")# use the given parameters to create the instancereturn"",201

When a service instance is updated, tsuru sends a PUT to /resources with the updated
parameters for the instance. If the service instance is successfully updated, your
API should return 200 in status code.

This endpoint is optional. That means you could leave it unimplemented and return a
404 status code, and tsuru would simply ignore it.

Here’s an example implementation for this endpoint:

fromflaskimportrequest@app.route("/resources",methods=["POST"])defadd_instance():@app.route("/resources/<name>",methods=["PUT"])defupdate_instance(name):name=request.form.get("name")description=request.form.get("description")tags=request.form.get("tag")team=request.form.get("team")plan=request.form.get("plan")# use the given parameters to update the instance "name"return"",200

In the bind action, tsuru calls your service via POST on
/resources/<service-instance-name>/bind-app with the parameters needed for
binding an app into a service instance.

If the bind operation succeeds, the API should return 201 as status code with
the variables to be exported in the app environment on body in JSON format.

As an example, let’s create a view that returns a json with a fake variable
called “SOMEVAR” to be injected in the app environment:

importjsonfromflaskimportrequest@app.route("/resources/<name>/bind-app",methods=["POST"])defbind_app(name):app_host=request.form.get("app-host")# use name and app_host to bind the service instance and the #applicationenvs={"SOMEVAR":"somevalue"}returnjson.dumps(envs),201

When binding and unbindin application and service instances, tsuru will also
provide information about units that will have access to the service instance,
so the service API can handle any required whitelisting (writing ACL rules to a
network switch or authorizing access in a firewall, for example).

tsuru will send POST and DELETE requests to the route
/resources/<name>/bind, with the host of the app and the unit, so any
access control can be handled by the API:

@app.route("/resources/<name>/bind",methods=["POST","DELETE"])defaccess_control(name):app_host=request.form.get("app-host")unit_host=request.form.get("unit-host")# use unit-host and app-host, according to the access control tool, and# the request method.return"",201

To check the status of an instance, tsuru issues a GET request to the URL
/resources/<service_name>/status. If the instance is ok, this URL should
return 204.

Let’s create a view for this action:

@app.route("/resources/<name>/status",methods=["GET"])defstatus(name):# check the status of the instance named "name"return"",204

The final code for our “fake API” developed in Flask is:

importjsonfromflaskimportFlask,requestapp=Flask(__name__)@app.route("/resources/plans",methods=["GET"])defplans():plans=[{"name":"small","description":"small instance"},{"name":"medium","description":"medium instance"},{"name":"big","description":"big instance"},{"name":"giant","description":"giant instance"}]returnjson.dumps(plans)@app.route("/resources",methods=["POST"])defadd_instance():name=request.form.get("name")plan=request.form.get("plan")team=request.form.get("team")# use the given parameters to create the instancereturn"",201@app.route("/resources/<name>/bind-app",methods=["POST"])defbind_app(name):app_host=request.form.get("app-host")# use name and app_host to bind the service instance and the #applicationenvs={"SOMEVAR":"somevalue"}returnjson.dumps(envs),201@app.route("/resources/<name>/bind-app",methods=["DELETE"])defunbind_app(name):app_host=request.form.get("app-host")# use name and app-host to remove the bindreturn"",200@app.route("/resources/<name>",methods=["DELETE"])defremove_instance(name):# remove the instance named "name"return"",200@app.route("/resources/<name>/bind",methods=["POST","DELETE"])defaccess_control(name):app_host=request.form.get("app-host")unit_host=request.form.get("unit-host")# use unit-host and app-host, according to the access control tool, and# the request method.return"",201@app.route("/resources/<name>/status",methods=["GET"])defstatus(name):# check the status of the instance named "name"return"",204if__name__=="__main__":app.run()