This project provides key-value storages as an extension for Apache CloudStack.
Following storage types are supported:
- ACCOUNT
Persistent storages for Apache CloudStack accounts managed via Apache CloudStack API. Each account can have many storages. This storage type can be configured to save a history of operations.
- VM
Storages for Apache CloudStack virtual machines created and deleted automatically while creating or expunging virtual machines.
- TEMP
Temporal storages with specified TTL created via Apache CloudStack API. TTL can be updated after creation. This operation as well as storage deletion can be done via Apache CloudStack API and key-value storage API.
Following components should be deployed:
- Elasticsearch 6.2
The official documentation can be found at https://www.elastic.co/guide/en/elasticsearch/reference/6.2/index.html
Once it is deployed next step is to create necessary indexes and templates. To achieve this [initialization script] (/elasticsearch/init.sh) should be executed (use -h option to print how to use it).
- cs-kv-storage
See configuration and build & run sections.
The example of the configuration file can be found here.
Property | Description |
---|---|
elasticsearch.uri | Elasticsearch addresses in the format elasticsearch://host:port,host:port, http://host:port,host:port or https://host:port,host:port. |
elasticsearch.auth.username | Elasticsearch username for authentication. |
elasticsearch.auth.password | Elasticsearch password for authentication. |
elasticsearch.scroll.page-size | Batch size to retrieve all results for key listing. |
elasticsearch.scroll.keep-alive | Timeout between batch requests to retrieve all results for key listing. |
elasticsearch.limit.value.max-size | Max length in bytes of the value in UTF-8; -1 if it is unlimited. |
elasticsearch.limit.key.max-size | Max length in bytes of the key in UTF-8; can not be more than 512 bytes. |
app.cache.max-size | Max size of the storage cache. |
app.cache.expiration-time | TTL for the storage cache items. |
app.history.flush-size | Size of batch requests to save a history of the storage operations. |
app.history.flush-timeout | Timeout between batch/retry requests to save a history of the storage operations. |
app.history.retry-limit | Amount of attempts to try to log the storage operation. |
app.default-page-size | A default number of results returned in the page for search requests. |
app.request-timeout | Maximum time to process the request. |
$ sbt assembly
$ java -Dconfig.file=<config.path> -jar target/scala-2.12/cs-kv-storage-<version>-jar-with-dependencies.jar
where <config.path>
and <version>
should be replaced with actual values.
This project provides two options to build docker images:
using a default version
$ docker build -t <tag> .
$ docker -p <port>:8080 -v <config.path>:/opt/cs-kv-storage/application.conf <tag>
where <tag>
, <port>
and <config.path>
should be replaced with actual values
using a ready jar
$ docker build -t <tag> --build-arg APP_PATH=<path to jar with dependencies> .
$ docker -p <port>:8080 -v <config.path>:/opt/cs-kv-storage/application.conf <tag>
where <tag>
, <path to jar with dependencies>
, <port>
and <config.path>
should be replaced with actual values.
<path to jar with dependencies>
can be a path in a file system or URL, e.g.
https://oss.sonatype.org/service/local/artifact/maven/redirect?r=snapshots&g=com.bwsw&a=cs-kv-storage_2.12&c=jar-with-dependencies&v=1.0.1-SNAPSHOT
target/scala-2.12/cs-kv-storage-1.0.1-SNAPSHOT-jar-with-dependencies.jar
All operations require a valid Secret-Key
header.
- Get the value by the key
- Get values by keys
- Set the value for the key
- Set values for keys
- Remove the mapping by the key
- Remove mappings by keys
- List keys
- Clear
GET /get/<storage UUID>/<key>
HTTP Status Code | Description |
---|---|
200 | The request is processed successfully. The value is returned in the body. The content type is text/plain. |
404 | The storage does not exist or does not contain a mapping for the key. |
500 | The request can not be processed because of an internal error. |
POST /get/<storage UUID>
Content-Type: application/json
[
"key1",
"key2",
"key3"
]
HTTP Status Code | Description |
---|---|
200 | The request is processed successfully. Results are returned in the body as a map with null values for keys that do not exist. The content type is application/json. |
404 | The storage does not exist. |
500 | The request can not be processed because of an internal error. |
In the following example the mapping for the second key does not exist.
{
"key1": "value1",
"key2": null,
"key3": "value3"
}
PUT /set/<storage UUID>/somekey
Content-Type: text/plain
somevalue
HTTP Status Code | Description |
---|---|
200 | The request is processed successfully. |
400 | The content-type, key or value are invalid. |
404 | The storage does not exist. |
500 | The request can not be processed because of an internal error. |
PUT /set/<storage UUID>
Content-Type: application/json
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
HTTP Status Code | Description |
---|---|
200 | The request is processed successfully. Results are returned in the body as a map with boolean values as an operation status. The content type is application/json. |
400 | The content-type, body, key or value are invalid. |
404 | The storage does not exist. |
500 | The request can not be processed because of an internal error. |
In the following example values for the first and third keys are set successfully.
{
"key1": true,
"key2": false,
"key3": true
}
DELETE /delete/<storage UUID>/somekey
HTTP Status Code | Description |
---|---|
200 | The request is processed successfully. |
404 | The storage does not exist. |
500 | The request can not be processed because of an internal error. |
POST /delete/<storage UUID>
Content-Type: application/json
[
"key1",
"key2",
"key3"
]
HTTP Status Code | Description |
---|---|
200 | The request is processed successfully. Results are returned in the body as a map with boolean values as an operation status. The content type is application/json. |
400 | The content-type or body are invalid. |
404 | The storage does not exist. |
500 | The request can not be processed because of an internal error. |
In the following example mappings for the first and third keys are deleted successfully.
{
"key1": true,
"key2": false,
"key3": true
}
GET /list/<storage UUID>
HTTP Status Code | Description |
---|---|
200 | The request is processed successfully. Results are returned in the body as an array. The content type is application/json. |
404 | The storage does not exist. |
500 | The request can not be processed because of an internal error. |
[
"key1",
"key2",
"key3"
]
POST /clear/<storage UUID>
HTTP Status Code | Description |
---|---|
200 | The request is processed successfully. |
409 | Conflicts occurred during executing the operation, the storage may have been cleared partially. |
500 | The request can not be processed because of an internal error. |
Secret-Key
header is mandatory.
GET /history/<storage UUID>
All parameters are optional.
Parameter | Description |
---|---|
keys | Comma separated list of keys |
operations | Comma separated list of operations. Possible values are set, delete or clear. |
start | The start date/time as Unix timestamp to retrieve history records with dates >= start |
end | The end date/time as Unix timestamp to retrieve history records with dates <= end |
sort | Comma separated list of response fields optionally prefixed with - (minus) for descending order. |
page | A page number of results (1 by default) |
size | A number of results returned in the page/batch (default value is specified in the configuration file) |
scroll | A timeout in ms for subsequent list requests |
* start
and end
parameters can be used separately. If both start
and end
parameters are specified history
records with dates that are greater/equal to start
and less/equal to end
are returned.
** If both page
and scroll
parameters are specified scroll
is used.
HTTP Status code | Description |
---|---|
200 | The request is processed successfully. Results are in the body in the format specified below. The content type is application/json. |
400 | The storage does not support history or the request is invalid. |
404 | The storage does not exist. |
500 | The request can not be processed because of an internal error. |
For requests with page parameter
{
"total":1000,
"size":10,
"page":1,
"items":[
{
"key":"key",
"value":"value",
"operation":"set/delete/clear",
"timestamp":1528442057000
}
]
}
For requests with scroll parameter
{
"total":1000,
"size":10,
"scrollId":"scroll id",
"items":[
{
"key":"key",
"value":"value",
"operation":"set/delete/clear",
"timestamp":1528442057000
}
]
}
POST /history
Content-Type: application/json
{
"scrollId":"scroll id",
"timeout": 60000
}
HTTP Status code | Body |
---|---|
200 | The request is processed successfully. Results are in the body in the format for search requests with scroll. The content type is application/json. |
400 | The scroll id is invalid/expired or the request is invalid. |
500 | The request can not be processed because of an internal error. |
All operations require a valid Secret-Key
header.
PUT /storage/<storage UUID>?ttl=<ttl>
Parameter | Description |
---|---|
ttl | TTL in milliseconds |
HTTP Status Code | Description |
---|---|
200 | The request is processed successfully. |
400 | The storage type is not TEMP. |
404 | The storage does not exist. |
500 | The request can not be processed because of an internal error. |
DELETE /storage/<storage UUID>
HTTP Status Code | Description |
---|---|
200 | The request is processed successfully. |
400 | The storage type is not TEMP. |
404 | The storage does not exist. |
500 | The request can not be processed because of an internal error. |
GET /health
Parameter | Description |
---|---|
detailed | An optional boolean parameter whether it is need to check components that it depends on. Default value - false. |
HTTP Status Code | Description |
---|---|
200 | Healthy |
500 | Unhealthy |
If detailed = false then response body is empty, otherwise results are in the response body in the format specified below and the content type is application/json:
{
"status":"HEALTHY/UNHEALTHY",
"checks":[
{
"name":"<name>",
"status":"HEALTHY/UNHEALTHY",
"message":"<message>"
}
]
}