Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions linodecli/overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ def handle_databases_postgres_config_view(
return databases_postgres_config_view_output(json_data)


@output_override("databases", "valkey-config-view", OutputMode.table)
def handle_databases_valkey_config_view(
operation, output_handler, json_data
) -> bool:
# pylint: disable=unused-argument
"""
Override the output of 'linode-cli databases valkey-config-view'
to properly display the valkey engine config.
"""
return databases_valkey_config_view_output(json_data)


@output_override("domains", "zone-file", OutputMode.delimited)
def handle_domains_zone_file(operation, output_handler, json_data) -> bool:
# pylint: disable=unused-argument
Expand Down Expand Up @@ -317,11 +329,26 @@ def pg_view_output(json_data) -> bool:
return False


def add_param_row(output, param_name, param_data):
def _format_param_type(param_type) -> str:
"""
Format a DB Config parameter type, which may be a list for nullable
parameters (e.g. ["integer", "null"] -> "integer | null").
"""
if isinstance(param_type, list):
return " | ".join(str(t) for t in param_type)

return str(param_type)


def add_param_row(output, param_name, param_data, format_nullable_type=False):
"""
Construct and add a row to the output table for DB Config view overrides.
"""
param_type = str(param_data.get("type", ""))
param_type = param_data.get("type", "")
if format_nullable_type:
param_type = _format_param_type(param_type)
else:
param_type = str(param_type)
example = str(param_data.get("example", ""))
minimum = str(param_data.get("minimum", ""))
maximum = str(param_data.get("maximum", ""))
Expand Down Expand Up @@ -409,3 +436,29 @@ def databases_postgres_config_view_output(json_data) -> bool:
console.print(output)

return False


def databases_valkey_config_view_output(json_data) -> bool:
"""
Parse and format the Valkey configuration output table.
"""
output = Table(header_style="bold", show_lines=True)

output.add_column("Parameter", style="bold")
output.add_column("Type", justify="center")
output.add_column("Example", justify="center")
output.add_column("Min", justify="center")
output.add_column("Max", justify="center")
output.add_column("Min Length", justify="center")
output.add_column("Max Length", justify="center")
output.add_column("Pattern", justify="center")
output.add_column("Requires Restart", justify="center")
output.add_column("Description", style="dim")

for field, params in json_data.items():
add_param_row(output, field, params, format_nullable_type=True)

console = Console()
console.print(output)

return False
97 changes: 97 additions & 0 deletions tests/integration/database/fixtures.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import json

import pytest

from tests.integration.database.helpers import wait_for_database_status
from tests.integration.helpers import (
BASE_CMDS,
DEFAULT_REGION,
delete_target_id,
exec_test_command,
get_random_text,
Expand Down Expand Up @@ -76,3 +80,96 @@ def mysql_cluster():
delete_target_id(
target="databases", delete_command="mysql-delete", id=database_id
)


@pytest.fixture(scope="session")
def valkey_cluster():
commands = BASE_CMDS["databases"]
engines = json.loads(exec_test_command(commands + ["engines", "--json"]))
engine = next(
(item["id"] for item in engines if item["id"].startswith("valkey/")),
None,
)
assert engine is not None, "No Valkey engine is available"

types = json.loads(exec_test_command(commands + ["types", "--json"]))
plans = [
(item["id"], item["engines"]["price"]["hourly"])
for item in types
if item.get("_split") == "valkey" and item["engines"]["quantity"] == 1
]
assert plans, "No single-node Valkey database type is available"
plan = min(plans, key=lambda entry: entry[1])[0]

regions = json.loads(
exec_test_command(["linode-cli", "regions", "list", "--json"])
)
supported_regions = [
item["id"]
for item in regions
if "Managed Databases" in item["capabilities"]
and item.get("status") == "ok"
]
region = (
DEFAULT_REGION
if DEFAULT_REGION in supported_regions
else next(iter(supported_regions), None)
)
assert region is not None, "No available Managed Databases region found"

label = get_random_text(8) + "-valkey"
created = json.loads(
exec_test_command(
commands
+ [
"valkey-create",
"--engine",
engine,
"--label",
label,
"--region",
region,
"--type",
plan,
"--engine_config.valkey_timeout",
"301",
"--engine_config.valkey_active_expire_effort",
"2",
"--engine_config.valkey_lfu_decay_time",
"2",
"--engine_config.valkey_lfu_log_factor",
"11",
"--engine_config.valkey_maxmemory_policy",
"allkeys-lru",
"--no-defaults",
"--json",
]
)
)[0]
database_id = created["id"]

try:
wait_for_database_status("valkey", database_id, "active")
yield {
"id": database_id,
"engine": engine,
"label": label,
"region": region,
"type": plan,
"engine_config": {
"valkey_timeout": 301,
"valkey_active_expire_effort": 2,
"valkey_lfu_decay_time": 2,
"valkey_lfu_log_factor": 11,
"valkey_maxmemory_policy": "allkeys-lru",
},
}
finally:
delete_target_id(
target="databases",
delete_command="valkey-delete",
id=str(database_id),
use_retry=True,
retries=5,
delay=15,
)
37 changes: 37 additions & 0 deletions tests/integration/database/helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,46 @@
import json
import subprocess

from tests.integration.helpers import (
BASE_CMDS,
exec_test_command,
wait_for_condition,
)


def database_command(action, *args):
return BASE_CMDS["databases"] + [action, *map(str, args)]


def database_json(action, *args):
return json.loads(
exec_test_command(database_command(action, *args, "--json"))
)


def private_database_json(action, database_id):
process = subprocess.run(
database_command(action, database_id, "--json"),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=False,
)
assert (
process.returncode == 0
), f"{action} failed (exit {process.returncode})"
return json.loads(process.stdout)[0]


def wait_for_database_status(engine, database_id, status):
wait_for_condition(
15,
900,
lambda: database_json(f"{engine}-view", database_id)[0]["status"]
== status,
)


def get_db_type_id():
db_type_ids = exec_test_command(
BASE_CMDS["databases"]
Expand Down
Loading
Loading