|
| 1 | +# (C) Datadog, Inc. 2026-present |
| 2 | +# All rights reserved |
| 3 | +# Licensed under a 3-clause BSD style license (see LICENSE) |
| 4 | +import pytest |
| 5 | + |
| 6 | +from datadog_checks.base import ConfigurationError |
| 7 | +from datadog_checks.cilium import CiliumCheck |
| 8 | +from datadog_checks.cilium.check import CiliumCheckV2 |
| 9 | +from datadog_checks.cilium.cilium import CiliumCheck as LegacyCiliumCheck |
| 10 | +from datadog_checks.cilium.metrics import construct_metrics_config |
| 11 | + |
| 12 | +pytestmark = pytest.mark.unit |
| 13 | + |
| 14 | + |
| 15 | +def test_construct_metrics_config_strips_total_suffix(): |
| 16 | + out = construct_metrics_config({"cilium_drop_count_total": "drop_count.total"}) |
| 17 | + assert out == [{"cilium_drop_count": {"name": "drop_count"}}] |
| 18 | + |
| 19 | + |
| 20 | +def test_construct_metrics_config_strips_counter_suffix(): |
| 21 | + out = construct_metrics_config({"cilium_api_counter": "api.counter"}) |
| 22 | + assert out == [{"cilium_api": {"name": "api"}}] |
| 23 | + |
| 24 | + |
| 25 | +def test_construct_metrics_config_passes_through_when_no_suffix(): |
| 26 | + out = construct_metrics_config({"cilium_endpoint": "endpoint.count"}) |
| 27 | + assert out == [{"cilium_endpoint": {"name": "endpoint.count"}}] |
| 28 | + |
| 29 | + |
| 30 | +def test_construct_metrics_config_empty_map_returns_empty_list(): |
| 31 | + assert construct_metrics_config({}) == [] |
| 32 | + |
| 33 | + |
| 34 | +def test_construct_metrics_config_iterates_every_entry(): |
| 35 | + out = construct_metrics_config( |
| 36 | + { |
| 37 | + "a_total": "a.total", |
| 38 | + "b_counter": "b.counter", |
| 39 | + "c": "c", |
| 40 | + } |
| 41 | + ) |
| 42 | + assert len(out) == 3 |
| 43 | + assert {"a": {"name": "a"}} in out |
| 44 | + assert {"b": {"name": "b"}} in out |
| 45 | + assert {"c": {"name": "c"}} in out |
| 46 | + |
| 47 | + |
| 48 | +def test_construct_metrics_config_total_substring_not_stripped(): |
| 49 | + # "_total" must be a suffix; the substring elsewhere stays intact. |
| 50 | + out = construct_metrics_config({"totally_random": "totally.random"}) |
| 51 | + assert out == [{"totally_random": {"name": "totally.random"}}] |
| 52 | + |
| 53 | + |
| 54 | +def test_construct_metrics_config_counter_substring_not_stripped(): |
| 55 | + out = construct_metrics_config({"countermeasure": "countermeasure"}) |
| 56 | + assert out == [{"countermeasure": {"name": "countermeasure"}}] |
| 57 | + |
| 58 | + |
| 59 | +def test_v2_default_metric_limit_is_zero(): |
| 60 | + assert CiliumCheckV2.DEFAULT_METRIC_LIMIT == 0 |
| 61 | + |
| 62 | + |
| 63 | +def test_v2_raises_when_neither_endpoint_set(): |
| 64 | + check = CiliumCheckV2("cilium", {}, [{}]) |
| 65 | + with pytest.raises(ConfigurationError, match="Must specify at least one"): |
| 66 | + check._parse_config() |
| 67 | + |
| 68 | + |
| 69 | +def test_v2_agent_endpoint_only_builds_one_scraper_pointing_at_agent(): |
| 70 | + check = CiliumCheckV2("cilium", {}, [{"agent_endpoint": "http://agent/metrics"}]) |
| 71 | + check._parse_config() |
| 72 | + assert len(check.scraper_configs) == 1 |
| 73 | + assert check.scraper_configs[0]["openmetrics_endpoint"] == "http://agent/metrics" |
| 74 | + |
| 75 | + |
| 76 | +def test_v2_operator_endpoint_only_builds_one_scraper_pointing_at_operator(): |
| 77 | + check = CiliumCheckV2("cilium", {}, [{"operator_endpoint": "http://op/metrics"}]) |
| 78 | + check._parse_config() |
| 79 | + assert len(check.scraper_configs) == 1 |
| 80 | + assert check.scraper_configs[0]["openmetrics_endpoint"] == "http://op/metrics" |
| 81 | + |
| 82 | + |
| 83 | +def test_v2_both_endpoints_build_two_scrapers(): |
| 84 | + check = CiliumCheckV2( |
| 85 | + "cilium", |
| 86 | + {}, |
| 87 | + [{"agent_endpoint": "http://agent/metrics", "operator_endpoint": "http://op/metrics"}], |
| 88 | + ) |
| 89 | + check._parse_config() |
| 90 | + endpoints = sorted(s["openmetrics_endpoint"] for s in check.scraper_configs) |
| 91 | + assert endpoints == ["http://agent/metrics", "http://op/metrics"] |
| 92 | + |
| 93 | + |
| 94 | +def test_v2_agent_scraper_uses_agent_metrics_not_operator(): |
| 95 | + check = CiliumCheckV2("cilium", {}, [{"agent_endpoint": "http://agent/metrics"}]) |
| 96 | + check._parse_config() |
| 97 | + metric_keys = {next(iter(m)) for m in check.scraper_configs[0]["metrics"]} |
| 98 | + assert "cilium_drop_count" in metric_keys |
| 99 | + assert not any(name.startswith("cilium_operator_eni_") for name in metric_keys) |
| 100 | + |
| 101 | + |
| 102 | +def test_v2_operator_scraper_uses_operator_metrics_not_agent(): |
| 103 | + check = CiliumCheckV2("cilium", {}, [{"operator_endpoint": "http://op/metrics"}]) |
| 104 | + check._parse_config() |
| 105 | + metric_keys = {next(iter(m)) for m in check.scraper_configs[0]["metrics"]} |
| 106 | + assert any(name.startswith("cilium_operator_") for name in metric_keys) |
| 107 | + assert "cilium_drop_count" not in metric_keys |
| 108 | + |
| 109 | + |
| 110 | +def test_legacy_default_metric_limit_is_zero(): |
| 111 | + assert LegacyCiliumCheck.DEFAULT_METRIC_LIMIT == 0 |
| 112 | + |
| 113 | + |
| 114 | +def test_legacy_routes_to_v2_when_use_openmetrics_is_truthy(): |
| 115 | + check = CiliumCheck("cilium", {}, [{"agent_endpoint": "http://agent/metrics", "use_openmetrics": True}]) |
| 116 | + assert isinstance(check, CiliumCheckV2) |
| 117 | + |
| 118 | + |
| 119 | +def test_legacy_routes_to_legacy_when_use_openmetrics_is_falsy(): |
| 120 | + check = CiliumCheck("cilium", {}, [{"agent_endpoint": "http://agent/metrics", "use_openmetrics": False}]) |
| 121 | + assert not isinstance(check, CiliumCheckV2) |
| 122 | + |
| 123 | + |
| 124 | +def test_legacy_default_use_openmetrics_routes_to_legacy(): |
| 125 | + # When the key is absent, the get() default must be False so we stay on legacy. |
| 126 | + check = CiliumCheck("cilium", {}, [{"agent_endpoint": "http://agent/metrics"}]) |
| 127 | + assert not isinstance(check, CiliumCheckV2) |
| 128 | + |
| 129 | + |
| 130 | +def test_legacy_new_picks_first_instance_for_routing_legacy_path(): |
| 131 | + check = CiliumCheck( |
| 132 | + "cilium", |
| 133 | + {}, |
| 134 | + [ |
| 135 | + {"agent_endpoint": "http://agent/metrics", "use_openmetrics": False}, |
| 136 | + {"agent_endpoint": "http://agent2/metrics", "use_openmetrics": True}, |
| 137 | + ], |
| 138 | + ) |
| 139 | + assert not isinstance(check, CiliumCheckV2) |
| 140 | + |
| 141 | + |
| 142 | +def test_legacy_new_picks_first_instance_for_routing_v2_path(): |
| 143 | + check = CiliumCheck( |
| 144 | + "cilium", |
| 145 | + {}, |
| 146 | + [ |
| 147 | + {"agent_endpoint": "http://agent/metrics", "use_openmetrics": True}, |
| 148 | + {"agent_endpoint": "http://agent2/metrics", "use_openmetrics": False}, |
| 149 | + ], |
| 150 | + ) |
| 151 | + assert isinstance(check, CiliumCheckV2) |
| 152 | + |
| 153 | + |
| 154 | +def test_legacy_raises_when_both_endpoints_set(): |
| 155 | + with pytest.raises(ConfigurationError, match="Only one endpoint needs to be specified"): |
| 156 | + CiliumCheck( |
| 157 | + "cilium", |
| 158 | + {}, |
| 159 | + [{"agent_endpoint": "http://agent/metrics", "operator_endpoint": "http://op/metrics"}], |
| 160 | + ) |
| 161 | + |
| 162 | + |
| 163 | +def test_legacy_raises_when_neither_endpoint_set(): |
| 164 | + with pytest.raises(ConfigurationError, match="Must provide at least one endpoint"): |
| 165 | + CiliumCheck("cilium", {}, [{}]) |
| 166 | + |
| 167 | + |
| 168 | +def test_legacy_init_uses_first_instance_for_endpoint_choice(): |
| 169 | + check = CiliumCheck( |
| 170 | + "cilium", |
| 171 | + {}, |
| 172 | + [ |
| 173 | + {"agent_endpoint": "http://first/metrics"}, |
| 174 | + {"agent_endpoint": "http://second/metrics"}, |
| 175 | + ], |
| 176 | + ) |
| 177 | + assert check.instance["prometheus_url"] == "http://first/metrics" |
| 178 | + |
| 179 | + |
| 180 | +def test_legacy_operator_endpoint_selects_operator_metric_set(): |
| 181 | + check = CiliumCheck("cilium", {}, [{"operator_endpoint": "http://op/metrics"}]) |
| 182 | + assert check.instance["prometheus_url"] == "http://op/metrics" |
| 183 | + metric_keys = {key for m in check.instance["metrics"] for key in m} |
| 184 | + assert "cilium_operator_process_cpu_seconds_total" in metric_keys |
| 185 | + |
| 186 | + |
| 187 | +def test_legacy_agent_endpoint_selects_agent_metric_set(): |
| 188 | + check = CiliumCheck("cilium", {}, [{"agent_endpoint": "http://agent/metrics"}]) |
| 189 | + assert check.instance["prometheus_url"] == "http://agent/metrics" |
| 190 | + metric_keys = {key for m in check.instance["metrics"] for key in m} |
| 191 | + assert "cilium_drop_count_total" in metric_keys |
| 192 | + assert "cilium_operator_eni_available" not in metric_keys |
| 193 | + |
| 194 | + |
| 195 | +def test_legacy_default_prometheus_timeout_is_10(): |
| 196 | + check = CiliumCheck("cilium", {}, [{"agent_endpoint": "http://agent/metrics"}]) |
| 197 | + assert check.instance["prometheus_timeout"] == 10 |
| 198 | + |
| 199 | + |
| 200 | +def test_legacy_custom_timeout_passes_through(): |
| 201 | + check = CiliumCheck("cilium", {}, [{"agent_endpoint": "http://agent/metrics", "timeout": 42}]) |
| 202 | + assert check.instance["prometheus_timeout"] == 42 |
| 203 | + |
| 204 | + |
| 205 | +def test_legacy_namespace_is_cilium(): |
| 206 | + check = CiliumCheck("cilium", {}, [{"agent_endpoint": "http://agent/metrics"}]) |
| 207 | + assert check.instance["namespace"] == "cilium" |
| 208 | + |
| 209 | + |
| 210 | +def test_legacy_metadata_metric_name_is_cilium_version(): |
| 211 | + check = CiliumCheck("cilium", {}, [{"agent_endpoint": "http://agent/metrics"}]) |
| 212 | + assert check.instance["metadata_metric_name"] == "cilium_version" |
0 commit comments