Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions manifests/agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ manifest:
- declaration: bug (DEBUG-5421)
component_version: ">=7.79.0-devel <7.84.4"
weblog: [chi, echo, fiber-v2-orchestrion, gin, net-http, net-http-orchestrion, uds-echo]
tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_SDK_Identity:
- declaration: missing_feature (Agent forwarding of EVP producer identity requires 7.84)
component_version: '<7.84.0'
tests/integrations/test_open_telemetry.py::Test_MsSql::test_error_exception_event:
- component_version: "<7.75.0-0"
declaration: irrelevant (new behavior implement in 7.75.0)
Expand Down
1 change: 1 addition & 0 deletions manifests/golang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1771,6 +1771,7 @@ manifest:
tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_Absent_Hashed: v2.11.0-dev
tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_False_Hashed: v2.11.0-dev
tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_True_Unhashed: v2.11.0-dev # Easy win for chi, echo, gin, net-http, net-http-orchestrion, uds-echo and version 2.10.0
tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_SDK_Identity: v2.11.0
tests/ffe/test_flag_eval_metrics.py: v2.8.0
tests/ffe/test_flag_eval_metrics.py::Test_FFE_Eval_Metric_Parse_Error_Invalid_Regex: irrelevant (Go validates regex at config load time)
tests/ffe/test_flag_eval_metrics.py::Test_FFE_Eval_Nested_Attributes_Ignored: irrelevant (FFL-1980)
Expand Down
1 change: 1 addition & 0 deletions manifests/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2287,6 +2287,7 @@ manifest:
tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_False_Hashed: missing_feature (FFL-2446)
tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_True_Unhashed: missing_feature (FFL-2446)
tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_Runtime_Default: missing_feature (FFL-2446)
tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_SDK_Identity: v4.16.0
Comment thread
vjfridge marked this conversation as resolved.
Resolved
tests/ffe/test_flag_eval_metrics.py: v4.7.0
tests/ffe/test_flag_eval_metrics.py::Test_FFE_Eval_Nested_Attributes_Ignored: irrelevant (FFL-1980)
tests/integration_frameworks:
Expand Down
9 changes: 9 additions & 0 deletions tests/ffe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ The protobuf fixture `tests/parametric/test_ffe/rules-wire.json` comes from `ope
./run.sh FEATURE_FLAGGING_AND_EXPERIMENTATION --library <language>
```

## SDK identity through the Agent

`Test_FFE_EVP_Flagevaluation_SDK_Identity` checks SDK origin and version headers on
Agent-to-intake captures while retaining the shared payload and evaluation-count checks.
The case requires Agent 7.84+ and is enabled for Go 2.11.0+, Python 4.16.0+, and
Node.js express5 under its existing version gate. Other languages retain their
current exclusions. Event-level `source` is rejected: source identity belongs in
the request headers.

## Per-subject evaluation telemetry opt-out

All four test classes report under [#649: Per-subject evaluation telemetry opt-out](https://feature-parity.us1.prod.dog/#/?feature=649).
Expand Down
22 changes: 22 additions & 0 deletions tests/ffe/test_flag_eval_evp.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from tests.ffe.utils.evp import (
assert_agentless_evp_intake_request,
assert_agentless_evp_topology,
assert_evp_sdk_identity,
feature_flagging_evp_egress,
register_expected_evp_capture,
)
Expand Down Expand Up @@ -163,6 +164,7 @@ def assert_batch_context(batch: JSON) -> None:

def assert_event_contract(event: JSON, flag_key: str) -> None:
assert_no_reason_field(event)
assert "source" not in event, "SDK identity belongs in EVP headers, not event-level source"
assert "targeting_rule" not in event, "targeting_rule must be omitted without real rule metadata"

flag = event.get("flag")
Expand Down Expand Up @@ -322,6 +324,26 @@ class Test_FFE_EVP_Flagevaluation_Egress_Datadog_Agent(FlagevaluationEgressContr
pass


@scenarios.feature_flagging_and_experimentation
@features.feature_flags_evp_flagevaluation
class Test_FFE_EVP_Flagevaluation_SDK_Identity(FlagevaluationEgressContract):
# Agent 7.84+ forwards producer headers; the Agent manifest gates this case.
# Library manifests separately gate SDK support. The Agent egress test retains
# payload coverage on older Agents wherever enabled by the library manifest.
flag_key = "evp-sdk-identity-flag"

def test_ffe_evp_flagevaluation_egress(self) -> None:
super().test_ffe_evp_flagevaluation_egress()
# interfaces.agent captures Agent-to-intake traffic, after forwarding.
for data in interfaces.agent.get_data(path_filters=EVP_FLAGEVALUATIONS_PATH):
if evp_flagevaluation_events_from_data(cast("JSON", data), self.flag_key, self.targeting_key):
assert_evp_sdk_identity(
cast("JSON", data),
library_name=context.library.name,
library_version=context.library.raw_version,
)


@scenario_crash
@scenarios.feature_flagging_and_experimentation_agentless_direct
@features.feature_flags_evp_flagevaluation
Expand Down
17 changes: 10 additions & 7 deletions tests/ffe/utils/evp.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"golang": "dd-trace-go",
"java": "dd-trace-java",
"nodejs": "dd-trace-js",
"php": "dd-trace-php",
Comment thread
vjfridge marked this conversation as resolved.
Resolved
"python": "dd-trace-py",
"ruby": "dd-trace-rb",
}
Expand Down Expand Up @@ -134,7 +135,7 @@ def expected_evp_origin(library_name: str) -> str:


def expected_evp_origin_version(library_name: str, library_version: str) -> str:
"""Return the tracer's canonical wire version for direct EVP identity.
"""Return the tracer's canonical wire version for EVP identity.

System-tests deliberately removes Go's leading ``v`` when it parses the
component version for manifest comparisons. dd-trace-go's public version
Expand All @@ -161,6 +162,13 @@ def _assert_single_header(data: dict[str, Any], name: str, expected: str | set[s
assert values[0] in allowed, f"expected {name} in {sorted(allowed)!r}, got {values[0]!r}"


def assert_evp_sdk_identity(data: dict[str, Any], *, library_name: str, library_version: str) -> None:
"""Require the SDK source identity on an intake-side capture."""
_assert_single_header(data, "DD-EVP-ORIGIN", expected_evp_origin(library_name))
assert library_version.strip(), "expected library version must be non-empty"
_assert_single_header(data, "DD-EVP-ORIGIN-VERSION", expected_evp_origin_version(library_name, library_version))


def assert_agentless_evp_intake_request(
data: dict[str, Any],
*,
Expand Down Expand Up @@ -197,12 +205,7 @@ def assert_agentless_evp_intake_request(
# SDK local-route identity and credential omission remain covered by SDK unit tests.
return

_assert_single_header(data, "DD-EVP-ORIGIN", expected_evp_origin(library_name))
_assert_single_header(
data,
"DD-EVP-ORIGIN-VERSION",
expected_evp_origin_version(library_name, library_version),
)
assert_evp_sdk_identity(data, library_name=library_name, library_version=library_version)


def assert_no_evp_proxy_requests(captures: Iterable[dict[str, Any]]) -> None:
Expand Down
Loading