Skip to content
Open
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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- `linodecli/configuration/` owns config loading, interactive configuration, OAuth token flow, env token handling, and API URL overrides.
- `linodecli/plugins/` contains hand-written commands outside the generated OpenAPI surface. If you add or change a plugin, read `linodecli/plugins/README.md` for the `call(args, context)` interface and third-party `PLUGIN_NAME` requirement.
- If you touch `linodecli/baked/*.py`, read `linodecli/baked/AGENTS.md` first. Key constraint: baked model state must stay pickle-safe.
- The table and ASCII table output overrides for `object-storage keys-create` disable truncation so the one-time secret key is displayed in full.

## Tests
- `make test` only runs unit tests; it is an alias for `make test-unit`.
Expand Down
24 changes: 24 additions & 0 deletions linodecli/overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,30 @@ def inner(func):
return inner


@output_override("object-storage", "keys-create", OutputMode.table)
def handle_object_storage_keys_create(
operation, output_handler, json_data
) -> bool:
# pylint: disable=unused-argument
"""
Preserve the complete secret key, which is only returned on creation.
"""
output_handler.disable_truncation = True
return True


@output_override("object-storage", "keys-create", OutputMode.ascii_table)
def handle_object_storage_keys_create_ascii(
operation, output_handler, json_data
) -> bool:
# pylint: disable=unused-argument
"""
Preserve the complete secret key in ASCII table output as well.
"""
output_handler.disable_truncation = True
return True


@output_override("databases", "mysql-config-view", OutputMode.table)
def handle_databases_mysql_config_view(
operation, output_handler, json_data
Expand Down
36 changes: 36 additions & 0 deletions tests/unit/test_overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import io
from unittest.mock import patch

import pytest
from rich.console import Console

from linodecli import OutputMode
from linodecli.overrides import OUTPUT_OVERRIDES

Expand All @@ -11,6 +14,39 @@ class TestOverrides:
Unit tests for linodecli.overrides
"""

@pytest.mark.parametrize("mode", [OutputMode.table, OutputMode.ascii_table])
@pytest.mark.parametrize("action", ["keys-create", "keys-list"])
def test_object_storage_key_truncation(
self, mock_cli, list_operation_for_output_tests, action, mode
):
operation = list_operation_for_output_tests
operation.command = "object-storage"
operation.action = action
mock_cli.output_handler.mode = mode
mock_cli.output_handler.columns = "cool"
secret = "UNIQUESECRETKEY0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
output = io.StringIO()
console = Console(file=output, width=28, color_system=None)

with patch(
"linodecli.output.output_handler.rprint",
lambda *args, **kwargs: console.print(*args),
):
operation.process_response_json(
{"cool": secret}, mock_cli.output_handler
)

# Rich folds long values across rows; reconstruct the cell content.
rendered = "".join(
line.strip("│| ") for line in output.getvalue().splitlines()
)
if action == "keys-create":
assert secret in rendered
assert "…" not in rendered
else:
assert secret not in rendered
assert "…" in rendered

def test_domains_zone_file(
self, mock_cli, list_operation_for_overrides_test
):
Expand Down