Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented Misc Contract Tests #163

Merged
merged 19 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
38 changes: 27 additions & 11 deletions contract-tests/tests/test/amazon/misc/configuration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@
from amazon.utils.application_signals_constants import ERROR_METRIC, FAULT_METRIC, LATENCY_METRIC
from opentelemetry.sdk.metrics.export import AggregationTemporality

# Tests in this class are supposed to validate that the SDK was configured in the correct way: It
# uses the X-Ray ID format. Metrics are deltaPreferred. Type of the metrics are exponentialHistogram

thpierce marked this conversation as resolved.
Show resolved Hide resolved
class ResourceAttributesTest(ContractTestBase):

class ConfigurationTest(ContractTestBase):
@override
def get_application_image_name(self) -> str:
return "aws-application-signals-tests-django-app"
Expand All @@ -34,27 +37,37 @@ def test_configuration_metrics(self):
metrics: List[ResourceScopeMetric] = self.mock_collector_client.get_metrics(
{LATENCY_METRIC, ERROR_METRIC, FAULT_METRIC}
)
self._assert_metric_configuration(metrics, metric_name="Error")
self._assert_metric_configuration(metrics, metric_name="Fault")
self._assert_metric_configuration(metrics, metric_name="Latency")
self._assert_metric_configuration(metrics)

def _assert_metric_configuration(self, metrics: List[ResourceScopeMetric], metric_name: str):
def _assert_metric_configuration(self, metrics: List[ResourceScopeMetric]):
for metric in metrics:
if metric.metric.name == metric_name:
self.assertIsNotNone(metric.metric.exponential_histogram)
self.assertEqual(
metric.metric.exponential_histogram.aggregation_temporality, AggregationTemporality.DELTA
)
self.assertIsNotNone(metric.metric.exponential_histogram)
self.assertEqual(metric.metric.exponential_histogram.aggregation_temporality, AggregationTemporality.DELTA)

def test_xray_id_format(self):
thpierce marked this conversation as resolved.
Show resolved Hide resolved

# We are testing here that the X-Ray id format is always used by inspecting the traceid that
# was in the span received by the collector, which should be consistent across multiple spans.
# We are testing the following properties:
# 1. Traceid is random
# 2. First 32 bits of traceid is a timestamp
# It is important to remember that the X-Ray traceId format had to be adapted to fit into the
# definition of the OpenTelemetry traceid:
# https://opentelemetry.io/docs/specs/otel/trace/api/#retrieving-the-traceid-and-spanid
# Specifically for an X-Ray traceid to be a valid Otel traceId, the version digit had to be
# dropped. Reference:
# https://github.com/open-telemetry/opentelemetry-java-contrib/blob/main/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/AwsXrayIdGenerator.java#L45

thpierce marked this conversation as resolved.
Show resolved Hide resolved
seen: List[str] = []
for _ in range(20):
for _ in range(100):
address: str = self.application.get_container_host_ip()
port: str = self.application.get_exposed_port(self.get_application_port())
url: str = f"http://{address}:{port}/success"
response: Response = request("GET", url, timeout=20)
self.assertEqual(200, response.status_code)

# Since we just made the request, the time in epoch registered in the traceid should be
# approximate equal to the current time in the test, since both run on the same host.
start_time_sec: int = int(time.time())

resource_scope_spans: List[ResourceScopeSpan] = self.mock_collector_client.get_traces()
Expand All @@ -64,6 +77,9 @@ def test_xray_id_format(self):
self.assertTrue(target_span.span.trace_id.hex() not in seen)
seen.append(target_span.span.trace_id.hex())

# trace_id is bytes, so we convert it to hex string and pick the first 8 byte
# that represent the timestamp, then convert it to int for timestamp in second

thpierce marked this conversation as resolved.
Show resolved Hide resolved
trace_id_time_stamp_int: int = int(target_span.span.trace_id.hex()[:8], 16)
self.assertGreater(trace_id_time_stamp_int, start_time_sec - 60)
self.assertGreater(start_time_sec + 60, trace_id_time_stamp_int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ def _get_k8s_attributes():
}


# Tests consuming this class are supposed to validate that the agent is able to get the resource
# attributes through the environment variables OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME
#
# These tests are structured with nested classes since it is only possible to change the
# resource attributes during the initialization of the OpenTelemetry SDK.


class ResourceAttributesTest(ContractTestBase):
thpierce marked this conversation as resolved.
Show resolved Hide resolved
@override
def get_application_image_name(self) -> str:
Expand All @@ -34,13 +41,13 @@ def get_application_wait_pattern(self) -> str:
def get_application_extra_environment_variables(self):
return {"DJANGO_SETTINGS_MODULE": "django_server.settings"}

def do_misc_test_request(self, pattern):
def do_test_resource_attributes(self, service_name):
address: str = self.application.get_container_host_ip()
port: str = self.application.get_exposed_port(self.get_application_port())
url: str = f"http://{address}:{port}/success"
response: Response = request("GET", url, timeout=20)
self.assertEqual(200, response.status_code)
self.assert_resource_attributes(pattern)
self.assert_resource_attributes(service_name)

def assert_resource_attributes(self, service_name):
resource_scope_spans: List[ResourceScopeSpan] = self.mock_collector_client.get_traces()
Expand All @@ -63,6 +70,7 @@ def assert_resource_attributes(self, service_name):
for resource_scope_metric in metrics:
if resource_scope_metric.metric.name in ["Error", "Fault", "Latency"]:
target_metrics.append(resource_scope_metric.resource_metrics)
self.assertEqual(len(target_metrics), 3)
for target_metric in target_metrics:
thpierce marked this conversation as resolved.
Show resolved Hide resolved
metric_attributes_dict: Dict[str, AnyValue] = self._get_attributes_dict(target_metric.resource.attributes)
for key, value in _get_k8s_attributes().items():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
from typing_extensions import override


class ServiceNameInResourceAttributesTest(ResourceAttributesTest):
class ServiceNameInEnvVarTest(ResourceAttributesTest):

@override
# pylint: disable=no-self-use
def get_application_extra_environment_variables(self) -> str:
return {"DJANGO_SETTINGS_MODULE": "django_server.settings", "OTEL_SERVICE_NAME": "service-name-test"}

@override
# pylint: disable=no-self-use
Expand All @@ -17,4 +22,4 @@ def get_application_otel_resource_attributes(self) -> str:
return ",".join(pairlist)

def test_service(self) -> None:
self.do_misc_test_request("unknown_service")
self.do_test_resource_attributes("service-name-test")
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ def get_application_otel_resource_attributes(self) -> str:
return ",".join(pairlist)

def test_service(self) -> None:
self.do_misc_test_request("service-name")
self.do_test_resource_attributes("service-name")
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@
from typing_extensions import override


class ServiceNameInResourceAttributesTest(ResourceAttributesTest):

@override
# pylint: disable=no-self-use
def get_application_extra_environment_variables(self) -> str:
return {"DJANGO_SETTINGS_MODULE": "django_server.settings", "OTEL_SERVICE_NAME": "service-name-test"}
class UnknownServiceNameTest(ResourceAttributesTest):

@override
# pylint: disable=no-self-use
Expand All @@ -22,4 +17,4 @@ def get_application_otel_resource_attributes(self) -> str:
return ",".join(pairlist)

def test_service(self) -> None:
self.do_misc_test_request("service-name-test")
self.do_test_resource_attributes("unknown_service")
Loading