ddapm-test-agent 1.33.0__py3-none-any.whl → 1.34.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
1
  def _get_version():
2
- import pkg_resources # type: ignore[import-untyped]
2
+ import pkg_resources
3
3
 
4
4
  return pkg_resources.get_distribution(__name__).version
ddapm_test_agent/agent.py CHANGED
@@ -133,7 +133,7 @@ def _session_token(request: Request) -> Optional[str]:
133
133
 
134
134
  async def _vcr_proxy_cassette_prefix(request: Request) -> Optional[str]:
135
135
  try:
136
- request_body: dict[str, str] = await request.json()
136
+ request_body: Dict[str, str] = await request.json()
137
137
  requested_test_name = request_body.get("test_name")
138
138
  return requested_test_name
139
139
  except (json.JSONDecodeError, UnicodeDecodeError):
@@ -165,7 +165,7 @@ async def handle_exception_middleware(request: Request, handler: _Handler) -> we
165
165
 
166
166
  async def _forward_request(
167
167
  request_data: bytes, headers: Mapping[str, str], full_agent_url: str
168
- ) -> tuple[ClientResponse, str]:
168
+ ) -> Tuple[ClientResponse, str]:
169
169
  async with ClientSession() as session:
170
170
  async with session.post(
171
171
  full_agent_url,
@@ -831,7 +831,6 @@ class Agent:
831
831
  "peer_tags": ["db.name", "mongodb.db", "messaging.system"],
832
832
  "span_events": True, # Advertise support for the top-level Span field for Span Events
833
833
  },
834
- headers={"Datadog-Agent-State": "03e868b3ecdd62a91423cc4c3917d0d151fb9fa486736911ab7f5a0750c63824"},
835
834
  )
836
835
 
837
836
  async def _handle_traces(self, request: Request, version: Literal["v0.4", "v0.5", "v0.7", "v1"]) -> web.Response:
@@ -929,10 +928,9 @@ class Agent:
929
928
  # Get the span attributes that are to be removed for this snapshot.
930
929
  default_attribute_regex_replaces: Dict[str, str] = request.app["snapshot_regex_placeholders"]
931
930
  regex_overrides = _parse_map(request.url.query.get("regex_placeholders", ""))
932
- attribute_regex_replaces = dict(
933
- (f"{{{key}}}", re.compile(regex))
934
- for (key, regex) in (default_attribute_regex_replaces | regex_overrides).items()
935
- )
931
+ regex_replaces = default_attribute_regex_replaces.copy()
932
+ regex_replaces.update(regex_overrides)
933
+ attribute_regex_replaces = dict((f"{{{key}}}", re.compile(regex)) for (key, regex) in regex_replaces.items())
936
934
  log.info("using regex placeholders %r", attribute_regex_replaces)
937
935
 
938
936
  if "span_id" in span_removes:
@@ -1357,6 +1355,7 @@ def make_app(
1357
1355
  snapshot_removed_attrs: List[str],
1358
1356
  snapshot_regex_placeholders: Dict[str, str],
1359
1357
  vcr_cassettes_directory: str,
1358
+ vcr_ci_mode: bool,
1360
1359
  ) -> web.Application:
1361
1360
  agent = Agent()
1362
1361
  app = web.Application(
@@ -1418,7 +1417,7 @@ def make_app(
1418
1417
  web.route(
1419
1418
  "*",
1420
1419
  "/vcr/{path:.*}",
1421
- lambda request: proxy_request(request, vcr_cassettes_directory),
1420
+ lambda request: proxy_request(request, vcr_cassettes_directory, vcr_ci_mode),
1422
1421
  ),
1423
1422
  ]
1424
1423
  )
@@ -1583,6 +1582,12 @@ def main(args: Optional[List[str]] = None) -> None:
1583
1582
  default=os.environ.get("VCR_CASSETTES_DIRECTORY", os.path.join(os.getcwd(), "vcr-cassettes")),
1584
1583
  help="Directory to read and store third party API cassettes.",
1585
1584
  )
1585
+ parser.add_argument(
1586
+ "--vcr-ci-mode",
1587
+ type=bool,
1588
+ default=os.environ.get("VCR_CI_MODE", False),
1589
+ help="Will change the test agent to record VCR cassettes in CI mode, throwing an error if a cassette is not found on /vcr/{provider}",
1590
+ )
1586
1591
  parsed_args = parser.parse_args(args=args)
1587
1592
  logging.basicConfig(level=parsed_args.log_level)
1588
1593
 
@@ -1626,6 +1631,7 @@ def main(args: Optional[List[str]] = None) -> None:
1626
1631
  snapshot_removed_attrs=parsed_args.snapshot_removed_attrs,
1627
1632
  snapshot_regex_placeholders=parsed_args.snapshot_regex_placeholders,
1628
1633
  vcr_cassettes_directory=parsed_args.vcr_cassettes_directory,
1634
+ vcr_ci_mode=parsed_args.vcr_ci_mode,
1629
1635
  )
1630
1636
 
1631
1637
  # Validate port configuration
@@ -3,13 +3,13 @@ import json
3
3
  import logging
4
4
  import operator
5
5
  import pprint
6
- from re import Pattern
7
6
  import textwrap
8
7
  from typing import Any
9
8
  from typing import Dict
10
9
  from typing import List
11
10
  from typing import Optional
12
11
  from typing import OrderedDict as OrderedDictType
12
+ from typing import Pattern
13
13
  from typing import Set
14
14
  from typing import Tuple
15
15
  from typing import cast
@@ -188,9 +188,13 @@ def _match_traces(t1s: List[Trace], t2s: List[Trace]) -> List[Tuple[Trace, Trace
188
188
  return matches
189
189
 
190
190
 
191
- def _diff_spans(
192
- s1: Span, s2: Span, ignored: Set[str]
193
- ) -> Tuple[List[str], List[str], List[str], List[Tuple[List[str], List[str]]], List[Tuple[List[str], List[str]]]]:
191
+ def _diff_spans(s1: Span, s2: Span, ignored: Set[str]) -> Tuple[
192
+ List[str],
193
+ List[str],
194
+ List[str],
195
+ List[Tuple[List[str], List[str]]],
196
+ List[Tuple[List[str], List[str]]],
197
+ ]:
194
198
  """Return differing attributes between two spans and their meta/metrics maps.
195
199
 
196
200
  It is assumed that the spans have passed through preliminary validation
@@ -255,7 +259,11 @@ def _diff_spans(
255
259
  )
256
260
  link_diff = []
257
261
  for d1, d2, ign in [
258
- (l1_no_tags, l2_no_tags, set(i[11:] for i in ignored if i.startswith("span_links."))),
262
+ (
263
+ l1_no_tags,
264
+ l2_no_tags,
265
+ set(i[11:] for i in ignored if i.startswith("span_links.")),
266
+ ),
259
267
  (
260
268
  l1.get("attributes") or {},
261
269
  l2.get("attributes") or {},
@@ -288,7 +296,11 @@ def _diff_spans(
288
296
  )
289
297
  event_diff = []
290
298
  for d1, d2, ign in [
291
- (l1_no_tags, l2_no_tags, set(i[12:] for i in ignored if i.startswith("span_events."))),
299
+ (
300
+ l1_no_tags,
301
+ l2_no_tags,
302
+ set(i[12:] for i in ignored if i.startswith("span_events.")),
303
+ ),
292
304
  (
293
305
  e1.get("attributes") or {},
294
306
  e2.get("attributes") or {},
@@ -307,7 +319,13 @@ def _diff_spans(
307
319
  results.append(event_diffs) # type: ignore
308
320
 
309
321
  return cast(
310
- Tuple[List[str], List[str], List[str], List[Tuple[List[str], List[str]]], List[Tuple[List[str], List[str]]]],
322
+ Tuple[
323
+ List[str],
324
+ List[str],
325
+ List[str],
326
+ List[Tuple[List[str], List[str]]],
327
+ List[Tuple[List[str], List[str]]],
328
+ ],
311
329
  tuple(results),
312
330
  )
313
331
 
@@ -334,9 +352,13 @@ def _compare_traces(expected: Trace, received: Trace, ignored: Set[str]) -> None
334
352
  ) as frame:
335
353
  frame.add_item(f"Expected span:\n{pprint.pformat(s_exp)}")
336
354
  frame.add_item(f"Received span:\n{pprint.pformat(s_rec)}")
337
- top_level_diffs, meta_diffs, metrics_diffs, span_link_diffs, span_event_diffs = _diff_spans(
338
- s_exp, s_rec, ignored
339
- )
355
+ (
356
+ top_level_diffs,
357
+ meta_diffs,
358
+ metrics_diffs,
359
+ span_link_diffs,
360
+ span_event_diffs,
361
+ ) = _diff_spans(s_exp, s_rec, ignored)
340
362
 
341
363
  for diffs, diff_type, d_exp, d_rec in [
342
364
  (top_level_diffs, "span", s_exp, s_rec),
@@ -363,7 +385,12 @@ def _compare_traces(expected: Trace, received: Trace, ignored: Set[str]) -> None
363
385
 
364
386
  for i, (link_level_diffs, attribute_diffs) in enumerate(span_link_diffs):
365
387
  for diffs, diff_type, d_exp, d_rec in [
366
- (link_level_diffs, f"{i}", s_exp["span_links"][i], s_rec["span_links"][i]),
388
+ (
389
+ link_level_diffs,
390
+ f"{i}",
391
+ s_exp["span_links"][i],
392
+ s_rec["span_links"][i],
393
+ ),
367
394
  (
368
395
  attribute_diffs,
369
396
  f"{i} attributes",
@@ -387,7 +414,12 @@ def _compare_traces(expected: Trace, received: Trace, ignored: Set[str]) -> None
387
414
 
388
415
  for i, (event_level_diffs, attribute_diffs) in enumerate(span_event_diffs):
389
416
  for diffs, diff_type, d_exp, d_rec in [
390
- (event_level_diffs, f"{i}", s_exp["span_events"][i], s_rec["span_events"][i]),
417
+ (
418
+ event_level_diffs,
419
+ f"{i}",
420
+ s_exp["span_events"][i],
421
+ s_rec["span_events"][i],
422
+ ),
391
423
  (
392
424
  attribute_diffs,
393
425
  f"{i} attributes",
@@ -545,4 +577,7 @@ def generate_snapshot(
545
577
  removed: Optional[List[str]] = None,
546
578
  attribute_regex_replaces: Optional[Dict[str, Pattern[str]]] = None,
547
579
  ) -> str:
548
- return _snapshot_json(_normalize_traces(received_traces, attribute_regex_replaces or {}), removed or [])
580
+ return _snapshot_json(
581
+ _normalize_traces(received_traces, attribute_regex_replaces or {}),
582
+ removed or [],
583
+ )
@@ -146,7 +146,7 @@ def generate_cassette_name(path: str, method: str, body: bytes, vcr_cassette_pre
146
146
  )
147
147
 
148
148
 
149
- async def proxy_request(request: Request, vcr_cassettes_directory: str) -> Response:
149
+ async def proxy_request(request: Request, vcr_cassettes_directory: str, vcr_ci_mode: bool) -> Response:
150
150
  path = request.match_info["path"]
151
151
  if request.query_string:
152
152
  path = path + "?" + request.query_string
@@ -159,14 +159,22 @@ async def proxy_request(request: Request, vcr_cassettes_directory: str) -> Respo
159
159
  if provider not in PROVIDER_BASE_URLS:
160
160
  return Response(body=f"Unsupported provider: {provider}", status=400)
161
161
 
162
- target_url = url_path_join(PROVIDER_BASE_URLS[provider], remaining_path)
163
-
164
- headers = {key: value for key, value in request.headers.items() if key != "Host"}
165
-
166
162
  body_bytes = await request.read()
167
163
 
168
164
  vcr_cassette_prefix = request.pop("vcr_cassette_prefix", None)
169
165
  cassette_name = generate_cassette_name(path, request.method, body_bytes, vcr_cassette_prefix)
166
+ cassette_file_name = f"{cassette_name}.yaml"
167
+ cassette_file_path = os.path.join(vcr_cassettes_directory, provider, cassette_file_name)
168
+ cassette_exists = os.path.exists(cassette_file_path)
169
+
170
+ if vcr_ci_mode and not cassette_exists:
171
+ return Response(
172
+ body=f"Cassette {cassette_file_name} not found while running in CI mode. Please generate the cassette locally and commit it.",
173
+ status=500,
174
+ )
175
+
176
+ target_url = url_path_join(PROVIDER_BASE_URLS[provider], remaining_path)
177
+ headers = {key: value for key, value in request.headers.items() if key != "Host"}
170
178
 
171
179
  request_kwargs: Dict[str, Any] = {
172
180
  "method": request.method,
@@ -178,7 +186,7 @@ async def proxy_request(request: Request, vcr_cassettes_directory: str) -> Respo
178
186
  "stream": True,
179
187
  }
180
188
 
181
- if provider in AWS_SERVICES and not os.path.exists(os.path.join(vcr_cassettes_directory, provider, cassette_name)):
189
+ if provider in AWS_SERVICES and not cassette_exists:
182
190
  if not AWS_SECRET_ACCESS_KEY:
183
191
  return Response(
184
192
  body="AWS_SECRET_ACCESS_KEY environment variable not set for aws signature recalculation",
@@ -192,7 +200,7 @@ async def proxy_request(request: Request, vcr_cassettes_directory: str) -> Respo
192
200
  auth = AWS4Auth(aws_access_key, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_SERVICES[provider])
193
201
  request_kwargs["auth"] = auth
194
202
 
195
- with get_vcr(provider, vcr_cassettes_directory).use_cassette(f"{cassette_name}.yaml"):
203
+ with get_vcr(provider, vcr_cassettes_directory).use_cassette(cassette_file_name):
196
204
  provider_response = requests.request(**request_kwargs)
197
205
 
198
206
  # Extract content type without charset
@@ -1,15 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddapm-test-agent
3
- Version: 1.33.0
3
+ Version: 1.34.0
4
4
  Summary: Test agent for Datadog APM client libraries
5
5
  Home-page: https://github.com/Datadog/dd-apm-test-agent
6
6
  Author: Kyle Verhoog
7
7
  Author-email: kyle@verhoog.ca
8
8
  License: BSD 3
9
9
  Classifier: Programming Language :: Python
10
- Classifier: Programming Language :: Python :: 3.8
11
- Classifier: Programming Language :: Python :: 3.9
12
- Classifier: Programming Language :: Python :: 3.10
13
10
  Classifier: Programming Language :: Python :: 3.11
14
11
  Classifier: Programming Language :: Python :: 3.12
15
12
  Classifier: Programming Language :: Python :: 3.13
@@ -64,11 +61,6 @@ See the [Development](#development) section for how to get the test agent runnin
64
61
 
65
62
  ## Installation
66
63
 
67
- The test agent can be installed using [nix](https://docs.determinate.systems/getting-started/):
68
-
69
- nix profile install github:datadog/dd-apm-test-agent#ddapm-test-agent
70
- # nix profile upgrade ddapm-test-agent # to upgrade
71
-
72
64
  The test agent can be installed from PyPI:
73
65
 
74
66
  pip install ddapm-test-agent
@@ -248,6 +240,10 @@ And pass in a valid API key (if needed) in the way that provider expects.
248
240
 
249
241
  To redact api keys, modify the `filter_headers` list in the `get_vcr` function in `ddapm_test_agent/vcr_proxy.py`. This can be confirmed by viewing cassettes in the `vcr-cassettes` directory (or the otherwise specified directory), and verifying that any new cassettes do not contain the api key.
250
242
 
243
+ #### Running in CI
244
+
245
+ To have the vcr proxy throw a 404 error if a cassette is not found in CI mode to ensure that all cassettes are generated locally and committed, set the `VCR_CI_MODE` environment variable or the `--vcr-ci-mode` flag in the cli tool to `true` (this value defaults to `false`).
246
+
251
247
  ## Configuration
252
248
 
253
249
  The test agent can be configured via command-line options or via environment variables.
@@ -1,5 +1,5 @@
1
- ddapm_test_agent/__init__.py,sha256=hJBQduemued6MAWH6Uk2gqJx2vb0_dd0UO52pJ83nLM,138
2
- ddapm_test_agent/agent.py,sha256=S5VzW5yvBQKqsGj50Xqb_u8gXvRnZguwWTZXn6BcX-M,73960
1
+ ddapm_test_agent/__init__.py,sha256=IEYMDM-xI0IoHYSYw4Eva5263puB_crrrbLstOCScRw,106
2
+ ddapm_test_agent/agent.py,sha256=Wt1L-z9g8pSc22FWa6rGfUaTpGIk7_GBwLl3c94RD6o,74240
3
3
  ddapm_test_agent/apmtelemetry.py,sha256=w_9-yUDh1dgox-FfLqeOHU2C14GcjOjen-_SVagiZrc,861
4
4
  ddapm_test_agent/checks.py,sha256=pBa4YKZQVA8qaTVJ_XgMA6TmlUZNh99YOrCFJA7fwo0,6865
5
5
  ddapm_test_agent/client.py,sha256=ViEmiRX9Y3SQ-KBhSc-FdzBmIVIe8Ij9jj-Q6VGyzLY,7359
@@ -12,15 +12,15 @@ ddapm_test_agent/metrics.py,sha256=EZo7lSec2oAiH7tUqavKZ2MJM7TwbuFGE3AT3cXwmSM,3
12
12
  ddapm_test_agent/remoteconfig.py,sha256=_QjYUKc3JF31DxdvISDXgslm5WVnYWAw0hyckWuLc1c,3606
13
13
  ddapm_test_agent/trace.py,sha256=t0OR8w3NcZK-EOOoadgPITiZqS5tAJGtxqLVGLEw7Kg,45816
14
14
  ddapm_test_agent/trace_checks.py,sha256=bRg2eLKoHROXIFJRbujMUn0T3x1X8pZso-j8wXNomec,9972
15
- ddapm_test_agent/trace_snapshot.py,sha256=g2MhKi8UE-Wsf6PtuzPoXymcW-cYRUnvj63SP9FETJs,22354
15
+ ddapm_test_agent/trace_snapshot.py,sha256=ayOUcCFo6xyotFRm0fSNdeA91_T447W5ShlkRvi0nZE,22932
16
16
  ddapm_test_agent/tracerflare.py,sha256=uoSjhPCOKZflgJn5JLv1Unh4gUdAR1-YbC9_1n1iH9w,954
17
17
  ddapm_test_agent/tracestats.py,sha256=q_WQZnh2kXSSN3fRIBe_0jMYCBQHcaS3fZmJTge4lWc,2073
18
18
  ddapm_test_agent/tracestats_snapshot.py,sha256=VsB6MVnHPjPWHVWnnDdCXJcVKL_izKXEf9lvJ0qbjNQ,3609
19
- ddapm_test_agent/vcr_proxy.py,sha256=qnzLD5f1LcKIcLFmqU0n5fzcfWTIt0m173pbpBfeXK4,6745
20
- ddapm_test_agent-1.33.0.dist-info/licenses/LICENSE.BSD3,sha256=J9S_Tq-hhvteDV2W8R0rqht5DZHkmvgdx3gnLZg4j6Q,1493
21
- ddapm_test_agent-1.33.0.dist-info/licenses/LICENSE.apache2,sha256=5V2RruBHZQIcPyceiv51DjjvdvhgsgS4pnXAOHDuZkQ,11342
22
- ddapm_test_agent-1.33.0.dist-info/METADATA,sha256=hxIxnr5yrlh06q1YfaKgeX7RcQUx0XzC7nM2pgHQQQ4,28483
23
- ddapm_test_agent-1.33.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
- ddapm_test_agent-1.33.0.dist-info/entry_points.txt,sha256=ulayVs6YJ-0Ej2kxbwn39wOHDVXbyQgFgsbRQmXydcs,250
25
- ddapm_test_agent-1.33.0.dist-info/top_level.txt,sha256=A9jiKOrrg6VjFAk-mtlSVYN4wr0VsZe58ehGR6IW47U,17
26
- ddapm_test_agent-1.33.0.dist-info/RECORD,,
19
+ ddapm_test_agent/vcr_proxy.py,sha256=2OmwCxd9kGBxRGoM7wYQx2H8XiRM3ADsrFlpMsBNfTs,7137
20
+ ddapm_test_agent-1.34.0.dist-info/licenses/LICENSE.BSD3,sha256=J9S_Tq-hhvteDV2W8R0rqht5DZHkmvgdx3gnLZg4j6Q,1493
21
+ ddapm_test_agent-1.34.0.dist-info/licenses/LICENSE.apache2,sha256=5V2RruBHZQIcPyceiv51DjjvdvhgsgS4pnXAOHDuZkQ,11342
22
+ ddapm_test_agent-1.34.0.dist-info/METADATA,sha256=XRVBSQse9hAtcu0cWS8ccFXErpUfXc_B2M9bYPAp5rM,28400
23
+ ddapm_test_agent-1.34.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
+ ddapm_test_agent-1.34.0.dist-info/entry_points.txt,sha256=ulayVs6YJ-0Ej2kxbwn39wOHDVXbyQgFgsbRQmXydcs,250
25
+ ddapm_test_agent-1.34.0.dist-info/top_level.txt,sha256=A9jiKOrrg6VjFAk-mtlSVYN4wr0VsZe58ehGR6IW47U,17
26
+ ddapm_test_agent-1.34.0.dist-info/RECORD,,