Repository: open-telemetry/opentelemetry-python-contrib

Issue:
Resource must be immutable
The boto instrumentation changes a span's resource to set attributes, this goes against the spec.


Discussion:

Resolving patch:
diff --git a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/__init__.py b/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/__init__.py
--- a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/__init__.py
@@ -120,18 +120,10 @@ def _common_request(  # pylint: disable=too-many-locals
         with self._tracer.start_as_current_span(
             "{}.command".format(endpoint_name), kind=SpanKind.CONSUMER,
         ) as span:
+            span.set_attribute("endpoint", endpoint_name)
             if args:
                 http_method = args[0]
-                span.resource = Resource(
-                    attributes={
-                        "endpoint": endpoint_name,
-                        "http_method": http_method.lower(),
-                    }
-                )
-            else:
-                span.resource = Resource(
-                    attributes={"endpoint": endpoint_name}
-                )
+                span.set_attribute("http_method", http_method.lower())
 
             # Original func returns a boto.connection.HTTPResponse object
             result = original_func(*args, **kwargs)


Test patch:
diff --git a/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py b/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py
--- a/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py
+++ b/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py
@@ -28,7 +28,6 @@
 )
 
 from opentelemetry.instrumentation.boto import BotoInstrumentor
-from opentelemetry.sdk.resources import Resource
 from opentelemetry.test.test_base import TestBase
 
 
@@ -71,12 +70,8 @@ def test_ec2_client(self):
         span = spans[1]
         self.assertEqual(span.attributes["aws.operation"], "RunInstances")
         assert_span_http_status_code(span, 200)
-        self.assertEqual(
-            span.resource,
-            Resource(
-                attributes={"endpoint": "ec2", "http_method": "runinstances"}
-            ),
-        )
+        self.assertEqual(span.attributes["endpoint"], "ec2")
+        self.assertEqual(span.attributes["http_method"], "runinstances")
         self.assertEqual(span.attributes["http.method"], "POST")
         self.assertEqual(span.attributes["aws.region"], "us-west-2")
         self.assertEqual(span.name, "ec2.command")
@@ -147,10 +142,8 @@ def test_s3_client(self):
         self.assertEqual(len(spans), 3)
         span = spans[2]
         assert_span_http_status_code(span, 200)
-        self.assertEqual(
-            span.resource,
-            Resource(attributes={"endpoint": "s3", "http_method": "head"}),
-        )
+        self.assertEqual(span.attributes["endpoint"], "s3")
+        self.assertEqual(span.attributes["http_method"], "head")
         self.assertEqual(span.attributes["http.method"], "HEAD")
         self.assertEqual(span.attributes["aws.operation"], "head_bucket")
         self.assertEqual(span.name, "s3.command")
@@ -162,10 +155,8 @@ def test_s3_client(self):
             spans = self.memory_exporter.get_finished_spans()
             assert spans
             span = spans[2]
-            self.assertEqual(
-                span.resource,
-                Resource(attributes={"endpoint": "s3", "http_method": "head"}),
-            )
+            self.assertEqual(spans[2].attributes["endpoint"], "s3")
+            self.assertEqual(spans[2].attributes["http_method"], "head")
 
     @mock_s3_deprecated
     def test_s3_put(self):
@@ -182,24 +173,18 @@ def test_s3_put(self):
         self.assertEqual(len(spans), 3)
         self.assertEqual(spans[0].attributes["aws.operation"], "create_bucket")
         assert_span_http_status_code(spans[0], 200)
-        self.assertEqual(
-            spans[0].resource,
-            Resource(attributes={"endpoint": "s3", "http_method": "put"}),
-        )
+        self.assertEqual(spans[0].attributes["endpoint"], "s3")
+        self.assertEqual(spans[0].attributes["http_method"], "put")
         # get bucket
         self.assertEqual(spans[1].attributes["aws.operation"], "head_bucket")
-        self.assertEqual(
-            spans[1].resource,
-            Resource(attributes={"endpoint": "s3", "http_method": "head"}),
-        )
+        self.assertEqual(spans[1].attributes["endpoint"], "s3")
+        self.assertEqual(spans[1].attributes["http_method"], "head")
         # put object
         self.assertEqual(
             spans[2].attributes["aws.operation"], "_send_file_internal"
         )
-        self.assertEqual(
-            spans[2].resource,
-            Resource(attributes={"endpoint": "s3", "http_method": "put"}),
-        )
+        self.assertEqual(spans[2].attributes["endpoint"], "s3")
+        self.assertEqual(spans[2].attributes["http_method"], "put")
 
     @mock_lambda_deprecated
     def test_unpatch(self):
@@ -239,10 +224,8 @@ def test_lambda_client(self):
         self.assertEqual(len(spans), 2)
         span = spans[0]
         assert_span_http_status_code(span, 200)
-        self.assertEqual(
-            span.resource,
-            Resource(attributes={"endpoint": "lambda", "http_method": "get"}),
-        )
+        self.assertEqual(span.attributes["endpoint"], "lambda")
+        self.assertEqual(span.attributes["http_method"], "get")
         self.assertEqual(span.attributes["http.method"], "GET")
         self.assertEqual(span.attributes["aws.region"], "us-east-2")
         self.assertEqual(span.attributes["aws.operation"], "list_functions")
@@ -256,15 +239,8 @@ def test_sts_client(self):
         spans = self.memory_exporter.get_finished_spans()
         assert spans
         span = spans[0]
-        self.assertEqual(
-            span.resource,
-            Resource(
-                attributes={
-                    "endpoint": "sts",
-                    "http_method": "getfederationtoken",
-                }
-            ),
-        )
+        self.assertEqual(span.attributes["endpoint"], "sts")
+        self.assertEqual(span.attributes["http_method"], "getfederationtoken")
         self.assertEqual(span.attributes["aws.region"], "us-west-2")
         self.assertEqual(
             span.attributes["aws.operation"], "GetFederationToken"
@@ -288,7 +264,5 @@ def test_elasticache_client(self):
         spans = self.memory_exporter.get_finished_spans()
         assert spans
         span = spans[0]
-        self.assertEqual(
-            span.resource, Resource(attributes={"endpoint": "elasticcache"})
-        )
+        self.assertEqual(span.attributes["endpoint"], "elasticcache")
         self.assertEqual(span.attributes["aws.region"], "us-west-2")