Skip to content

Commit 3101d3f

Browse files
Collect ArgoCD entities for applications, cluster and repos (DataDog#23917)
* test argocd submission * add unique identifier * better unique identifier * allow list approach * validations * argocd to submit entity metadata * Update 23917.added * remove public plication * Update metadata.csv * use a different separator * pin dcb * remove review files * fix naming * clean up * more knobs * nits * Read genresources config from pydantic and clean up reviewer findings - Defer ArgocdResourceCollector construction to the first check() call so it observes the populated pydantic config (self.config) instead of the raw instance dict; drops the hardcoded TTL / interval / cap literals that were duplicating the spec defaults. - Read collect_genresources from self.config. - Swap the volume-cap log args to read type/fetched/cap in order and use log.exception for the fetch-failure path so tracebacks survive. - Spec wording: ArgoCD (one word) and allowlist / allowlisted per the style guide. - Tests: add a _check() helper that loads the config models and attaches the collector, add coverage for the missing-endpoint path and for credential scrubbing on cluster.connectionState.message, tighten the volume-cap assertion against the new message format, and mark the file as pytest.mark.unit. * Update argocd/datadog_checks/argocd/resources.py Co-authored-by: Kyle Neale <kyle.neale@datadoghq.com> * Update argocd/datadog_checks/argocd/resources.py Co-authored-by: Kyle Neale <kyle.neale@datadoghq.com> * Update argocd/datadog_checks/argocd/resources.py Co-authored-by: Kyle Neale <kyle.neale@datadoghq.com> * Update argocd/datadog_checks/argocd/resources.py Co-authored-by: Kyle Neale <kyle.neale@datadoghq.com> --------- Co-authored-by: Kyle-Neale <kyle.neale@datadoghq.com>
1 parent b905c78 commit 3101d3f

10 files changed

Lines changed: 1099 additions & 2 deletions

File tree

argocd/assets/configuration/spec.yaml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,114 @@ files:
5151
- name: commit_server_endpoint
5252
description: |
5353
Endpoint exposing Commit Server's Prometheus metrics.
54+
fleet_configurable: false
5455
value:
5556
display_default: null
5657
example: http://argocd-commit-server:8087/metrics
5758
type: string
59+
- name: collect_genresources
60+
hidden: true
61+
fleet_configurable: false
62+
description: |
63+
Enable the generic resources pilot collector that ships ArgoCD
64+
Applications, Clusters, and Repositories to Datadog as generic
65+
resources. Disabled by default.
66+
value:
67+
type: boolean
68+
example: false
69+
- name: genresources_endpoint
70+
hidden: true
71+
fleet_configurable: false
72+
description: |
73+
Base URL of the ArgoCD REST API (for example, ``https://argocd.example.com``).
74+
Required when ``collect_genresources`` is set to ``true``.
75+
value:
76+
display_default: null
77+
example: https://<ARGOCD_HOST>
78+
type: string
79+
- name: genresources_auth_token
80+
hidden: true
81+
fleet_configurable: false
82+
description: |
83+
Raw bearer token used to authenticate against the ArgoCD REST API.
84+
When set, the collector adds ``Authorization: Bearer <token>`` to
85+
each REST request. Leave unset to inherit the request authentication
86+
configured on the instance (for example, the structured ``auth_token``
87+
config object handled by the HTTP wrapper).
88+
secret: true
89+
value:
90+
display_default: null
91+
example: <BEARER_TOKEN>
92+
type: string
93+
- name: genresources_ttl_seconds
94+
hidden: true
5895
fleet_configurable: false
96+
description: |
97+
Time-to-live in seconds applied to every emitted resource.
98+
Resources expire ``ttl_seconds`` after the last observation.
99+
Minimum of 1.
100+
value:
101+
type: integer
102+
example: 21600
103+
minimum: 1
104+
- name: genresources_collection_interval_seconds
105+
hidden: true
106+
fleet_configurable: false
107+
description: |
108+
Minimum number of seconds between generic resources collection cycles.
109+
The collector polls the ArgoCD API at most once per interval,
110+
independent of the check's metrics scrape frequency, to limit load on
111+
the ArgoCD API server in large deployments. Minimum of 1.
112+
value:
113+
type: integer
114+
example: 120
115+
minimum: 1
116+
- name: genresources_max_resources_per_cycle
117+
hidden: true
118+
fleet_configurable: false
119+
description: |
120+
Maximum number of items emitted per resource type per check cycle.
121+
When an ArgoCD API endpoint returns more than this, the excess is
122+
dropped and a warning is logged. Applied independently to
123+
Applications, Clusters, and Repositories.
124+
value:
125+
type: integer
126+
example: 10000
127+
minimum: 1
128+
- name: genresources_extra_include_paths
129+
hidden: true
130+
fleet_configurable: false
131+
description: |
132+
Additional dotted JSON paths appended to the baseline allowlist of
133+
every collected resource type. Only enumerated paths are shipped;
134+
anything not listed never leaves the Agent. ``[*]`` denotes array
135+
iteration. A path that does not match any field for a given type is
136+
silently skipped. This list is additive; it can add fields to ship but
137+
cannot remove baseline fields. Each path must resolve to a value, or a
138+
list of values, not a whole object: a path that lands on an object
139+
causes that resource to be dropped, so enumerate leaf fields rather
140+
than their parents.
141+
value:
142+
type: array
143+
items:
144+
type: string
145+
example: []
146+
- name: genresources_exclude_paths
147+
hidden: true
148+
fleet_configurable: false
149+
description: |
150+
Dotted JSON paths to remove from the baseline allowlist of every
151+
collected resource type, applied after ``genresources_extra_include_paths``.
152+
An entry removes any allowlisted path equal to it or nested beneath it;
153+
for example, ``status.history`` drops every ``status.history[*]`` field
154+
while ``status.conditions[*].message`` drops only that leaf. Use this to
155+
stop shipping a field without a code change. Removing fields only ever
156+
ships less, never more.
157+
value:
158+
type: array
159+
items:
160+
type: string
161+
example: []
59162
- template: instances/openmetrics
60163
overrides:
61164
openmetrics_endpoint.required: false

argocd/changelog.d/23917.added

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add a collector that submits ArgoCD applications, clusters, and repositories to Datadog as generic resources.

argocd/datadog_checks/argocd/check.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
NOTIFICATIONS_CONTROLLER_METRICS,
1616
REPO_SERVER_METRICS,
1717
)
18+
from .resources import ArgocdResourceCollector
1819

1920
(
2021
API_SERVER_NAMESPACE,
@@ -40,6 +41,17 @@ def __init__(self, name, init_config, instances):
4041
super(ArgocdCheck, self).__init__(name, init_config, instances)
4142
self.check_initializations.appendleft(self.parse_config)
4243
self.check_initializations.append(self.configure_additional_transformers)
44+
self._resource_collector: ArgocdResourceCollector | None = None
45+
46+
def check(self, instance):
47+
if self.config.collect_genresources:
48+
if self._resource_collector is None:
49+
self._resource_collector = ArgocdResourceCollector(self)
50+
try:
51+
self._resource_collector.collect()
52+
except Exception:
53+
self.log.exception("genresources: collection cycle failed")
54+
super().check(instance)
4355

4456
def parse_config(self):
4557
endpoint_configs = [
@@ -97,7 +109,7 @@ def argocd_cluster_connection_status_transformer(_metric, sample_data, _runtime_
97109
return argocd_cluster_connection_status_transformer
98110

99111
def configure_additional_transformers(self):
100-
endpoints = [key for key in self.instance.keys() if "_endpoint" in key]
112+
endpoints = [key for key in self.instance.keys() if "_endpoint" in key and self.instance[key] in self.scrapers]
101113
for endpoint in endpoints:
102114
if endpoint == "app_controller_endpoint":
103115
self.scrapers[self.instance[endpoint]].metric_transformer.add_custom_transformer(

argocd/datadog_checks/argocd/config_models/defaults.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ def instance_collect_counters_with_distributions():
3636
return False
3737

3838

39+
def instance_collect_genresources():
40+
return False
41+
42+
3943
def instance_collect_histogram_buckets():
4044
return True
4145

@@ -56,6 +60,18 @@ def instance_enable_legacy_tags_normalization():
5660
return True
5761

5862

63+
def instance_genresources_collection_interval_seconds():
64+
return 120
65+
66+
67+
def instance_genresources_max_resources_per_cycle():
68+
return 10000
69+
70+
71+
def instance_genresources_ttl_seconds():
72+
return 21600
73+
74+
5975
def instance_histogram_buckets_as_distributions():
6076
return False
6177

argocd/datadog_checks/argocd/config_models/instance.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class InstanceConfig(BaseModel):
101101
cache_metric_wildcards: Optional[bool] = None
102102
cache_shared_labels: Optional[bool] = None
103103
collect_counters_with_distributions: Optional[bool] = None
104+
collect_genresources: Optional[bool] = None
104105
collect_histogram_buckets: Optional[bool] = None
105106
commit_server_endpoint: Optional[str] = None
106107
connect_timeout: Optional[float] = None
@@ -113,6 +114,13 @@ class InstanceConfig(BaseModel):
113114
exclude_metrics_by_labels: Optional[MappingProxyType[str, Union[bool, tuple[str, ...]]]] = None
114115
extra_headers: Optional[MappingProxyType[str, Any]] = None
115116
extra_metrics: Optional[tuple[Union[str, MappingProxyType[str, Union[str, ExtraMetrics]]], ...]] = None
117+
genresources_auth_token: Optional[str] = None
118+
genresources_collection_interval_seconds: Optional[int] = Field(None, ge=1)
119+
genresources_endpoint: Optional[str] = None
120+
genresources_exclude_paths: Optional[tuple[str, ...]] = None
121+
genresources_extra_include_paths: Optional[tuple[str, ...]] = None
122+
genresources_max_resources_per_cycle: Optional[int] = Field(None, ge=1)
123+
genresources_ttl_seconds: Optional[int] = Field(None, ge=1)
116124
headers: Optional[MappingProxyType[str, Any]] = None
117125
histogram_buckets_as_distributions: Optional[bool] = None
118126
hostname_format: Optional[str] = None

0 commit comments

Comments
 (0)