Building your service

Overview

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

In order to create a service you need to implement a provisioning API for yourservice, which tsuru will call using HTTP protocolwhen 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 servicemanifest. We provide a command-line tool to help you to create this manifestand manage your service.

Creating your service API

To create your service API, you can use any programming language or framework.In this tutorial we will use Flask.

Authentication

tsuru uses basic authentication for authenticating the services, for moredetails, check the service API workflow.

Using Flask, you can manage basic authentication using a decorator described inthis Flask snippet: http://flask.pocoo.org/snippets/8/.

Prerequisites

First, let’s ensure that Python and pip are already installed:

  1. $ python --version
  2. Python 2.7.2
  3.  
  4. $ pip
  5. Usage: pip COMMAND [OPTIONS]
  6.  
  7. pip: error: You must give a command (use "pip help" to see a list of commands)

For more information about how to install python you can see the Pythondownload documentation and about how to installpip you can see the pip installation instructions.

Now, with python and pip installed, you can use pip to install Flask:

  1. $ pip install flask

Now that Flask is installed, it’s time to create a file called api.py and addthe code needed to create a minimal Flask application:

  1. from flask import Flask
  2. app = Flask(__name__)
  3.  
  4. @app.route("/")
  5. def hello():
  6. return "Hello World!"
  7.  
  8. if __name__ == "__main__":
  9. app.run()

For run this app you can do:

  1. $ python api.py
  2. * Running on http://127.0.0.1:5000/

If you open your web browser and access the url http://127.0.0.1:5000/ you willsee the message “Hello World!”.

Then, you need to implement the resources of a tsuru service API, as describedin the tsuru service API workflow.

Listing available plans

tsuru will get the list of available plans by issuing a GET request in the/resources/plans URL. Let’s create the view that will handle this kindof request:

  1. import json
  2.  
  3.  
  4. @app.route("/resources/plans", methods=["GET"])
  5. def plans():
  6. plans = [{"name": "small", "description": "small instance"},
  7. {"name": "medium", "description": "medium instance"},
  8. {"name": "big", "description": "big instance"},
  9. {"name": "giant", "description": "giant instance"}]
  10. return json.dumps(plans)

Creating new instances

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

Let’s create the view for this action:

  1. from flask import request
  2.  
  3.  
  4. @app.route("/resources", methods=["POST"])
  5. def add_instance():
  6. name = request.form.get("name")
  7. plan = request.form.get("plan")
  8. team = request.form.get("team")
  9. # use the given parameters to create the instance
  10. return "", 201

Updating service instances

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

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

Here’s an example implementation for this endpoint:

  1. from flask import request
  2.  
  3.  
  4. @app.route("/resources", methods=["POST"])
  5. def add_instance():
  6.  
  7. @app.route("/resources/<name>", methods=["PUT"])
  8. def update_instance(name):
  9. name = request.form.get("name")
  10. description = request.form.get("description")
  11. tags = request.form.get("tag")
  12. team = request.form.get("team")
  13. plan = request.form.get("plan")
  14. # use the given parameters to update the instance "name"
  15. return "", 200

Binding instances to apps

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

If the bind operation succeeds, the API should return 201 as status code withthe 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 variablecalled “SOMEVAR” to be injected in the app environment:

  1. import json
  2.  
  3. from flask import request
  4.  
  5.  
  6. @app.route("/resources/<name>/bind-app", methods=["POST"])
  7. def bind_app(name):
  8. app_host = request.form.get("app-host")
  9. # use name and app_host to bind the service instance and the #
  10. application
  11. envs = {"SOMEVAR": "somevalue"}
  12. return json.dumps(envs), 201

Unbinding instances from apps

In the unbind action, tsuru issues a DELETE request to the URL/resources/<service-instance-name>/bind-app.

If the unbind operation succeeds, the API should return 200 as status code.Let’s create the view for this action:

  1. @app.route("/resources/<name>/bind-app", methods=["DELETE"])
  2. def unbind_app(name):
  3. app_host = request.form.get("app-host")
  4. # use name and app-host to remove the bind
  5. return "", 200

Whitelisting units

When binding and unbindin application and service instances, tsuru will alsoprovide information about units that will have access to the service instance,so the service API can handle any required whitelisting (writing ACL rules to anetwork 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 anyaccess control can be handled by the API:

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

Removing instances

In the remove action, tsuru issues a DELETE request to the URL/resources/<service_name>.

If the service instance is successfully removed, the API should return 200 asstatus code.

Let’s create a view for this action:

  1. @app.route("/resources/<name>", methods=["DELETE"])
  2. def remove_instance(name):
  3. # remove the instance named "name"
  4. return "", 200

Checking the status of an instance

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 shouldreturn 204.

Let’s create a view for this action:

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

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

  1. import json
  2.  
  3. from flask import Flask, request
  4.  
  5. app = Flask(__name__)
  6.  
  7.  
  8. @app.route("/resources/plans", methods=["GET"])
  9. def plans():
  10. plans = [{"name": "small", "description": "small instance"},
  11. {"name": "medium", "description": "medium instance"},
  12. {"name": "big", "description": "big instance"},
  13. {"name": "giant", "description": "giant instance"}]
  14. return json.dumps(plans)
  15.  
  16.  
  17. @app.route("/resources", methods=["POST"])
  18. def add_instance():
  19. name = request.form.get("name")
  20. plan = request.form.get("plan")
  21. team = request.form.get("team")
  22. # use the given parameters to create the instance
  23. return "", 201
  24.  
  25.  
  26. @app.route("/resources/<name>/bind-app", methods=["POST"])
  27. def bind_app(name):
  28. app_host = request.form.get("app-host")
  29. # use name and app_host to bind the service instance and the #
  30. application
  31. envs = {"SOMEVAR": "somevalue"}
  32. return json.dumps(envs), 201
  33.  
  34.  
  35. @app.route("/resources/<name>/bind-app", methods=["DELETE"])
  36. def unbind_app(name):
  37. app_host = request.form.get("app-host")
  38. # use name and app-host to remove the bind
  39. return "", 200
  40.  
  41.  
  42. @app.route("/resources/<name>", methods=["DELETE"])
  43. def remove_instance(name):
  44. # remove the instance named "name"
  45. return "", 200
  46.  
  47.  
  48. @app.route("/resources/<name>/bind", methods=["POST", "DELETE"])
  49. def access_control(name):
  50. app_host = request.form.get("app-host")
  51. unit_host = request.form.get("unit-host")
  52. # use unit-host and app-host, according to the access control tool, and
  53. # the request method.
  54. return "", 201
  55.  
  56.  
  57. @app.route("/resources/<name>/status", methods=["GET"])
  58. def status(name):
  59. # check the status of the instance named "name"
  60. return "", 204
  61.  
  62. if __name__ == "__main__":
  63. app.run()

Creating a service manifest

Using tsuru-client you can create a manifest template:

  1. $ tsuru service-template

This will create a manifest.yaml in your current path with this content:

  1. id: servicename
  2. password: abc123
  3. endpoint:
  4. production: production-endpoint.com

The manifest.yaml is used to defined the ID, the password and theproduction endpoint of your service.

Change these information in the created manifest, and the submit yourservice:

  1. id: servicename
  2. username: username_to_auth
  3. password: 1CWpoX2Zr46Jhc7u
  4. endpoint:
  5. production: production-endpoint.com
  6. test: test-endpoint.com:8080

submit your service: Submiting your service API

Submiting your service API

To submit your service, you can run:

  1. $ tsuru service-create manifest.yaml

For more details, check the service API workflow and thetsuru-client service management reference.

原文: https://docs.tsuru.io/1.6/services/build.html