Skip to content

Commit

Permalink
api: add support for v2
Browse files Browse the repository at this point in the history
Scylla supports swagger v2 api for config and metrics-config.
Currently this api-client bases its per-api metadata on version 1.2,
so translate the v2 json metadata to the existing structures
so the v2 apis can be represented along the existing ones.

Fixes scylladb#47

Signed-off-by: Benny Halevy <[email protected]>
  • Loading branch information
bhalevy committed Dec 1, 2024
1 parent 67e26f5 commit 2af51c9
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 5 deletions.
33 changes: 32 additions & 1 deletion scylla_api_client/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import json
from argparse import ArgumentParser
from pprint import PrettyPrinter
import json

from .rest.scylla_rest_client import ScyllaRestClient

Expand Down Expand Up @@ -387,7 +388,7 @@ def load(self):
return
for module_def in top_json["apis"]:
# FIXME: handle service down, errors
module_json = self.client.get_raw_api_json(f"{module_def['path']}/")
module_json = self.client.get_raw_api_json(f"/api-doc{module_def['path']}/")
module_path = module_def['path'].strip(' /')
module = ScyllaApiModule(module_path, module_def['description'])
for command_json in module_json["apis"]:
Expand All @@ -401,3 +402,33 @@ def load(self):
command.load_json(command_json)
module.add_command(command)
self.add_module(module)
for module_def in [json.loads('{ "path": "/v2", "description": "V2 API"}')]:
module_json = self.client.get_raw_api_json(module_def['path'])["paths"]
module_path = module_def['path'].strip(' /')
module = ScyllaApiModule(module_path, module_def['description'])
for command_path in module_json:
operations = []
for op, v2_meta in module_json[command_path].items():
if op.upper() not in ["GET", "POST", "DELETE"]:
continue
operation = { "method": op }
kw_trans = { "description": "summary", "produces": "produces", "parameters": "parameters"}
for v2_kw, v1_kw in kw_trans.items():
if v2_kw in v2_meta:
operation[v1_kw] = v2_meta[v2_kw]
operations.append(operation)
if "produces" in v2_meta:
operation["produces"] = v2_meta["produces"]
command_path = command_path.strip(' /')
if command_path.startswith(module_path):
command_path = command_path[len(module_path)+1:]
json_str = f'{{"path": "{command_path}", "operations": {json.dumps(operations)} }}'
command_json = json.loads(json_str)
log.debug(f"{module_path} {command_path}: {command_json}")
command = ScyllaApiCommand(module_name=module_path,
command_name=command_path,
host=self._host,
port=self._port)
command.load_json(command_json)
module.add_command(command)
self.add_module(module)
4 changes: 2 additions & 2 deletions scylla_api_client/rest/scylla_rest_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ class ScyllaRestClient(RestClient):
def __init__(self, host: str = "localhost", port: str = "10000"):
super().__init__(host=host, port=port)

def get_raw_api_json(self, resource_path: str = ""):
if api := self.get(f"/api-doc{resource_path}"):
def get_raw_api_json(self, resource_path: str = "/api-doc"):
if api := self.get(resource_path):
return api.json()
return None

Expand Down
200 changes: 200 additions & 0 deletions tests/api_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def do_GET(self):
content = json.dumps(self.error_injection()).encode(encoding="utf-8")
elif self.path == "/api-doc/compaction_manager/":
content = json.dumps(self.compaction_manager()).encode(encoding="utf-8")
elif self.path == "/v2":
content = json.dumps(self.v2()).encode(encoding="utf-8")
else:
content = json.dumps(f"""{{"URL": "{self.command}", "method": "{self.path}"}}""").encode(encoding="utf-8")

Expand Down Expand Up @@ -609,6 +611,204 @@ def compaction_manager(self):
}
}""")

def v2(self):
json_resp = json.loads("""{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Scylla API",
"description": "The scylla API version 2.0",
"termsOfService": "http://www.scylladb.com/tos/",
"contact": {
"name": "Scylla Team",
"email": "[email protected]",
"url": "http://scylladb.com"
},
"license": {
"name": "AGPL",
"url": "https://github.com/scylladb/scylla/blob/master/LICENSE.AGPL"
}
},
"host": "localhost:10000",
"basePath": "/",
"schemes": [
"http"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/v2/metrics-config/":{
"get":{
"description":"Return the metrics layer configuration",
"operationId":"get_metrics_config",
"produces":[
"application/json"
],
"tags":[
"metrics"
],
"parameters":[
],
"responses":{
"200":{
"schema": {
"type":"array",
"items":{
"$ref":"#/definitions/metrics_config",
"description":"metrics Config value"
}
}
},
"default":{
"description":"unexpected error",
"schema":{
"$ref":"#/definitions/ErrorModel"
}
}
}
},
"post": {
"description":"Set the metrics layer relabel configuration",
"operationId":"set_metrics_config",
"produces":[
"application/json"
],
"tags":[
"metrics"
],
"parameters":[
{
"in":"body",
"name":"conf",
"description":"An array of relabel_config objects",
"schema": {
"type":"array",
"items":{
"$ref":"#/definitions/metrics_config",
"description":"metrics Config value"
}
}
}
],
"responses":{
"200":{
"description": "OK"
},
"default":{
"description":"unexpected error",
"schema":{
"$ref":"#/definitions/ErrorModel"
}
}
}
}
},
"/v2/config/background_writer_scheduling_quota": {
"get": {
"description": "max cpu usage ratio (between 0 and 1) for compaction process. Not intended for setting in normal operations. Setting it to 1 or higher will disable it, recommended operational setting is 0.5.",
"operationId": "find_config_background_writer_scheduling_quota",
"produces": [
"application/json"
],
"tags": [
"config"
],
"parameters": [],
"responses": {
"200": {
"description": "Config value",
"schema": {
"type": "double"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/ErrorModel"
}
}
}
}
},
"/v2/config/log_to_syslog": {
"get": {
"description": "Send log output to syslog",
"operationId": "find_config_log_to_syslog",
"produces": [
"application/json"
],
"tags": [
"config"
],
"parameters": [],
"responses": {
"200": {
"description": "Config value",
"schema": {
"type": "bool"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/ErrorModel"
}
}
}
}
}
},
"definitions": {
"metrics_config": {
"id": "metrics_config",
"summary": "An entry in the metrics configuration",
"properties": {
"source_labels": {
"type": "array",
"items": {
"type": "string"
},
"description": "The source labels, a match is based on concatenation of the labels"
},
"action": {
"type": "string",
"description": "The action to perform on match",
"enum": [
"skip_when_empty",
"report_when_empty",
"replace",
"keep",
"drop",
"drop_label"
]
},
"target_label": {
"type": "string",
"description": "The application state version"
},
"replacement": {
"type": "string",
"description": "The replacement string to use when replacing a value"
},
"regex": {
"type": "string",
"description": "The regex string to use when replacing a value"
},
"separator": {
"type": "string",
"description": "The separator string to use when concatenating the labels"
}
}
}
}
}"""
)
return json_resp


class ScyllaApiServer:
def __init__(self, port):
Expand Down
4 changes: 2 additions & 2 deletions tests/api_tests/test_scylla_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ def scylla_api_obj(api_server):


def test_number_of_scylla_api_modules(scylla_api_obj):
assert len(scylla_api_obj.modules) == 3
assert len(scylla_api_obj.modules) == 4


def test_module_names(scylla_api_obj):
module_names = ["system", "compaction_manager", "error_injection"]
module_names = ["system", "compaction_manager", "error_injection", "v2"]

for module, expected_name in zip(scylla_api_obj.modules, module_names):
assert module == expected_name
Expand Down

0 comments on commit 2af51c9

Please sign in to comment.