From bb9b07351972f795d17b2541d4209b5df3e3564d Mon Sep 17 00:00:00 2001 From: vshanthe Date: Mon, 8 Sep 2025 18:25:05 +0530 Subject: [PATCH 1/4] integration_tests --- tests/integration/account/test_account.py | 11 +- tests/integration/helpers.py | 41 ++++- tests/integration/lke/test_clusters.py | 21 ++- .../maintenance/test_maintenance_policies.py | 27 ++++ tests/integration/monitor/fixtures.py | 24 +++ tests/integration/monitor/test_alerts.py | 152 ++++++++++++++++++ tests/integration/monitor/test_metrics.py | 119 ++++++++++++++ 7 files changed, 388 insertions(+), 7 deletions(-) create mode 100644 tests/integration/maintenance/test_maintenance_policies.py create mode 100644 tests/integration/monitor/fixtures.py create mode 100644 tests/integration/monitor/test_alerts.py create mode 100644 tests/integration/monitor/test_metrics.py diff --git a/tests/integration/account/test_account.py b/tests/integration/account/test_account.py index 0d3a0689..e3d99c3c 100644 --- a/tests/integration/account/test_account.py +++ b/tests/integration/account/test_account.py @@ -196,6 +196,7 @@ def test_account_setting_view(): "longview_subscription", "network_helper", "interfaces_for_new_linodes", + "maintenance_policy", ] settings_text = exec_test_command( @@ -343,7 +344,15 @@ def test_maintenance_list(): ) lines = res.splitlines() - headers = ["entity.type", "entity.label"] + headers = [ + "complete_time", + "entity.type", + "entity.label", + "maintenance_policy_set", + "not_before", + "source", + "start_time", + ] assert_headers_in_lines(headers, lines) diff --git a/tests/integration/helpers.py b/tests/integration/helpers.py index 0408da72..c58aad09 100644 --- a/tests/integration/helpers.py +++ b/tests/integration/helpers.py @@ -16,9 +16,9 @@ # TypeVars for generic type hints below T = TypeVar("T") - MODULES = [ "account", + "alerts", "domains", "linodes", "nodebalancers", @@ -33,7 +33,9 @@ "linodes", "lke", "longview", + "maintenance", "managed", + "monitor", "networking", "obj", "object-storage", @@ -100,8 +102,41 @@ def exec_failing_test_command( # Delete/Remove helper functions (mainly used in clean-ups after tests) -def delete_target_id(target: str, id: str, delete_command: str = "delete"): - command = ["linode-cli", target, delete_command, id] +def delete_target_id( + target: str, + id: str, + delete_command: str = "delete", + service_type: str = None, + use_retry: bool = False, + retries: int = 3, + delay: int = 80, +): + if service_type: + command = ["linode-cli", target, delete_command, service_type, id] + else: + command = ["linode-cli", target, delete_command, id] + + if use_retry: + last_exc = None + for attempt in range(retries): + try: + subprocess.run( + command, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + return # success + except Exception as e: + last_exc = e + if attempt < retries - 1: + time.sleep(delay) + # If all retries fail, raise + raise RuntimeError( + f"Error executing command '{' '.join(command)}' after {retries} retries: {last_exc}" + ) + try: subprocess.run( command, diff --git a/tests/integration/lke/test_clusters.py b/tests/integration/lke/test_clusters.py index bfd629a9..e5f46361 100644 --- a/tests/integration/lke/test_clusters.py +++ b/tests/integration/lke/test_clusters.py @@ -52,6 +52,8 @@ def test_deploy_an_lke_cluster(): '[{"type":"ext4","size":1024}]', "--k8s_version", lke_version, + "--tier", + "standard", "--text", "--delimiter", ",", @@ -75,7 +77,7 @@ def test_lke_cluster_list(): ) lines = res.splitlines() - headers = ["label", "k8s_version"] + headers = ["label", "k8s_version", "tier", "apl_enabled"] assert_headers_in_lines(headers, lines) @@ -87,8 +89,21 @@ def test_view_lke_cluster(lke_cluster): + ["cluster-view", cluster_id, "--text", "--delimiter=,"] ) lines = res.splitlines() - headers = ["label", "k8s_version"] + headers = ["label", "k8s_version", "tier"] assert_headers_in_lines(headers, lines) + data_rows = lines[1:] + assert data_rows, "No cluster data returned from cluster-view command" + + for row in data_rows: + parts = row.split(",") + tier_value = parts[-1].strip().lower() + + # Accept legacy 'false' values for older clusters + valid_tiers = ("standard", "enterprise", "false") + assert tier_value in valid_tiers, ( + f"Unexpected tier value: {tier_value}. " + f"Expected one of {valid_tiers}." + ) def test_update_kubernetes_cluster(lke_cluster): @@ -160,7 +175,7 @@ def test_view_pool(lke_cluster): ) lines = res.splitlines() - headers = ["type", "labels"] + headers = ["type", "labels", "k8s_version", "label"] assert_headers_in_lines(headers, lines) diff --git a/tests/integration/maintenance/test_maintenance_policies.py b/tests/integration/maintenance/test_maintenance_policies.py new file mode 100644 index 00000000..f5bb86da --- /dev/null +++ b/tests/integration/maintenance/test_maintenance_policies.py @@ -0,0 +1,27 @@ +from tests.integration.helpers import ( + BASE_CMDS, + assert_headers_in_lines, + exec_test_command, +) + + +def test_maintenance_policies_list(): + res = exec_test_command( + BASE_CMDS["maintenance"] + ["policies-list", "--text", "--delimiter=,"] + ) + lines = res.splitlines() + headers = [ + "description", + "is_default", + "label", + "is_default", + "slug", + "type", + ] + assert_headers_in_lines(headers, lines) + rows = [line.split(",") for line in lines[1:] if line.strip()] + + type_index = headers.index("type") + types = [row[type_index].strip() for row in rows] + + assert set(types) == {"migrate", "power_off_on"} diff --git a/tests/integration/monitor/fixtures.py b/tests/integration/monitor/fixtures.py new file mode 100644 index 00000000..c56a92b1 --- /dev/null +++ b/tests/integration/monitor/fixtures.py @@ -0,0 +1,24 @@ +import pytest + +from tests.integration.helpers import ( + BASE_CMDS, + exec_test_command, +) + + +@pytest.fixture +def get_service_type(): + service_ids = exec_test_command( + BASE_CMDS["monitor"] + + [ + "service-list", + "--text", + "--no-headers", + "--delimiter", + ",", + "--format", + "service_type", + ] + ).splitlines() + first_id = service_ids[0].split(",")[0] + yield first_id diff --git a/tests/integration/monitor/test_alerts.py b/tests/integration/monitor/test_alerts.py new file mode 100644 index 00000000..d03471bd --- /dev/null +++ b/tests/integration/monitor/test_alerts.py @@ -0,0 +1,152 @@ +import pytest + +from tests.integration.helpers import ( + BASE_CMDS, + assert_headers_in_lines, + delete_target_id, + exec_test_command, + get_random_text, + retry_exec_test_command_with_delay, +) + + +def test_channels_list(): + res = exec_test_command( + BASE_CMDS["alerts"] + ["channels-list", "--text", "--delimiter=,"] + ) + lines = res.splitlines() + headers = [ + "channel_type", + "content.email.email_addresses", + "id", + "label", + "type", + "updated", + ] + assert_headers_in_lines(headers, lines) + + +@pytest.fixture +def get_channel_id(): + channel_ids = exec_test_command( + BASE_CMDS["alerts"] + + [ + "channels-list", + "--text", + "--no-headers", + "--delimiter", + ",", + "--format", + "id", + ] + ).splitlines() + first_id = channel_ids[0].split(",")[0] + yield first_id + + +def test_alerts_definition_create(get_channel_id, get_service_type): + label = get_random_text(8) + "_alert" + exec_test_command( + BASE_CMDS["alerts"] + + [ + "definition-create", + "--channel_ids", + get_channel_id, + "--label", + label, + "--rule_criteria.rules.metric", + "cpu_usage", + "--rule_criteria.rules.operator", + "eq", + "--rule_criteria.rules.threshold", + "80", + "--rule_criteria.rules.aggregate_function", + "avg", + "--severity", + "1", + "--trigger_conditions.criteria_condition", + "ALL", + "--trigger_conditions.evaluation_period_seconds", + "300", + "--trigger_conditions.polling_interval_seconds", + "300", + "--trigger_conditions.trigger_occurrences", + "3", + get_service_type, + ] + ) + + +def test_alerts_list(): + res = exec_test_command( + BASE_CMDS["alerts"] + + ["definitions-list-all", "--text", "--delimiter=,"] + ) + lines = res.splitlines() + headers = ["class", "created", "label", "severity", "service_type"] + assert_headers_in_lines(headers, lines) + + +@pytest.fixture +def get_alert_id(): + alert_id = exec_test_command( + BASE_CMDS["alerts"] + + [ + "definitions-list-all", + "--text", + "--no-headers", + "--delimiter", + ",", + "--format", + "id", + ] + ).splitlines() + first_id = alert_id[-1] + yield first_id + + +def test_alert_view(get_alert_id, get_service_type): + alert_id = get_alert_id + service_type = get_service_type + res = exec_test_command( + BASE_CMDS["alerts"] + + [ + "definition-view", + service_type, + alert_id, + "--text", + "--delimiter=,", + ] + ) + lines = res.splitlines() + + headers = ["class", "created", "label", "severity", "service_type"] + assert_headers_in_lines(headers, lines) + + +def test_alert_update(get_alert_id, get_service_type): + alert_id = get_alert_id + service_type = get_service_type + new_label = get_random_text(8) + "_updated" + updated_label = retry_exec_test_command_with_delay( + BASE_CMDS["alerts"] + + [ + "definition-update", + service_type, + alert_id, + "--label", + new_label, + "--text", + "--no-headers", + "--format=label", + ], + delay=50, + ) + assert updated_label == new_label + delete_target_id( + target="alerts", + delete_command="definition-delete", + service_type=service_type, + id=alert_id, + use_retry=True, + ) diff --git a/tests/integration/monitor/test_metrics.py b/tests/integration/monitor/test_metrics.py new file mode 100644 index 00000000..38742e18 --- /dev/null +++ b/tests/integration/monitor/test_metrics.py @@ -0,0 +1,119 @@ +import pytest + +from tests.integration.helpers import ( + BASE_CMDS, + assert_headers_in_lines, + exec_test_command, +) + + +def test_dashboard_list(): + res = exec_test_command( + BASE_CMDS["monitor"] + + ["dashboards-list-all", "--text", "--delimiter=,"] + ) + lines = res.splitlines() + headers = ["created", "id", "label", "service_type", "type", "updated"] + assert_headers_in_lines(headers, lines) + + +@pytest.fixture +def get_dashboard_id(): + dashboard_ids = exec_test_command( + BASE_CMDS["monitor"] + + [ + "dashboards-list-all", + "--text", + "--no-headers", + "--delimiter", + ",", + "--format", + "id", + ] + ).splitlines() + first_id = dashboard_ids[0].split(",")[0] + yield first_id + + +def test_dashboard_view(get_dashboard_id): + dashboard_id = get_dashboard_id + res = exec_test_command( + BASE_CMDS["monitor"] + + [ + "dashboards-view", + dashboard_id, + "--text", + "--delimiter=,", + ] + ) + lines = res.splitlines() + + headers = ["created", "id", "label", "service_type", "type", "updated"] + assert_headers_in_lines(headers, lines) + + +def test_service_list(): + res = exec_test_command( + BASE_CMDS["monitor"] + ["service-list", "--text", "--delimiter=,"] + ) + lines = res.splitlines() + headers = ["label", "service_type"] + assert_headers_in_lines(headers, lines) + + +def test_service_view(get_service_type): + dashboard_id = get_service_type + res = exec_test_command( + BASE_CMDS["monitor"] + + [ + "service-view", + dashboard_id, + "--text", + "--delimiter=,", + ] + ) + lines = res.splitlines() + + headers = ["label", "service_type"] + assert_headers_in_lines(headers, lines) + + +def test_dashboard_service_type_list(get_service_type): + dashboard_id = get_service_type + res = exec_test_command( + BASE_CMDS["monitor"] + + [ + "dashboards-list", + dashboard_id, + "--text", + "--delimiter=,", + ] + ) + lines = res.splitlines() + + headers = ["created", "id", "label", "service_type", "type", "updated"] + assert_headers_in_lines(headers, lines) + + +def test_metrics_list(get_service_type): + dashboard_id = get_service_type + res = exec_test_command( + BASE_CMDS["monitor"] + + [ + "metrics-list", + dashboard_id, + "--text", + "--delimiter=,", + ] + ) + lines = res.splitlines() + + headers = [ + "available_aggregate_functions", + "is_alertable", + "label", + "metric", + "metric_type", + "scrape_interval", + ] + assert_headers_in_lines(headers, lines) From 721459b7a326f805cece1ed9920ad2b0131ddb76 Mon Sep 17 00:00:00 2001 From: vshanthe Date: Tue, 9 Sep 2025 12:20:04 +0530 Subject: [PATCH 2/4] fix --- tests/integration/monitor/{fixtures.py => conftest.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/integration/monitor/{fixtures.py => conftest.py} (100%) diff --git a/tests/integration/monitor/fixtures.py b/tests/integration/monitor/conftest.py similarity index 100% rename from tests/integration/monitor/fixtures.py rename to tests/integration/monitor/conftest.py From 9d9802ac1057f444070c3b2c875d30fc4bd7384f Mon Sep 17 00:00:00 2001 From: vshanthe Date: Tue, 9 Sep 2025 12:20:29 +0530 Subject: [PATCH 3/4] fix --- tests/integration/lke/test_clusters.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/integration/lke/test_clusters.py b/tests/integration/lke/test_clusters.py index e5f46361..473eddc7 100644 --- a/tests/integration/lke/test_clusters.py +++ b/tests/integration/lke/test_clusters.py @@ -77,7 +77,15 @@ def test_lke_cluster_list(): ) lines = res.splitlines() - headers = ["label", "k8s_version", "tier", "apl_enabled"] + headers = [ + "label", + "k8s_version", + "tier", + "apl_enabled", + "vpc_id", + "subnet_id", + "stack_type", + ] assert_headers_in_lines(headers, lines) @@ -89,7 +97,15 @@ def test_view_lke_cluster(lke_cluster): + ["cluster-view", cluster_id, "--text", "--delimiter=,"] ) lines = res.splitlines() - headers = ["label", "k8s_version", "tier"] + headers = [ + "label", + "k8s_version", + "tier", + "apl_enabled", + "vpc_id", + "subnet_id", + "stack_type", + ] assert_headers_in_lines(headers, lines) data_rows = lines[1:] assert data_rows, "No cluster data returned from cluster-view command" From cdc166b6f1bdca6c841aa0596bec87414c5551c4 Mon Sep 17 00:00:00 2001 From: vshanthe Date: Wed, 10 Sep 2025 10:28:48 +0530 Subject: [PATCH 4/4] address PR comments --- tests/integration/lke/test_clusters.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/integration/lke/test_clusters.py b/tests/integration/lke/test_clusters.py index 473eddc7..f1796a7d 100644 --- a/tests/integration/lke/test_clusters.py +++ b/tests/integration/lke/test_clusters.py @@ -107,19 +107,6 @@ def test_view_lke_cluster(lke_cluster): "stack_type", ] assert_headers_in_lines(headers, lines) - data_rows = lines[1:] - assert data_rows, "No cluster data returned from cluster-view command" - - for row in data_rows: - parts = row.split(",") - tier_value = parts[-1].strip().lower() - - # Accept legacy 'false' values for older clusters - valid_tiers = ("standard", "enterprise", "false") - assert tier_value in valid_tiers, ( - f"Unexpected tier value: {tier_value}. " - f"Expected one of {valid_tiers}." - ) def test_update_kubernetes_cluster(lke_cluster):