diff --git a/istio/README.md b/istio/README.md index 47161f5c98e7d..926828991a9b2 100644 --- a/istio/README.md +++ b/istio/README.md @@ -31,7 +31,7 @@ instances: send_histograms_buckets: true ``` -The first two endpoints are required for the check to work. See the [istio documentation][4] to learn more about the prometheus adapter. +Each of the endpoints are optional, but at least one must be configured. See the [istio documentation][4] to learn more about the prometheus adapter. ### Validation diff --git a/istio/datadog_checks/istio/data/conf.yaml.example b/istio/datadog_checks/istio/data/conf.yaml.example index b3659bbc8ebab..84384d7e36eba 100644 --- a/istio/datadog_checks/istio/data/conf.yaml.example +++ b/istio/datadog_checks/istio/data/conf.yaml.example @@ -2,7 +2,7 @@ init_config: instances: - ## @param istio_mesh_endpoint - string - required + ## @param istio_mesh_endpoint - string - optional ## To enable Istio metrics you must specify the url exposing the API ## ## Note for RHEL and SUSE users: due to compatibility issues, the check does not make use of @@ -11,7 +11,7 @@ instances: # - istio_mesh_endpoint: http://istio-telemetry.istio-system:42422/metrics - ## @param mixer_endpoint - string - required + ## @param mixer_endpoint - string - optional ## Define the mixer endpoint in order to collect all Prometheus metrics on the Mixer process as well ## as gRPC metrics related to API calls and metrics on adapter dispatch. ## If using Istio < v1.1, replace port 15014 with port 9093. See the changes here: diff --git a/istio/datadog_checks/istio/istio.py b/istio/datadog_checks/istio/istio.py index aaa7677ac3f05..d6eb88bc0ecff 100644 --- a/istio/datadog_checks/istio/istio.py +++ b/istio/datadog_checks/istio/istio.py @@ -27,23 +27,26 @@ def __init__(self, name, init_config, agentConfig, instances=None): def check(self, instance): """ - Process the istio_mesh endpoint, the process_mixer endpoint and optionally the pilot and galley endpoints + Process the istio_mesh, process_mixer, pilot, and galley endpoints associated with this instance. + All the endpoints themselves are optional, but at least one must be passed. """ # Get the config for the istio_mesh instance istio_mesh_endpoint = instance.get('istio_mesh_endpoint') - istio_mesh_config = self.config_map[istio_mesh_endpoint] + if istio_mesh_endpoint: + istio_mesh_config = self.config_map[istio_mesh_endpoint] - # Process istio_mesh - self.process(istio_mesh_config) + # Process istio_mesh + self.process(istio_mesh_config) # Get the config for the process_mixer instance process_mixer_endpoint = instance.get('mixer_endpoint') - process_mixer_config = self.config_map[process_mixer_endpoint] + if process_mixer_endpoint: + process_mixer_config = self.config_map[process_mixer_endpoint] - # Process process_mixer - self.process(process_mixer_config) + # Process process_mixer + self.process(process_mixer_config) # Get the config for the process_pilot instance process_pilot_endpoint = instance.get('pilot_endpoint') @@ -61,15 +64,21 @@ def check(self, instance): # Process process_galley self.process(process_galley_config) + # Check that at least 1 endpoint is configured + if not (process_galley_endpoint or process_pilot_endpoint or process_mixer_endpoint or istio_mesh_endpoint): + raise CheckException("At least one of Mixer, Mesh, Pilot, or Galley endpoints must be configured") + def create_generic_instances(self, instances): """ Generalize each (single) Istio instance into two OpenMetricsBaseCheck instances """ for instance in instances: - istio_mesh_instance = self._create_istio_mesh_instance(instance) - yield istio_mesh_instance - process_mixer_instance = self._create_process_mixer_instance(instance) - yield process_mixer_instance + if 'istio_mesh_endpoint' in instance: + istio_mesh_instance = self._create_istio_mesh_instance(instance) + yield istio_mesh_instance + if 'mixer_endpoint' in instance: + process_mixer_instance = self._create_process_mixer_instance(instance) + yield process_mixer_instance if 'pilot_endpoint' in instance: process_pilot_instance = self._create_process_pilot_instance(instance) yield process_pilot_instance diff --git a/istio/tests/test_check.py b/istio/tests/test_check.py index 1d27308a0049a..483c91ef9dc30 100644 --- a/istio/tests/test_check.py +++ b/istio/tests/test_check.py @@ -368,6 +368,10 @@ 'galley_endpoint': 'http://istio-galley:15014/metrics', } +NEW_MOCK_PILOT_ONLY_INSTANCE = {'pilot_endpoint': 'http://istio-pilot:15014/metrics'} + +NEW_MOCK_GALLEY_ONLY_INSTANCE = {'galley_endpoint': 'http://istio-galley:15014/metrics'} + class MockResponse: """ @@ -419,6 +423,32 @@ def new_mesh_mixture_fixture(): yield +@pytest.fixture +def new_pilot_fixture(): + files = ['pilot.txt'] + responses = [] + for filename in files: + file_path = os.path.join(os.path.dirname(__file__), 'fixtures', '1.1', filename) + with open(file_path, 'r') as f: + responses.append(f.read()) + + with mock.patch('requests.get', return_value=MockResponse(responses, 'text/plain'), __name__="get"): + yield + + +@pytest.fixture +def new_galley_fixture(): + files = ['galley.txt'] + responses = [] + for filename in files: + file_path = os.path.join(os.path.dirname(__file__), 'fixtures', '1.1', filename) + with open(file_path, 'r') as f: + responses.append(f.read()) + + with mock.patch('requests.get', return_value=MockResponse(responses, 'text/plain'), __name__="get"): + yield + + def test_istio(aggregator, mesh_mixture_fixture): """ Test the full check @@ -442,6 +472,26 @@ def test_new_istio(aggregator, new_mesh_mixture_fixture): aggregator.assert_all_metrics_covered() +def test_pilot_only_istio(aggregator, new_pilot_fixture): + check = Istio('istio', {}, {}, [NEW_MOCK_PILOT_ONLY_INSTANCE]) + check.check(NEW_MOCK_PILOT_ONLY_INSTANCE) + + for metric in PILOT_METRICS: + aggregator.assert_metric(metric) + + aggregator.assert_all_metrics_covered() + + +def test_galley_only_istio(aggregator, new_galley_fixture): + check = Istio('istio', {}, {}, [NEW_MOCK_GALLEY_ONLY_INSTANCE]) + check.check(NEW_MOCK_GALLEY_ONLY_INSTANCE) + + for metric in GALLEY_METRICS: + aggregator.assert_metric(metric) + + aggregator.assert_all_metrics_covered() + + def test_scraper_creator(): check = Istio('istio', {}, {}, [MOCK_INSTANCE]) istio_mesh_config = check.config_map.get(MOCK_INSTANCE['istio_mesh_endpoint'])