Skip to content

Commit 49b6c23

Browse files
committed
distro: add cloud resource detectors
This add some code to dinamically filter the available resource detectors in order to avoid any eventual costly detection.
1 parent 97a7e93 commit 49b6c23

File tree

5 files changed

+128
-3
lines changed

5 files changed

+128
-3
lines changed

pyproject.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ dependencies = [
3131
"opentelemetry-exporter-otlp == 1.28.2",
3232
"opentelemetry-instrumentation == 0.49b2",
3333
"opentelemetry-instrumentation-system-metrics == 0.49b2",
34-
"opentelemetry-semantic-conventions == 0.49b2",
34+
"opentelemetry-resourcedetector-gcp ~= 1.7.0a0",
35+
"opentelemetry-resource-detector-azure ~= 0.1.5",
36+
"opentelemetry-resource-detector-container ~= 0.49b2",
3537
"opentelemetry-sdk == 1.28.2",
38+
"opentelemetry-sdk-extension-aws ~= 2.0.2",
39+
"opentelemetry-semantic-conventions == 0.49b2",
3640
]
3741

3842
[project.optional-dependencies]
@@ -47,6 +51,7 @@ distro = "elasticotel.distro:ElasticOpenTelemetryDistro"
4751
[project.entry-points.opentelemetry_resource_detector]
4852
process_runtime = "elasticotel.sdk.resources:ProcessRuntimeResourceDetector"
4953
telemetry_distro = "elasticotel.sdk.resources:TelemetryDistroResourceDetector"
54+
_gcp = "opentelemetry.resourcedetector.gcp_resource_detector._metadata:GoogleCloudResourceDetector"
5055

5156
[project.readme]
5257
file = "README.md"

src/elasticotel/distro/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
from opentelemetry.util._importlib_metadata import EntryPoint
5757

5858
from elasticotel.distro.environment_variables import ELASTIC_OTEL_SYSTEM_METRICS_ENABLED
59+
from elasticotel.distro.resource_detectors import get_cloud_resource_detectors
5960

6061

6162
logger = logging.getLogger(__name__)
@@ -139,8 +140,11 @@ def _configure(self, **kwargs):
139140
os.environ.setdefault(OTEL_METRICS_EXPORTER, "otlp")
140141
os.environ.setdefault(OTEL_LOGS_EXPORTER, "otlp")
141142
os.environ.setdefault(OTEL_EXPORTER_OTLP_PROTOCOL, "grpc")
142-
os.environ.setdefault(OTEL_EXPERIMENTAL_RESOURCE_DETECTORS, "process_runtime,os,otel,telemetry_distro")
143143
# disable exemplars by default for now
144144
os.environ.setdefault(OTEL_METRICS_EXEMPLAR_FILTER, "always_off")
145145
# preference to use DELTA temporality as we can handle only this kind of Histograms
146146
os.environ.setdefault(OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, "DELTA")
147+
148+
base_resource_detectors = ["process_runtime", "os", "otel", "telemetry_distro"]
149+
detectors = base_resource_detectors + get_cloud_resource_detectors()
150+
os.environ.setdefault(OTEL_EXPERIMENTAL_RESOURCE_DETECTORS, ",".join(detectors))
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright
4+
# ownership. Elasticsearch B.V. licenses this file to you under
5+
# the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import os
18+
19+
AWS_LAMBDA_DETECTORS = ["aws_lambda"]
20+
AZURE_FUNCTIONS_DETECTORS = ["azure_functions"]
21+
GCP_CLOUD_RUN_DETECTORS = ["_gcp"]
22+
KUBERNETES_DETECTORS = ["_gcp", "aws_eks", "container"]
23+
OTHER_CLOUD_DETECTORS = [
24+
"_gcp",
25+
"aws_ec2",
26+
"aws_ecs",
27+
"aws_elastic_beanstalk",
28+
"azure_app_service",
29+
"azure_vm",
30+
"container",
31+
]
32+
33+
34+
def _on_aws_lambda():
35+
"""Cheap check to detect if we are running on AWS lambda"""
36+
return "AWS_LAMBDA_FUNCTION_NAME" in os.environ
37+
38+
39+
def _on_azure_functions():
40+
"""Cheap check to detect if we are running on Azure functions"""
41+
return "FUNCTIONS_WORKER_RUNTIME" in os.environ
42+
43+
44+
def _on_gcp_cloud_run():
45+
"""Cheap check to detect if we are running inside Google Cloud Run"""
46+
return "K_CONFIGURATION" in os.environ
47+
48+
49+
def _on_k8s():
50+
"""Cheap check to detect if we are running inside a Kubernetes pod or not"""
51+
return "KUBERNETES_SERVICE_HOST" in os.environ
52+
53+
54+
def get_cloud_resource_detectors():
55+
"""Helper to get a subset of the available cloud resource detectors depending on the environment
56+
57+
This is done to avoid loading resource detectors doing HTTP requests for metadata that will fail"""
58+
if _on_aws_lambda():
59+
return AWS_LAMBDA_DETECTORS
60+
elif _on_azure_functions():
61+
return AZURE_FUNCTIONS_DETECTORS
62+
elif _on_gcp_cloud_run():
63+
return GCP_CLOUD_RUN_DETECTORS
64+
elif _on_k8s():
65+
return KUBERNETES_DETECTORS
66+
return OTHER_CLOUD_DETECTORS

tests/distro/test_distro.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ def test_default_configuration(self):
4242
self.assertEqual("otlp", os.environ.get(OTEL_LOGS_EXPORTER))
4343
self.assertEqual("grpc", os.environ.get(OTEL_EXPORTER_OTLP_PROTOCOL))
4444
self.assertEqual(
45-
"process_runtime,os,otel,telemetry_distro", os.environ.get(OTEL_EXPERIMENTAL_RESOURCE_DETECTORS)
45+
"process_runtime,os,otel,telemetry_distro,_gcp,aws_ec2,aws_ecs,aws_elastic_beanstalk,azure_app_service,azure_vm,container",
46+
os.environ.get(OTEL_EXPERIMENTAL_RESOURCE_DETECTORS),
4647
)
4748
self.assertEqual("always_off", os.environ.get(OTEL_METRICS_EXEMPLAR_FILTER))
4849
self.assertEqual("DELTA", os.environ.get(OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE))
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright
4+
# ownership. Elasticsearch B.V. licenses this file to you under
5+
# the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
from unittest import TestCase, mock
18+
19+
from elasticotel.distro.resource_detectors import get_cloud_resource_detectors
20+
21+
22+
class TestGetCloudResourceDetectors(TestCase):
23+
@mock.patch.dict("os.environ", {"AWS_LAMBDA_FUNCTION_NAME": "lambda"}, clear=True)
24+
def test_aws_lambda(self):
25+
resource_detectors = get_cloud_resource_detectors()
26+
self.assertEqual(resource_detectors, ["aws_lambda"])
27+
28+
@mock.patch.dict("os.environ", {"FUNCTIONS_WORKER_RUNTIME": "azure"}, clear=True)
29+
def test_azure_functions(self):
30+
resource_detectors = get_cloud_resource_detectors()
31+
self.assertEqual(resource_detectors, ["azure_functions"])
32+
33+
@mock.patch.dict("os.environ", {"K_CONFIGURATION": "cloudrun"}, clear=True)
34+
def test_gcp_cloud_run(self):
35+
resource_detectors = get_cloud_resource_detectors()
36+
self.assertEqual(resource_detectors, ["_gcp"])
37+
38+
@mock.patch.dict("os.environ", {"KUBERNETES_SERVICE_HOST": "k8s"}, clear=True)
39+
def test_kubernetes_pod(self):
40+
resource_detectors = get_cloud_resource_detectors()
41+
self.assertEqual(resource_detectors, ["_gcp", "aws_eks", "container"])
42+
43+
@mock.patch.dict("os.environ", {}, clear=True)
44+
def test_other_cloud_detectors(self):
45+
resource_detectors = get_cloud_resource_detectors()
46+
self.assertEqual(
47+
resource_detectors,
48+
["_gcp", "aws_ec2", "aws_ecs", "aws_elastic_beanstalk", "azure_app_service", "azure_vm", "container"],
49+
)

0 commit comments

Comments
 (0)