Skip to content

Commit bf9a8e8

Browse files
author
Michele Mancioppi
authored
Implement aws.ecs.* resource attributes (#1212)
1 parent 70187ff commit bf9a8e8

File tree

7 files changed

+562
-3
lines changed

7 files changed

+562
-3
lines changed

Diff for: CHANGELOG.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10-
## Fixed
10+
### Added
11+
12+
- `opentelemetry/sdk/extension/aws` Implement [`aws.ecs.*`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud_provider/aws/ecs.md) and [`aws.logs.*`](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/cloud_provider/aws/logs/) resource attributes in the `AwsEcsResourceDetector` detector when the ECS Metadata v4 is available
13+
([#1212](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1212))
14+
15+
### Fixed
1116

1217
- Fix aiopg instrumentation to work with aiopg < 2.0.0
1318
([#1473](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1473))

Diff for: sdk-extension/opentelemetry-sdk-extension-aws/src/opentelemetry/sdk/extension/aws/resource/ecs.py

+113-1
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import json
1516
import logging
1617
import os
18+
import re
1719
import socket
20+
from urllib.request import Request, urlopen
1821

1922
from opentelemetry.sdk.resources import Resource, ResourceDetector
2023
from opentelemetry.semconv.resource import (
@@ -58,18 +61,127 @@ def detect(self) -> "Resource":
5861
"Failed to get container ID on ECS: %s.", exception
5962
)
6063

61-
return Resource(
64+
base_resource = Resource(
6265
{
6366
ResourceAttributes.CLOUD_PROVIDER: CloudProviderValues.AWS.value,
6467
ResourceAttributes.CLOUD_PLATFORM: CloudPlatformValues.AWS_ECS.value,
6568
ResourceAttributes.CONTAINER_NAME: socket.gethostname(),
6669
ResourceAttributes.CONTAINER_ID: container_id,
6770
}
6871
)
72+
73+
metadata_v4_endpoint = os.environ.get(
74+
"ECS_CONTAINER_METADATA_URI_V4"
75+
)
76+
77+
if not metadata_v4_endpoint:
78+
return base_resource
79+
80+
# Returns https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4.html#task-metadata-endpoint-v4-response
81+
metadata_container = json.loads(_http_get(metadata_v4_endpoint))
82+
metadata_task = json.loads(
83+
_http_get(f"{metadata_v4_endpoint}/task")
84+
)
85+
86+
task_arn = metadata_task["TaskARN"]
87+
base_arn = task_arn[0 : task_arn.rindex(":")] # noqa
88+
cluster: str = metadata_task["Cluster"]
89+
cluster_arn = (
90+
cluster
91+
if cluster.startswith("arn:")
92+
else f"{base_arn}:cluster/{cluster}"
93+
)
94+
95+
logs_resource = _get_logs_resource(metadata_container)
96+
97+
return base_resource.merge(logs_resource).merge(
98+
Resource(
99+
{
100+
ResourceAttributes.AWS_ECS_CONTAINER_ARN: metadata_container[
101+
"ContainerARN"
102+
],
103+
ResourceAttributes.AWS_ECS_CLUSTER_ARN: cluster_arn,
104+
ResourceAttributes.AWS_ECS_LAUNCHTYPE: metadata_task[
105+
"LaunchType"
106+
].lower(),
107+
ResourceAttributes.AWS_ECS_TASK_ARN: task_arn,
108+
ResourceAttributes.AWS_ECS_TASK_FAMILY: metadata_task[
109+
"Family"
110+
],
111+
ResourceAttributes.AWS_ECS_TASK_REVISION: metadata_task[
112+
"Revision"
113+
],
114+
}
115+
)
116+
)
69117
# pylint: disable=broad-except
70118
except Exception as exception:
71119
if self.raise_on_error:
72120
raise exception
73121

74122
logger.warning("%s failed: %s", self.__class__.__name__, exception)
75123
return Resource.get_empty()
124+
125+
126+
def _get_logs_resource(metadata_container):
127+
if metadata_container.get("LogDriver") == "awslogs":
128+
log_options = metadata_container.get("LogOptions")
129+
if log_options:
130+
logs_region = log_options.get("awslogs-region")
131+
logs_group_name = log_options.get("awslogs-group")
132+
logs_stream_name = log_options.get("awslogs-stream")
133+
134+
container_arn = metadata_container["ContainerARN"]
135+
136+
if not logs_region:
137+
aws_region_match = re.match(
138+
r"arn:aws:ecs:([^:]+):.*", container_arn
139+
)
140+
if aws_region_match:
141+
logs_region = aws_region_match.group(1)
142+
143+
else:
144+
logger.warning("Cannot parse AWS region out of ECS ARN")
145+
146+
# We need to retrieve the account ID from some other ARN to create the
147+
# log-group and log-stream ARNs
148+
aws_account = None
149+
aws_account_match = re.match(
150+
r"arn:aws:ecs:[^:]+:([^:]+):.*", container_arn
151+
)
152+
if aws_account_match:
153+
aws_account = aws_account_match.group(1)
154+
155+
logs_group_arn = None
156+
logs_stream_arn = None
157+
if logs_region and aws_account:
158+
if logs_group_name:
159+
logs_group_arn = f"arn:aws:logs:{logs_region}:{aws_account}:log-group:{logs_group_name}"
160+
161+
if logs_stream_name:
162+
logs_stream_arn = f"arn:aws:logs:{logs_region}:{aws_account}:log-group:{logs_group_name}:log-stream:{logs_stream_name}"
163+
164+
return Resource(
165+
{
166+
ResourceAttributes.AWS_LOG_GROUP_NAMES: [logs_group_name],
167+
ResourceAttributes.AWS_LOG_GROUP_ARNS: [logs_group_arn],
168+
ResourceAttributes.AWS_LOG_STREAM_NAMES: [
169+
logs_stream_name
170+
],
171+
ResourceAttributes.AWS_LOG_STREAM_ARNS: [logs_stream_arn],
172+
}
173+
)
174+
175+
logger.warning(
176+
"The metadata endpoint v4 has returned 'awslogs' as 'LogDriver', but there is no 'LogOptions' data"
177+
)
178+
179+
return Resource.get_empty()
180+
181+
182+
def _http_get(url):
183+
with urlopen(
184+
Request(url, method="GET"),
185+
timeout=5,
186+
) as response:
187+
return response.read().decode("utf-8")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"DockerId": "ea32192c8553fbff06c9340478a2ff089b2bb5646fb718b4ee206641c9086d66",
3+
"Name": "curl",
4+
"DockerName": "ecs-curltest-24-curl-cca48e8dcadd97805600",
5+
"Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest",
6+
"ImageID": "sha256:d691691e9652791a60114e67b365688d20d19940dde7c4736ea30e660d8d3553",
7+
"Labels": {
8+
"com.amazonaws.ecs.cluster": "default",
9+
"com.amazonaws.ecs.container-name": "curl",
10+
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/8f03e41243824aea923aca126495f665",
11+
"com.amazonaws.ecs.task-definition-family": "curltest",
12+
"com.amazonaws.ecs.task-definition-version": "24"
13+
},
14+
"DesiredStatus": "RUNNING",
15+
"KnownStatus": "RUNNING",
16+
"Limits": {
17+
"CPU": 10,
18+
"Memory": 128
19+
},
20+
"CreatedAt": "2020-10-02T00:15:07.620912337Z",
21+
"StartedAt": "2020-10-02T00:15:08.062559351Z",
22+
"Type": "NORMAL",
23+
"LogDriver": "awslogs",
24+
"LogOptions": {
25+
"awslogs-create-group": "true",
26+
"awslogs-group": "/ecs/metadata",
27+
"awslogs-region": "us-west-2",
28+
"awslogs-stream": "ecs/curl/8f03e41243824aea923aca126495f665"
29+
},
30+
"ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9",
31+
"Networks": [
32+
{
33+
"NetworkMode": "awsvpc",
34+
"IPv4Addresses": [
35+
"10.0.2.100"
36+
],
37+
"AttachmentIndex": 0,
38+
"MACAddress": "0e:9e:32:c7:48:85",
39+
"IPv4SubnetCIDRBlock": "10.0.2.0/24",
40+
"PrivateDNSName": "ip-10-0-2-100.us-west-2.compute.internal",
41+
"SubnetGatewayIpv4Address": "10.0.2.1/24"
42+
}
43+
]
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"DockerId": "cd189a933e5849daa93386466019ab50-2495160603",
3+
"Name": "curl",
4+
"DockerName": "curl",
5+
"Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest",
6+
"ImageID": "sha256:25f3695bedfb454a50f12d127839a68ad3caf91e451c1da073db34c542c4d2cb",
7+
"Labels": {
8+
"com.amazonaws.ecs.cluster": "arn:aws:ecs:us-west-2:111122223333:cluster/default",
9+
"com.amazonaws.ecs.container-name": "curl",
10+
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/cd189a933e5849daa93386466019ab50",
11+
"com.amazonaws.ecs.task-definition-family": "curltest",
12+
"com.amazonaws.ecs.task-definition-version": "2"
13+
},
14+
"DesiredStatus": "RUNNING",
15+
"KnownStatus": "RUNNING",
16+
"Limits": {
17+
"CPU": 10,
18+
"Memory": 128
19+
},
20+
"CreatedAt": "2020-10-08T20:09:11.44527186Z",
21+
"StartedAt": "2020-10-08T20:09:11.44527186Z",
22+
"Type": "NORMAL",
23+
"Networks": [
24+
{
25+
"NetworkMode": "awsvpc",
26+
"IPv4Addresses": [
27+
"192.0.2.3"
28+
],
29+
"AttachmentIndex": 0,
30+
"MACAddress": "0a:de:f6:10:51:e5",
31+
"IPv4SubnetCIDRBlock": "192.0.2.0/24",
32+
"DomainNameServers": [
33+
"192.0.2.2"
34+
],
35+
"DomainNameSearchList": [
36+
"us-west-2.compute.internal"
37+
],
38+
"PrivateDNSName": "ip-10-0-0-222.us-west-2.compute.internal",
39+
"SubnetGatewayIpv4Address": "192.0.2.0/24"
40+
}
41+
],
42+
"ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1",
43+
"LogOptions": {
44+
"awslogs-create-group": "true",
45+
"awslogs-group": "/ecs/containerlogs",
46+
"awslogs-region": "us-west-2",
47+
"awslogs-stream": "ecs/curl/cd189a933e5849daa93386466019ab50"
48+
},
49+
"LogDriver": "awslogs"
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
{
2+
"Cluster": "default",
3+
"TaskARN": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c",
4+
"Family": "curltest",
5+
"Revision": "26",
6+
"DesiredStatus": "RUNNING",
7+
"KnownStatus": "RUNNING",
8+
"PullStartedAt": "2020-10-02T00:43:06.202617438Z",
9+
"PullStoppedAt": "2020-10-02T00:43:06.31288465Z",
10+
"AvailabilityZone": "us-west-2d",
11+
"LaunchType": "EC2",
12+
"Containers": [
13+
{
14+
"DockerId": "598cba581fe3f939459eaba1e071d5c93bb2c49b7d1ba7db6bb19deeb70d8e38",
15+
"Name": "~internal~ecs~pause",
16+
"DockerName": "ecs-curltest-26-internalecspause-e292d586b6f9dade4a00",
17+
"Image": "amazon/amazon-ecs-pause:0.1.0",
18+
"ImageID": "",
19+
"Labels": {
20+
"com.amazonaws.ecs.cluster": "default",
21+
"com.amazonaws.ecs.container-name": "~internal~ecs~pause",
22+
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c",
23+
"com.amazonaws.ecs.task-definition-family": "curltest",
24+
"com.amazonaws.ecs.task-definition-version": "26"
25+
},
26+
"DesiredStatus": "RESOURCES_PROVISIONED",
27+
"KnownStatus": "RESOURCES_PROVISIONED",
28+
"Limits": {
29+
"CPU": 0,
30+
"Memory": 0
31+
},
32+
"CreatedAt": "2020-10-02T00:43:05.602352471Z",
33+
"StartedAt": "2020-10-02T00:43:06.076707576Z",
34+
"Type": "CNI_PAUSE",
35+
"Networks": [
36+
{
37+
"NetworkMode": "awsvpc",
38+
"IPv4Addresses": [
39+
"10.0.2.61"
40+
],
41+
"AttachmentIndex": 0,
42+
"MACAddress": "0e:10:e2:01:bd:91",
43+
"IPv4SubnetCIDRBlock": "10.0.2.0/24",
44+
"PrivateDNSName": "ip-10-0-2-61.us-west-2.compute.internal",
45+
"SubnetGatewayIpv4Address": "10.0.2.1/24"
46+
}
47+
]
48+
},
49+
{
50+
"DockerId": "ee08638adaaf009d78c248913f629e38299471d45fe7dc944d1039077e3424ca",
51+
"Name": "curl",
52+
"DockerName": "ecs-curltest-26-curl-a0e7dba5aca6d8cb2e00",
53+
"Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest",
54+
"ImageID": "sha256:d691691e9652791a60114e67b365688d20d19940dde7c4736ea30e660d8d3553",
55+
"Labels": {
56+
"com.amazonaws.ecs.cluster": "default",
57+
"com.amazonaws.ecs.container-name": "curl",
58+
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c",
59+
"com.amazonaws.ecs.task-definition-family": "curltest",
60+
"com.amazonaws.ecs.task-definition-version": "26"
61+
},
62+
"DesiredStatus": "RUNNING",
63+
"KnownStatus": "RUNNING",
64+
"Limits": {
65+
"CPU": 10,
66+
"Memory": 128
67+
},
68+
"CreatedAt": "2020-10-02T00:43:06.326590752Z",
69+
"StartedAt": "2020-10-02T00:43:06.767535449Z",
70+
"Type": "NORMAL",
71+
"LogDriver": "awslogs",
72+
"LogOptions": {
73+
"awslogs-create-group": "true",
74+
"awslogs-group": "/ecs/metadata",
75+
"awslogs-region": "us-west-2",
76+
"awslogs-stream": "ecs/curl/158d1c8083dd49d6b527399fd6414f5c"
77+
},
78+
"ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/abb51bdd-11b4-467f-8f6c-adcfe1fe059d",
79+
"Networks": [
80+
{
81+
"NetworkMode": "awsvpc",
82+
"IPv4Addresses": [
83+
"10.0.2.61"
84+
],
85+
"AttachmentIndex": 0,
86+
"MACAddress": "0e:10:e2:01:bd:91",
87+
"IPv4SubnetCIDRBlock": "10.0.2.0/24",
88+
"PrivateDNSName": "ip-10-0-2-61.us-west-2.compute.internal",
89+
"SubnetGatewayIpv4Address": "10.0.2.1/24"
90+
}
91+
]
92+
}
93+
]
94+
}

0 commit comments

Comments
 (0)