355 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			355 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # REST API Tutorial
 | |
| 
 | |
| This tutorial is a quick overview of what the MaxScale REST API offers, how it
 | |
| can be used to inspect the state of MaxScale and how to use it to modify the
 | |
| runtime configuration of MaxScale. The tutorial uses the `curl` command line
 | |
| client to demonstrate how the API is used.
 | |
| 
 | |
| ## Configuration and Hardening
 | |
| 
 | |
| The MaxScale REST API listens on port 8989 on the local host. The `admin_port`
 | |
| and `admin_host` parameters control which port and address the REST API listens
 | |
| on. Note that for security reasons the API only listens for local connections
 | |
| with the default configuration. It is critical that the default credentials are
 | |
| changed and TLS/SSL encryption is configured before exposing the REST API to a
 | |
| network.
 | |
| 
 | |
| The default user for the REST API is `admin` and the password is `mariadb`. The
 | |
| easiest way to secure the REST API is to use the `maxctrl` command line client
 | |
| to create a new admin user and delete the default one. To do this, run the
 | |
| following commands:
 | |
| 
 | |
| ```
 | |
| maxctrl create user my_user my_password --type=admin
 | |
| maxctrl destroy user admin
 | |
| ```
 | |
| 
 | |
| This will create the user `my_user` with the password `my_password` that is an
 | |
| administrative account. After this account is created, the default `admin`
 | |
| account is removed with the next command.
 | |
| 
 | |
| The next step is to enable TLS encryption. To do this, you need a CA
 | |
| certificate, a private key and a public certificate file all in PEM format. Add
 | |
| the following three parameters under the `[maxscale]` section of the MaxScale
 | |
| configuration file and restart MaxScale.
 | |
| 
 | |
| ```
 | |
| admin_ssl_key=/certs/server-key.pem
 | |
| admin_ssl_cert=/certs/server-cert.pem
 | |
| admin_ssl_ca_cert=/certs/ca-cert.pem
 | |
| ```
 | |
| 
 | |
| Use `maxctrl` to verify that the TLS encryption is enabled. In this tutorial our
 | |
| server certificates are self-signed so the `--tls-verify-server-cert=false`
 | |
| option is required.
 | |
| 
 | |
| ```
 | |
| maxctrl --user=my_user --password=my_password --secure --tls-ca-cert=/certs/ca-cert.pem --tls-verify-server-cert=false show maxscale
 | |
| ```
 | |
| 
 | |
| If no errors are raised, this means that the communication via the REST API is
 | |
| now secure and can be used across networks.
 | |
| 
 | |
| ## Requesting Data
 | |
| 
 | |
| **Note:** For the sake of brevity, the rest of this tutorial will omit the
 | |
| TLS/SSL options from the `curl` command line. For more information, refer to the
 | |
| `curl` manpage.
 | |
| 
 | |
| The most basic task to do with the REST API is to see whether MaxScale is up and
 | |
| running. To do this, we do a HTTP request on the root resource (the `-i` option
 | |
| shows the HTTP headers).
 | |
| 
 | |
| `curl -i 127.0.0.1:8989/v1/`
 | |
| ```
 | |
| HTTP/1.1 200 OK
 | |
| Connection: Keep-Alive
 | |
| Content-Length: 0
 | |
| Last-Modified: Mon, 04 Mar 2019 08:23:09 GMT
 | |
| ETag: "0"
 | |
| Date: Mon, 04 Mar 19 08:29:41 GMT
 | |
| ```
 | |
| 
 | |
| To query a resource collection endpoint, append it to the URL. The `/v1/filters/`
 | |
| endpoint shows the list of filters configured in MaxScale. This is a _resource
 | |
| collection_ endpoint: it contains the list of all resources of a particular
 | |
| type.
 | |
| 
 | |
| `curl 127.0.0.1:8989/v1/filters`
 | |
| ```
 | |
| {
 | |
|     "links": {
 | |
|         "self": "http://127.0.0.1:8989/v1/filters/"
 | |
|     },
 | |
|     "data": [
 | |
|         {
 | |
|             "id": "Hint",
 | |
|             "type": "filters",
 | |
|             "relationships": {
 | |
|                 "services": {
 | |
|                     "links": {
 | |
|                         "self": "http://127.0.0.1:8989/v1/services/"
 | |
|                     },
 | |
|                     "data": [
 | |
|                         {
 | |
|                             "id": "RW-Split-Hint-Router",
 | |
|                             "type": "services"
 | |
|                         }
 | |
|                     ]
 | |
|                 }
 | |
|             },
 | |
|             "attributes": {
 | |
|                 "module": "hintfilter",
 | |
|                 "parameters": {}
 | |
|             },
 | |
|             "links": {
 | |
|                 "self": "http://127.0.0.1:8989/v1/filters/Hint"
 | |
|             }
 | |
|         },
 | |
|         {
 | |
|             "id": "Logger",
 | |
|             "type": "filters",
 | |
|             "relationships": {
 | |
|                 "services": {
 | |
|                     "links": {
 | |
|                         "self": "http://127.0.0.1:8989/v1/services/"
 | |
|                     },
 | |
|                     "data": []
 | |
|                 }
 | |
|             },
 | |
|             "attributes": {
 | |
|                 "module": "qlafilter",
 | |
|                 "parameters": {
 | |
|                     "match": null,
 | |
|                     "exclude": null,
 | |
|                     "user": null,
 | |
|                     "source": null,
 | |
|                     "filebase": "/tmp/log",
 | |
|                     "options": "ignorecase",
 | |
|                     "log_type": "session",
 | |
|                     "log_data": "date,user,query",
 | |
|                     "newline_replacement": "\" \"",
 | |
|                     "separator": ",",
 | |
|                     "flush": false,
 | |
|                     "append": false
 | |
|                 },
 | |
|                 "filter_diagnostics": {
 | |
|                     "separator": ",",
 | |
|                     "newline_replacement": "\" \""
 | |
|                 }
 | |
|             },
 | |
|             "links": {
 | |
|                 "self": "http://127.0.0.1:8989/v1/filters/Logger"
 | |
|             }
 | |
|         }
 | |
|     ]
 | |
| }
 | |
| ```
 | |
| 
 | |
| The `data` holds the actual list of resources: the `Hint` and `Logger`
 | |
| filters. Each object has the `id` field which is the unique name of that
 | |
| object. It is the same as the section name in `maxscale.cnf`.
 | |
| 
 | |
| Each resource in the list has a `relationships` object. This shows the
 | |
| relationship links between resources. In our example, the `Hint` filter is used
 | |
| by a service named `RW-Split-Hint-Router` and the `Logger` is not currently in
 | |
| use.
 | |
| 
 | |
| To request an individual resource, we add the object name to the resource
 | |
| collection URL. For example, if we want to get only the `Logger` filter we
 | |
| execute the following command.
 | |
| 
 | |
| `curl 127.0.0.1:8989/v1/filters/Logger`
 | |
| ```
 | |
| {
 | |
|     "links": {
 | |
|         "self": "http://127.0.0.1:8989/v1/filters/Logger"
 | |
|     },
 | |
|     "data": {
 | |
|         "id": "Logger",
 | |
|         "type": "filters",
 | |
|         "relationships": {
 | |
|             "services": {
 | |
|                 "links": {
 | |
|                     "self": "http://127.0.0.1:8989/v1/services/"
 | |
|                 },
 | |
|                 "data": []
 | |
|             }
 | |
|         },
 | |
|         "attributes": {
 | |
|             "module": "qlafilter",
 | |
|             "parameters": {
 | |
|                 "match": null,
 | |
|                 "exclude": null,
 | |
|                 "user": null,
 | |
|                 "source": null,
 | |
|                 "filebase": "/tmp/log",
 | |
|                 "options": "ignorecase",
 | |
|                 "log_type": "session",
 | |
|                 "log_data": "date,user,query",
 | |
|                 "newline_replacement": "\" \"",
 | |
|                 "separator": ",",
 | |
|                 "flush": false,
 | |
|                 "append": false
 | |
|             },
 | |
|             "filter_diagnostics": {
 | |
|                 "separator": ",",
 | |
|                 "newline_replacement": "\" \""
 | |
|             }
 | |
|         },
 | |
|         "links": {
 | |
|             "self": "http://127.0.0.1:8989/v1/filters/Logger"
 | |
|         }
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| Note that this time the `data` member holds an object instead of an array of
 | |
| objects. All other parts of the response are similar to what was shown in the
 | |
| previous example.
 | |
| 
 | |
| ## Creating Objects
 | |
| 
 | |
| One of the uses of the REST API is to create new objects in MaxScale at
 | |
| runtime. This allows new servers, services, filters, monitor and listeners to be
 | |
| created without restarting MaxScale.
 | |
| 
 | |
| For example, to create a new server in MaxScale the JSON definition of a server
 | |
| must be sent to the REST API at the `/v1/servers/` endpoint. The request body
 | |
| defines the server name as well as the parameters for it.
 | |
| 
 | |
| To create objects with `curl`, first write the JSON definition into a file.
 | |
| 
 | |
| ```
 | |
| {
 | |
|     "data": {
 | |
|         "id": "server1",
 | |
|         "type": "servers",
 | |
|         "attributes": {
 | |
|             "parameters": {
 | |
|                 "address": "127.0.0.1",
 | |
|                 "port": 3003
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| To send the data, use the following command.
 | |
| 
 | |
| ```
 | |
| curl -X POST -d @new_server.txt 127.0.0.1:8989/v1/servers
 | |
| ```
 | |
| 
 | |
| The `-d` option takes a file name prefixed with a `@` as an argument. Here we
 | |
| have `@new_server.txt` which is the name of the file where the JSON definition
 | |
| was stored. The `-X` option defines the HTTP verb to use and to create a new
 | |
| object we must use the POST verb.
 | |
| 
 | |
| To verify the data request the newly created object.
 | |
| 
 | |
| ```
 | |
| curl 127.0.0.1:8989/v1/servers/server1
 | |
| ```
 | |
| 
 | |
| ## Modifying Data
 | |
| 
 | |
| The easiest way to modify an object is to first request it, store the result in
 | |
| a file, edit it and then send the updated object back to the REST API.
 | |
| 
 | |
| Let's say we want to modify the port that the server we created earlier listens
 | |
| on. First we request the current object and store the result in a file.
 | |
| 
 | |
| ```
 | |
| curl 127.0.0.1:8989/v1/servers/server1 > server1.txt
 | |
| ```
 | |
| 
 | |
| After that we edit the file and change the port from 3003 to 3306. Next the
 | |
| modified JSON object is sent to the REST API as a PATCH command. To do this,
 | |
| execute the following command.
 | |
| 
 | |
| ```
 | |
| curl -X PATCH -d @server1.txt 127.0.0.1:8989/v1/servers/server1
 | |
| ```
 | |
| 
 | |
| To verify that the data was updated correctly, request the updated object.
 | |
| 
 | |
| ```
 | |
| curl 127.0.0.1:8989/v1/servers/server1
 | |
| ```
 | |
| 
 | |
| ## Object Relationships
 | |
| 
 | |
| To continue with our previous example, we add the updated server to a
 | |
| service. To do this, the `relationships` object of the server must be modified
 | |
| to include the service we want to add the server to.
 | |
| 
 | |
| To define a relationship between a server and a service, the `data` member must
 | |
| have the `relationships` field and it must contain an object with the `services`
 | |
| field (some fields omitted for brevity).
 | |
| 
 | |
| ```
 | |
| {
 | |
|     "data": {
 | |
|         "id": "server1",
 | |
|         "type": "servers",
 | |
|         "relationships": {
 | |
|             "services": {
 | |
|                 "data": [
 | |
|                     {
 | |
|                         "id": "RW-Split-Router",
 | |
|                         "type": "services"
 | |
|                     }
 | |
|                 ]
 | |
|             }
 | |
|         },
 | |
|         "attributes":  ...
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| The `data.relationships.services.data` field contains a list of objects that
 | |
| define the `id` and `type` fields. The id is the name of the object (a service
 | |
| or a monitor for servers) and the type tells which type it is. Only `services`
 | |
| type objects should be present in the `services` object.
 | |
| 
 | |
| In our example we are linking the `server1` server to the `RW-Split-Router`
 | |
| service. As was seen with the previous example, the easiest way to do this is to
 | |
| store the result, edit it and then send it back with a HTTP PATCH.
 | |
| 
 | |
| If we want to remove a server from _all_ services, we can set the
 | |
| `relationships` field to `{}`. The REST API interprets this as an instruction
 | |
| to remove the server from all services and monitors. This is useful if you want
 | |
| to delete the server which can only be done if it has no relationships to other
 | |
| objects.
 | |
| 
 | |
| ## Deleting Objects
 | |
| 
 | |
| To delete an object, simply execute a HTTP DELETE request on the resource you
 | |
| want to delete. For example, to delete the `server1` server, execute the
 | |
| following command.
 | |
| 
 | |
| ```
 | |
| curl -X DELETE 127.0.0.1:8989/v1/servers/server1
 | |
| ```
 | |
| 
 | |
| ## Further Reading
 | |
| 
 | |
| The full list of all available endpoints in MaxScale can be found in the
 | |
| [REST API documentation](../REST-API/API.md).
 | |
| 
 | |
| The `maxctrl` command line client is self-documenting and the `maxctrl help`
 | |
| command is a good tool for exploring the various commands that are available in
 | |
| it. The `maxctrl api get` command can be useful way to explore the REST API as
 | |
| it provides a way to easily extract values out of the JSON data generated by the
 | |
| REST API.
 | |
| 
 | |
| There is a multitude of REST API clients readily available and most of them are
 | |
| far more convenient to use than `curl`. We recommend investigating what you need
 | |
| and how you intend to either integrate or use the MaxScale REST API. Most modern
 | |
| languages either have a built-in HTTP library or there exists a de facto
 | |
| standard library.
 | |
| 
 | |
| The MaxScale REST API follows the JSON API specification and there exist
 | |
| libraries that are built specifically for these sorts of APIs
 | 
