Skip to content

Commit 1b0d935

Browse files
committed
feat: contribute ec2 resource detector
1 parent 782c046 commit 1b0d935

File tree

12 files changed

+595
-0
lines changed

12 files changed

+595
-0
lines changed

resources/aws/Gemfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright The OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
source 'https://rubygems.org'
8+
9+
gemspec
10+
11+
group :test do
12+
gem 'minitest', '~> 5.0'
13+
gem 'simplecov', '~> 0.17.1'
14+
gem 'webmock', '~> 3.7.0'
15+
end
16+
17+
group :development do
18+
gem 'rubocop', '~> 1.42.0'
19+
gem 'yard', '~> 0.9'
20+
end

resources/aws/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# OpenTelemetry::Resource::Detector::AWS
2+
3+
The `opentelemetry-resource-detector-aws` gem provides an AWS resource detector for OpenTelemetry.
4+
5+
## What is OpenTelemetry?
6+
7+
OpenTelemetry is an open source observability framework, providing a general-purpose API, SDK, and related tools required for the instrumentation of cloud-native software, frameworks, and libraries.
8+
9+
OpenTelemetry provides a single set of APIs, libraries, agents, and collector services to capture distributed traces and metrics from your application. You can analyze them using Prometheus, Jaeger, and other observability tools.
10+
11+
## How does this gem fit in?
12+
13+
The `opentelemetry-resource-detector-aws` gem provides a means of retrieving a resource for supported environments following the resource semantic conventions. When running on AWS platforms, this detector automatically identifies and populates resource attributes with relevant metadata from the environment.
14+
15+
## Installation
16+
17+
Install the gem using:
18+
19+
```console
20+
gem install opentelemetry-sdk
21+
gem install opentelemetry-resource-detector-aws
22+
```
23+
24+
Or, if you use Bundler, include `opentelemetry-sdk` and `opentelemetry-resource-detector-aws` in your `Gemfile`.
25+
26+
## Usage
27+
28+
```rb
29+
require 'opentelemetry/sdk'
30+
require 'opentelemetry/resource/detector'
31+
32+
OpenTelemetry::SDK.configure do |c|
33+
c.resource = OpenTelemetry::Resource::Detector::AWS.detect
34+
end
35+
```
36+
37+
## Supported AWS Platforms
38+
39+
### AWS EC2 Detector
40+
41+
Populates `cloud` and `host` for processes running on Amazon EC2, including abstractions such as ECS on EC2. Notably, it does not populate anything on AWS Fargate.
42+
43+
| Resource Attribute | Description |
44+
|--------------------|-------------|
45+
| `cloud.account.id` | Value of `accountId` from `/latest/dynamic/instance-identity/document` request |
46+
| `cloud.availability_zone` | Value of `availabilityZone` from `/latest/dynamic/instance-identity/document` request |
47+
| `cloud.platform` | The cloud platform. In this context, it's always "aws_ec2" |
48+
| `cloud.provider` | The cloud provider. In this context, it's always "aws" |
49+
| `cloud.region` | Value of `region` from `/latest/dynamic/instance-identity/document` request |
50+
| `host.id` | Value of `instanceId` from `/latest/dynamic/instance-identity/document` request |
51+
| `host.name` | Value of hostname from `/latest/meta-data/hostname` request |
52+
| `host.type` | Value of `instanceType` from `/latest/dynamic/instance-identity/document` request |
53+
54+
Additional AWS platforms (ECS, EKS, Lambda) will be supported in future versions.
55+
56+
## License
57+
58+
The `opentelemetry-resource-detector-aws` gem is distributed under the Apache 2.0 license. See LICENSE for more information.

resources/aws/Rakefile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright The OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
require 'bundler/gem_tasks'
8+
require 'rake/testtask'
9+
require 'yard'
10+
require 'rubocop/rake_task'
11+
12+
RuboCop::RakeTask.new
13+
14+
Rake::TestTask.new :test do |t|
15+
t.libs << 'test'
16+
t.libs << 'lib'
17+
t.test_files = FileList['test/**/*_test.rb']
18+
end
19+
20+
YARD::Rake::YardocTask.new do |t|
21+
t.stats_options = ['--list-undoc']
22+
end
23+
24+
if RUBY_ENGINE == 'truffleruby'
25+
task default: %i[test]
26+
else
27+
task default: %i[test rubocop yard]
28+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright The OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
require_relative 'opentelemetry/resource/detector'
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright The OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
require 'opentelemetry/sdk'
8+
require 'opentelemetry/resource/detector/aws'
9+
require 'opentelemetry/resource/detector/aws/version'
10+
11+
# OpenTelemetry is an open source observability framework, providing a
12+
# general-purpose API, SDK, and related tools required for the instrumentation
13+
# of cloud-native software, frameworks, and libraries.
14+
#
15+
# The OpenTelemetry module provides global accessors for telemetry objects.
16+
# See the documentation for the `opentelemetry-api` gem for details.
17+
module OpenTelemetry
18+
module Resource
19+
# Detector contains the resource detectors
20+
module Detector
21+
end
22+
end
23+
end
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright The OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
require 'opentelemetry/resource/detector/aws/ec2'
8+
9+
module OpenTelemetry
10+
module Resource
11+
module Detector
12+
# AWS contains detect class method for determining AWS environment resource attributes
13+
module AWS
14+
extend self
15+
16+
def detect
17+
# This will be a composite of all the AWS platform detectors
18+
ec2_resource = EC2.detect
19+
20+
# For now, return the EC2 resource directly
21+
# In the future, we'll implement detection for EC2, ECS, EKS, etc.
22+
return ec2_resource
23+
end
24+
end
25+
end
26+
end
27+
end
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright The OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
require 'net/http'
8+
require 'json'
9+
10+
module OpenTelemetry
11+
module Resource
12+
module Detector
13+
module AWS
14+
# EC2 contains detect class method for determining EC2 resource attributes
15+
module EC2
16+
extend self
17+
18+
# EC2 metadata service endpoints and constants
19+
EC2_METADATA_HOST = '169.254.169.254'
20+
TOKEN_ENDPOINT = '/latest/api/token'
21+
IDENTITY_DOCUMENT_ENDPOINT = '/latest/dynamic/instance-identity/document'
22+
HOSTNAME_ENDPOINT = '/latest/meta-data/hostname'
23+
24+
TOKEN_HEADER = 'X-aws-ec2-metadata-token'
25+
TOKEN_TTL_HEADER = 'X-aws-ec2-metadata-token-ttl-seconds'
26+
TOKEN_TTL_VALUE = '60'
27+
28+
# Timeout in seconds for HTTP requests
29+
HTTP_TIMEOUT = 1
30+
31+
def detect
32+
# Placeholder for EC2 implementation
33+
resource_attributes = {}
34+
35+
begin
36+
# Get IMDSv2 token - this will fail quickly if not on EC2
37+
token = fetch_token
38+
return OpenTelemetry::SDK::Resources::Resource.create({}) if token.nil?
39+
40+
# Get instance identity document which contains most metadata
41+
identity = fetch_identity_document(token)
42+
return OpenTelemetry::SDK::Resources::Resource.create({}) if identity.nil?
43+
44+
hostname = fetch_hostname(token)
45+
46+
# Set resource attributes from the identity document
47+
resource_attributes[OpenTelemetry::SemanticConventions::Resource::CLOUD_PROVIDER] = 'aws'
48+
resource_attributes[OpenTelemetry::SemanticConventions::Resource::CLOUD_PLATFORM] = 'aws_ec2'
49+
resource_attributes[OpenTelemetry::SemanticConventions::Resource::CLOUD_ACCOUNT_ID] = identity['accountId']
50+
resource_attributes[OpenTelemetry::SemanticConventions::Resource::CLOUD_REGION] = identity['region']
51+
resource_attributes[OpenTelemetry::SemanticConventions::Resource::CLOUD_AVAILABILITY_ZONE] = identity['availabilityZone']
52+
53+
resource_attributes[OpenTelemetry::SemanticConventions::Resource::HOST_ID] = identity['instanceId']
54+
resource_attributes[OpenTelemetry::SemanticConventions::Resource::HOST_TYPE] = identity['instanceType']
55+
resource_attributes[OpenTelemetry::SemanticConventions::Resource::HOST_NAME] = hostname
56+
rescue StandardError => e
57+
OpenTelemetry.logger.debug("EC2 resource detection failed: #{e.message}")
58+
return OpenTelemetry::SDK::Resources::Resource.create({})
59+
end
60+
61+
# Filter out nil or empty values
62+
resource_attributes.delete_if { |_key, value| value.nil? || value.empty? }
63+
OpenTelemetry::SDK::Resources::Resource.create(resource_attributes)
64+
end
65+
66+
private
67+
68+
# Fetches an IMDSv2 token from the EC2 metadata service
69+
#
70+
# @return [String, nil] The token or nil if the request failed
71+
def fetch_token
72+
uri = URI.parse("http://#{EC2_METADATA_HOST}#{TOKEN_ENDPOINT}")
73+
request = Net::HTTP::Put.new(uri)
74+
request[TOKEN_TTL_HEADER] = TOKEN_TTL_VALUE
75+
76+
response = make_request(uri, request)
77+
return nil unless response.is_a?(Net::HTTPSuccess)
78+
79+
response.body
80+
end
81+
82+
# Fetches the instance identity document which contains EC2 instance metadata
83+
#
84+
# @param token [String] IMDSv2 token
85+
# @return [Hash, nil] Parsed identity document or nil if the request failed
86+
def fetch_identity_document(token)
87+
uri = URI.parse("http://#{EC2_METADATA_HOST}#{IDENTITY_DOCUMENT_ENDPOINT}")
88+
request = Net::HTTP::Get.new(uri)
89+
request[TOKEN_HEADER] = token
90+
91+
response = make_request(uri, request)
92+
return nil unless response.is_a?(Net::HTTPSuccess)
93+
94+
begin
95+
JSON.parse(response.body)
96+
rescue JSON::ParserError
97+
nil
98+
end
99+
end
100+
101+
# Fetches the EC2 instance hostname
102+
#
103+
# @param token [String] IMDSv2 token
104+
# @return [String, nil] The hostname or nil if the request failed
105+
def fetch_hostname(token)
106+
uri = URI.parse("http://#{EC2_METADATA_HOST}#{HOSTNAME_ENDPOINT}")
107+
request = Net::HTTP::Get.new(uri)
108+
request[TOKEN_HEADER] = token
109+
110+
response = make_request(uri, request)
111+
return nil unless response.is_a?(Net::HTTPSuccess)
112+
113+
response.body
114+
end
115+
116+
# Makes an HTTP request with timeout handling
117+
#
118+
# @param uri [URI] The request URI
119+
# @param request [Net::HTTP::Request] The request to perform
120+
# @return [Net::HTTPResponse, nil] The response or nil if the request failed
121+
def make_request(uri, request)
122+
http = Net::HTTP.new(uri.host, uri.port)
123+
http.open_timeout = HTTP_TIMEOUT
124+
http.read_timeout = HTTP_TIMEOUT
125+
126+
begin
127+
http.request(request)
128+
rescue StandardError => e
129+
OpenTelemetry.logger.debug("EC2 metadata service request failed: #{e.message}")
130+
nil
131+
end
132+
end
133+
end
134+
end
135+
end
136+
end
137+
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright The OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
module OpenTelemetry
8+
module Resource
9+
module Detector
10+
module AWS
11+
VERSION = '0.1.0'
12+
end
13+
end
14+
end
15+
end
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright The OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
lib = File.expand_path('lib', __dir__)
8+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
9+
require 'opentelemetry/resource/detector/aws/version'
10+
11+
Gem::Specification.new do |spec|
12+
spec.name = 'opentelemetry-resource-detector-aws'
13+
spec.version = OpenTelemetry::Resource::Detector::AWS::VERSION
14+
spec.authors = ['OpenTelemetry Authors']
15+
spec.email = ['[email protected]']
16+
17+
spec.summary = 'AWS resource detector for OpenTelemetry'
18+
spec.description = 'AWS resource detector for OpenTelemetry'
19+
spec.homepage = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib'
20+
spec.license = 'Apache-2.0'
21+
22+
spec.files = Dir.glob('lib/**/*.rb') +
23+
['LICENSE', 'README.md']
24+
spec.require_paths = ['lib']
25+
spec.required_ruby_version = '>= 2.5.0'
26+
27+
spec.add_dependency 'opentelemetry-sdk', '~> 1.0'
28+
29+
spec.add_development_dependency 'bundler', '>= 1.17'
30+
spec.add_development_dependency 'minitest', '~> 5.0'
31+
spec.add_development_dependency 'rake', '~> 12.0'
32+
spec.add_development_dependency 'rubocop', '~> 1.42.0'
33+
spec.add_development_dependency 'simplecov', '~> 0.17'
34+
spec.add_development_dependency 'webmock', '~> 3.7'
35+
spec.add_development_dependency 'yard', '~> 0.9'
36+
end

0 commit comments

Comments
 (0)