Skip to content

Commit e788e6c

Browse files
authored
Refactor resource mapping logic into resource detection package (#235)
and update the exporters to use the shared code.
1 parent 65893a8 commit e788e6c

File tree

15 files changed

+239
-234
lines changed

15 files changed

+239
-234
lines changed

e2e-test-server/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ FROM python-base as build-base
2828
# copy local dependencies
2929
COPY opentelemetry-exporter-gcp-trace opentelemetry-exporter-gcp-trace
3030
COPY opentelemetry-propagator-gcp opentelemetry-propagator-gcp
31+
COPY opentelemetry-resourcedetector-gcp opentelemetry-resourcedetector-gcp
3132
WORKDIR $SRC/e2e-test-server
3233
# copy requirements/constraints
3334
COPY e2e-test-server/requirements.txt e2e-test-server/constraints.txt ./

e2e-test-server/constraints.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ PyYAML==5.4.1
3131
requests==2.25.1
3232
rsa==4.7.2
3333
six==1.15.0
34-
typing-extensions==3.10.0.0
34+
typing-extensions==4.5.0
3535
typing-inspect==0.6.0
3636
urllib3==1.26.4
3737
waitress==2.0.0

e2e-test-server/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
-c constraints.txt
22
../opentelemetry-exporter-gcp-trace
33
../opentelemetry-propagator-gcp
4+
../opentelemetry-resourcedetector-gcp
45
opentelemetry-sdk
56
opentelemetry-api
67
Flask

opentelemetry-exporter-gcp-monitoring/setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ install_requires =
2828
google-cloud-monitoring ~= 2.0
2929
opentelemetry-api ~= 1.0
3030
opentelemetry-sdk ~= 1.0
31+
opentelemetry-resourcedetector-gcp >= 1.5.0dev0, == 1.*
3132

3233
[options.packages.find]
3334
where = src

opentelemetry-exporter-gcp-monitoring/src/opentelemetry/exporter/cloud_monitoring/__init__.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from google.api.label_pb2 import LabelDescriptor
2525
from google.api.metric_pb2 import Metric as GMetric
2626
from google.api.metric_pb2 import MetricDescriptor
27+
from google.api.monitored_resource_pb2 import MonitoredResource
2728
from google.cloud.monitoring_v3 import (
2829
CreateMetricDescriptorRequest,
2930
CreateTimeSeriesRequest,
@@ -39,10 +40,10 @@
3940

4041
# pylint: disable=no-name-in-module
4142
from google.protobuf.timestamp_pb2 import Timestamp
42-
from opentelemetry.exporter.cloud_monitoring._resource import (
43+
from opentelemetry.exporter.cloud_monitoring.version import __version__
44+
from opentelemetry.resourcedetector.gcp_resource_detector._mapping import (
4345
get_monitored_resource,
4446
)
45-
from opentelemetry.exporter.cloud_monitoring.version import __version__
4647
from opentelemetry.sdk.metrics.export import (
4748
Gauge,
4849
Histogram,
@@ -296,9 +297,19 @@ def export(
296297
all_series = []
297298

298299
for resource_metric in metrics_data.resource_metrics:
299-
monitored_resource = get_monitored_resource(
300+
monitored_resource_data = get_monitored_resource(
300301
resource_metric.resource
301302
)
303+
# convert it to proto
304+
monitored_resource = (
305+
MonitoredResource(
306+
type=monitored_resource_data.type,
307+
labels=monitored_resource_data.labels,
308+
)
309+
if monitored_resource_data
310+
else None
311+
)
312+
302313
for scope_metric in resource_metric.scope_metrics:
303314
for metric in scope_metric.metrics:
304315
# Convert all data_points to Sequences, see

opentelemetry-exporter-gcp-trace/setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ install_requires =
2828
google-cloud-trace ~= 1.1
2929
opentelemetry-api ~= 1.0
3030
opentelemetry-sdk ~= 1.0
31+
opentelemetry-resourcedetector-gcp >= 1.5.0dev0, == 1.*
3132

3233
[options.packages.find]
3334
where = src

opentelemetry-exporter-gcp-trace/src/opentelemetry/exporter/cloud_trace/__init__.py

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@
102102
OTEL_EXPORTER_GCP_TRACE_RESOURCE_REGEX,
103103
)
104104
from opentelemetry.exporter.cloud_trace.version import __version__
105+
from opentelemetry.resourcedetector.gcp_resource_detector import (
106+
_constants as _resource_constants,
107+
)
108+
from opentelemetry.resourcedetector.gcp_resource_detector._mapping import (
109+
get_monitored_resource,
110+
)
105111
from opentelemetry.sdk.resources import Resource
106112
from opentelemetry.sdk.trace import Event
107113
from opentelemetry.sdk.trace.export import (
@@ -128,6 +134,7 @@
128134
("grpc.primary_user_agent", _USER_AGENT),
129135
]
130136

137+
131138
MAX_NUM_LINKS = 128
132139
MAX_NUM_EVENTS = 32
133140
MAX_EVENT_ATTRS = 4
@@ -420,24 +427,6 @@ def _strip_characters(ot_version):
420427
return "".join(filter(lambda x: x.isdigit() or x == ".", ot_version))
421428

422429

423-
OT_RESOURCE_ATTRIBUTE_TO_GCP = {
424-
"gce_instance": {
425-
"host.id": "instance_id",
426-
"cloud.account.id": "project_id",
427-
"cloud.zone": "zone",
428-
},
429-
"gke_container": {
430-
"k8s.cluster.name": "cluster_name",
431-
"k8s.namespace.name": "namespace_id",
432-
"k8s.pod.name": "pod_id",
433-
"host.id": "instance_id",
434-
"container.name": "container_name",
435-
"cloud.account.id": "project_id",
436-
"cloud.zone": "zone",
437-
},
438-
}
439-
440-
441430
def _extract_resources(
442431
resource: Resource, resource_regex: Optional[Pattern] = None
443432
) -> Dict[str, str]:
@@ -451,24 +440,18 @@ def _extract_resources(
451440
if resource_regex.match(k)
452441
}
453442
)
454-
if resource_attributes.get("cloud.provider") != "gcp":
455-
return extracted_attributes
456-
resource_type = resource_attributes["gcp.resource_type"]
457-
if (
458-
not isinstance(resource_type, str)
459-
or resource_type not in OT_RESOURCE_ATTRIBUTE_TO_GCP
443+
monitored_resource = get_monitored_resource(resource)
444+
# Do not map generic_task and generic_node to g.co/r/... span labels.
445+
if monitored_resource and monitored_resource.type not in (
446+
_resource_constants.GENERIC_NODE,
447+
_resource_constants.GENERIC_TASK,
460448
):
461-
return extracted_attributes
462-
extracted_attributes.update(
463-
{
464-
"g.co/r/{}/{}".format(resource_type, gcp_resource_key): str(
465-
resource_attributes[ot_resource_key]
466-
)
467-
for ot_resource_key, gcp_resource_key in OT_RESOURCE_ATTRIBUTE_TO_GCP[
468-
resource_type
469-
].items()
470-
}
471-
)
449+
extracted_attributes.update(
450+
{
451+
"g.co/r/{}/{}".format(monitored_resource.type, k): v
452+
for k, v in monitored_resource.labels.items()
453+
}
454+
)
472455
return extracted_attributes
473456

474457

opentelemetry-exporter-gcp-trace/tests/test_cloud_trace_exporter.py

Lines changed: 42 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,14 @@ def test_constructor_explicit(self):
124124
def test_export(self):
125125
resource_info = Resource(
126126
{
127-
"cloud.account.id": 123,
128-
"host.id": "host",
129-
"cloud.zone": "US",
127+
"cloud.account.id": "123",
128+
"cloud.platform": "gcp_compute_engine",
130129
"cloud.provider": "gcp",
131-
"gcp.resource_type": "gce_instance",
130+
"cloud.region": "us-east4",
131+
"cloud.availability_zone": "us-east4-b",
132+
"host.id": "host",
133+
"host.name": "fakeName",
134+
"host.type": "fakeMachineType",
132135
}
133136
)
134137
span_datas = [
@@ -157,13 +160,12 @@ def test_export(self):
157160
),
158161
"attributes": ProtoSpan.Attributes(
159162
attribute_map={
160-
"g.co/r/gce_instance/zone": _format_attribute_value("US"),
163+
"g.co/r/gce_instance/zone": _format_attribute_value(
164+
"us-east4-b"
165+
),
161166
"g.co/r/gce_instance/instance_id": _format_attribute_value(
162167
"host"
163168
),
164-
"g.co/r/gce_instance/project_id": _format_attribute_value(
165-
"123"
166-
),
167169
"g.co/agent": self.agent_code,
168170
"attr_key": _format_attribute_value("attr_value"),
169171
}
@@ -652,27 +654,30 @@ def test_too_many_link_attributes(self):
652654
)
653655

654656
def test_extract_empty_resources(self):
655-
self.assertEqual(_extract_resources(Resource.get_empty()), {})
657+
self.assertEqual(
658+
_extract_resources(Resource.get_empty()),
659+
{},
660+
)
656661

657662
def test_extract_resource_attributes_with_regex(self):
658663
resource_regex = re.compile(r"service\..*")
659664
resource = Resource(
660665
attributes={
661-
"cloud.account.id": 123,
662-
"host.id": "host",
663-
"cloud.zone": "US",
666+
"cloud.account.id": "123",
667+
"cloud.availability_zone": "us-east4-b",
668+
"cloud.platform": "gcp_compute_engine",
664669
"cloud.provider": "gcp",
665-
"extra_info": "extra",
666-
"gcp.resource_type": "gce_instance",
667-
"not_gcp_resource": "value",
670+
"cloud.region": "us-east4",
671+
"host.id": "host",
672+
"host.name": "fakeName",
673+
"host.type": "fakeMachineType",
668674
"service.name": "my-app",
669675
"service.version": "1",
670676
}
671677
)
672678
expected_extract = {
673-
"g.co/r/gce_instance/project_id": "123",
674679
"g.co/r/gce_instance/instance_id": "host",
675-
"g.co/r/gce_instance/zone": "US",
680+
"g.co/r/gce_instance/zone": "us-east4-b",
676681
"service.name": "my-app",
677682
"service.version": "1",
678683
}
@@ -684,19 +689,19 @@ def test_non_matching_regex(self):
684689
resource_regex = re.compile(r"this-regex-matches-nothing")
685690
resource = Resource(
686691
attributes={
687-
"cloud.account.id": 123,
688-
"host.id": "host",
689-
"cloud.zone": "US",
692+
"cloud.account.id": "123",
693+
"cloud.availability_zone": "us-east4-b",
694+
"cloud.platform": "gcp_compute_engine",
690695
"cloud.provider": "gcp",
691-
"extra_info": "extra",
692-
"gcp.resource_type": "gce_instance",
693-
"not_gcp_resource": "value",
696+
"cloud.region": "us-east4",
697+
"host.id": "host",
698+
"host.name": "fakeName",
699+
"host.type": "fakeMachineType",
694700
}
695701
)
696702
expected_extract = {
697-
"g.co/r/gce_instance/project_id": "123",
698703
"g.co/r/gce_instance/instance_id": "host",
699-
"g.co/r/gce_instance/zone": "US",
704+
"g.co/r/gce_instance/zone": "us-east4-b",
700705
}
701706
self.assertEqual(
702707
_extract_resources(resource, resource_regex), expected_extract
@@ -705,34 +710,22 @@ def test_non_matching_regex(self):
705710
def test_extract_well_formed_resources(self):
706711
resource = Resource(
707712
attributes={
708-
"cloud.account.id": 123,
709-
"host.id": "host",
710-
"cloud.zone": "US",
713+
"cloud.account.id": "123",
714+
"cloud.availability_zone": "us-east4-b",
715+
"cloud.platform": "gcp_compute_engine",
711716
"cloud.provider": "gcp",
712-
"extra_info": "extra",
713-
"gcp.resource_type": "gce_instance",
714-
"not_gcp_resource": "value",
717+
"cloud.region": "us-east4",
718+
"host.id": "host",
719+
"host.name": "fakeName",
720+
"host.type": "fakeMachineType",
715721
}
716722
)
717723
expected_extract = {
718-
"g.co/r/gce_instance/project_id": "123",
719724
"g.co/r/gce_instance/instance_id": "host",
720-
"g.co/r/gce_instance/zone": "US",
725+
"g.co/r/gce_instance/zone": "us-east4-b",
721726
}
722727
self.assertEqual(_extract_resources(resource), expected_extract)
723728

724-
def test_extract_malformed_resources(self):
725-
# This resource doesn't have all the fields required for a gce_instance
726-
# Specifically its missing "host.id", "cloud.zone", "cloud.account.id"
727-
resource = Resource(
728-
attributes={
729-
"gcp.resource_type": "gce_instance",
730-
"cloud.provider": "gcp",
731-
}
732-
)
733-
# Should throw when passed a malformed GCP resource dict
734-
self.assertRaises(KeyError, _extract_resources, resource)
735-
736729
def test_extract_unsupported_gcp_resources(self):
737730
# Unsupported gcp resources will be ignored
738731
resource = Resource(
@@ -741,24 +734,14 @@ def test_extract_unsupported_gcp_resources(self):
741734
"host.id": "host",
742735
"extra_info": "extra",
743736
"not_gcp_resource": "value",
744-
"gcp.resource_type": "unsupported_gcp_resource",
737+
"cloud.platform": "gcp_some_unsupported_thing",
745738
"cloud.provider": "gcp",
746739
}
747740
)
748-
self.assertEqual(_extract_resources(resource), {})
749-
750-
def test_extract_unsupported_provider_resources(self):
751-
# Resources with currently unsupported providers will be ignored
752-
resource = Resource(
753-
attributes={
754-
"cloud.account.id": "123",
755-
"host.id": "host",
756-
"extra_info": "extra",
757-
"not_gcp_resource": "value",
758-
"cloud.provider": "aws",
759-
}
741+
self.assertEqual(
742+
_extract_resources(resource),
743+
{},
760744
)
761-
self.assertEqual(_extract_resources(resource), {})
762745

763746
def test_truncate_string(self):
764747
"""Cloud Trace API imposes limits on the length of many things,

0 commit comments

Comments
 (0)