MXS-2260: Add REST API tutorial
The tutorial shows how to configure the MaxScale REST API and communicate with it using the `curl` command line client.
This commit is contained in:
parent
af33d19703
commit
42b3f970c5
@ -53,6 +53,7 @@ Here are tutorials on monitoring and managing MariaDB MaxScale in cluster enviro
|
||||
|
||||
- [MariaDB MaxScale HA with Lsyncd](Tutorials/MaxScale-HA-with-lsyncd.md)
|
||||
- [Nagios Plugins for MariaDB MaxScale Tutorial](Tutorials/Nagios-Plugins.md)
|
||||
- [REST API Tutorial](Tutorials/REST-API-Tutorial.md)
|
||||
|
||||
## Routers
|
||||
|
||||
|
355
Documentation/Tutorials/REST-API-Tutorial.md
Normal file
355
Documentation/Tutorials/REST-API-Tutorial.md
Normal file
@ -0,0 +1,355 @@
|
||||
# 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 for 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 created
|
||||
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
|
Loading…
x
Reference in New Issue
Block a user