opentelemetry-resource-detector-aws 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c33d7a76ab1f55c8ae05709a72136b9063ef1bd20e6baf2ef1b9ca393d3a9760
4
- data.tar.gz: f6ace2338eaa9089aa950351b6b44864e88d459113f441d977e41a4e8261a0d8
3
+ metadata.gz: 87bc3d87967be6d6ea2a1303f78fc3347c060983b0931a25ace5559b93abd86f
4
+ data.tar.gz: bd9b141d06677d4c47a7dc7d76ac91a6b16b9fe9a63077c8f831eb920f8c7421
5
5
  SHA512:
6
- metadata.gz: 441a273890e25c9b61ff3187d98e86b10da94a5e57497e88c7b310de86ccc2255659522b7dfcaee70718569882504cc46667fdf82fb9da44bf62b70fbd0d3f06
7
- data.tar.gz: 000b2b695a571b483fc840003b7e84fb49d6791d1ee0df4fbae7db9d05f7ee4b620174c286f3e4dbf17fc489ce23954be4569c10a69d42983de1d79847c0ad8f
6
+ metadata.gz: 4c3c596f86e3a8052efcfdd39fb22af027400db89117bf763c1e375420962f8923c1d1636fe1f9c0291204569f831fc946f5fe237ee5ba76d76588b9d22fd2b4
7
+ data.tar.gz: 6ff3808c917d91d77cb231dd0079f64ac89340bfcb40ac264357b427e8538066c67b04277ecd2a1fa5dc4fa94675a8b12717b27e3b369f8d7cc3f42646b60a5e
data/README.md CHANGED
@@ -31,11 +31,13 @@ require 'opentelemetry/resource/detector'
31
31
 
32
32
  OpenTelemetry::SDK.configure do |c|
33
33
  # Specify which AWS resource detectors to use
34
- c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:ec2, :ecs])
34
+ c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:ec2, :ecs, :eks, :lambda])
35
35
 
36
36
  # Or use just one detector
37
37
  c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:ec2])
38
38
  c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:ecs])
39
+ c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:eks])
40
+ c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:lambda])
39
41
  end
40
42
  ```
41
43
 
@@ -75,7 +77,32 @@ Populates `cloud`, `container`, and AWS ECS-specific attributes for processes ru
75
77
  | `aws.log.stream.names` | The CloudWatch log stream names (if awslogs driver is used) |
76
78
  | `aws.log.stream.arns` | The CloudWatch log stream ARNs (if awslogs driver is used) |
77
79
 
78
- Additional AWS platforms (EKS, Lambda) will be supported in future versions.
80
+ ### AWS EKS Detector
81
+
82
+ Populates `cloud`, `container`, and Kubernetes (k8s) attributes for processes running on Amazon EKS.
83
+ | Resource Attribute | Description |
84
+ |--------------------|-------------|
85
+ | `cloud.platform` | The cloud platform. In this context, it's always "aws_eks" |
86
+ | `cloud.provider` | The cloud provider. In this context, it's always "aws" |
87
+ | `container.id` | The container ID from the `/proc/self/cgroup` file |
88
+ | `k8s.cluster.name` | The name of the EKS cluster from the `cluster-info` config map in the `amazon-cloudwatch` namespace |
89
+
90
+ The EKS detector verifies that the process is running on EKS by checking:
91
+ 1. Presence of Kubernetes service account token and certificate
92
+ 2. Ability to access the `aws-auth` config map in the `kube-system` namespace
93
+ 3. Availability of either cluster name or container ID
94
+
95
+ ### AWS Lambda Detector
96
+ Populates `cloud` and `faas` (Function as a Service) attributes for processes running on AWS Lambda.
97
+ | Resource Attribute | Description |
98
+ |--------------------|-------------|
99
+ | `cloud.platform` | The cloud platform. In this context, it's always "aws_lambda" |
100
+ | `cloud.provider` | The cloud provider. In this context, it's always "aws" |
101
+ | `cloud.region` | The AWS region from the `AWS_REGION` environment variable |
102
+ | `faas.name` | The Lambda function name from the `AWS_LAMBDA_FUNCTION_NAME` environment variable |
103
+ | `faas.version` | The Lambda function version from the `AWS_LAMBDA_FUNCTION_VERSION` environment variable |
104
+ | `faas.instance` | The Lambda function instance ID from the `AWS_LAMBDA_LOG_STREAM_NAME` environment variable |
105
+ | `faas.max_memory` | The Lambda function memory size in MB from the `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` environment variable |
79
106
 
80
107
  ## License
81
108
 
@@ -7,6 +7,7 @@
7
7
  require 'net/http'
8
8
  require 'json'
9
9
  require 'opentelemetry/common'
10
+ require 'opentelemetry/semantic_conventions/resource'
10
11
 
11
12
  module OpenTelemetry
12
13
  module Resource
@@ -59,7 +60,7 @@ module OpenTelemetry
59
60
  resource_attributes[RESOURCE::HOST_TYPE] = identity['instanceType']
60
61
  resource_attributes[RESOURCE::HOST_NAME] = hostname
61
62
  rescue StandardError => e
62
- OpenTelemetry.logger.debug("EC2 resource detection failed: #{e.message}")
63
+ OpenTelemetry.handle_error(exception: e, message: 'EC2 resource detection failed')
63
64
  return OpenTelemetry::SDK::Resources::Resource.create({})
64
65
  end
65
66
 
@@ -133,7 +134,7 @@ module OpenTelemetry
133
134
  http.request(request)
134
135
  end
135
136
  rescue StandardError => e
136
- OpenTelemetry.logger.debug("EC2 metadata service request failed: #{e.message}")
137
+ OpenTelemetry.handle_error(exception: e, message: 'EC2 metadata service request failed')
137
138
  nil
138
139
  end
139
140
  end
@@ -8,6 +8,7 @@ require 'net/http'
8
8
  require 'json'
9
9
  require 'socket'
10
10
  require 'opentelemetry/common'
11
+ require 'opentelemetry/semantic_conventions/resource'
11
12
 
12
13
  module OpenTelemetry
13
14
  module Resource
@@ -68,7 +69,7 @@ module OpenTelemetry
68
69
  logs_attributes = get_logs_resource(container_metadata)
69
70
  resource_attributes.merge!(logs_attributes)
70
71
  rescue StandardError => e
71
- OpenTelemetry.logger.debug("ECS resource detection failed: #{e.message}")
72
+ OpenTelemetry.handle_error(exception: e, message: 'ECS resource detection failed')
72
73
  return OpenTelemetry::SDK::Resources::Resource.create({})
73
74
  end
74
75
 
@@ -92,7 +93,7 @@ module OpenTelemetry
92
93
  end
93
94
  end
94
95
  rescue Errno::ENOENT => e
95
- OpenTelemetry.logger.debug("Failed to get container ID on ECS: #{e.message}")
96
+ OpenTelemetry.handle_error(exception: e, message: 'Failed to get container ID on ECS')
96
97
  end
97
98
 
98
99
  ''
@@ -139,7 +140,7 @@ module OpenTelemetry
139
140
  log_attributes[RESOURCE::AWS_LOG_STREAM_NAMES] = [logs_stream_name].compact
140
141
  log_attributes[RESOURCE::AWS_LOG_STREAM_ARNS] = [logs_stream_arn].compact
141
142
  else
142
- OpenTelemetry.logger.debug("The metadata endpoint v4 has returned 'awslogs' as 'LogDriver', but there is no 'LogOptions' data")
143
+ OpenTelemetry.handle_error(message: 'The metadata endpoint v4 has returned \'awslogs\' as \'LogDriver\', but there is no \'LogOptions\' data')
143
144
  end
144
145
  end
145
146
 
@@ -0,0 +1,174 @@
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
+ require 'openssl'
10
+ require 'uri'
11
+ require 'opentelemetry/common'
12
+ require 'opentelemetry/semantic_conventions/resource'
13
+
14
+ module OpenTelemetry
15
+ module Resource
16
+ module Detector
17
+ module AWS
18
+ # EKS contains detect class method for determining EKS resource attributes
19
+ module EKS
20
+ extend self
21
+
22
+ # Container ID length from cgroup file
23
+ CONTAINER_ID_LENGTH = 64
24
+
25
+ # HTTP request timeout in seconds
26
+ HTTP_TIMEOUT = 5
27
+
28
+ # Kubernetes token and certificate paths
29
+ TOKEN_PATH = '/var/run/secrets/kubernetes.io/serviceaccount/token'
30
+ CERT_PATH = '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'
31
+
32
+ # Kubernetes API paths
33
+ AWS_AUTH_PATH = '/api/v1/namespaces/kube-system/configmaps/aws-auth'
34
+ CLUSTER_INFO_PATH = '/api/v1/namespaces/amazon-cloudwatch/configmaps/cluster-info'
35
+
36
+ # Create a constant for resource semantic conventions
37
+ RESOURCE = OpenTelemetry::SemanticConventions::Resource
38
+
39
+ def detect
40
+ # Return empty resource if not running on K8s
41
+ return OpenTelemetry::SDK::Resources::Resource.create({}) unless k8s?
42
+
43
+ resource_attributes = {}
44
+
45
+ begin
46
+ # Get K8s credentials
47
+ cred_value = k8s_cred_value
48
+
49
+ # Verify this is an EKS cluster
50
+ unless eks?(cred_value)
51
+ OpenTelemetry.logger.debug('Could not confirm process is running on EKS')
52
+ return OpenTelemetry::SDK::Resources::Resource.create({})
53
+ end
54
+
55
+ # Get cluster name and container ID
56
+ cluster_name_val = cluster_name(cred_value)
57
+ container_id_val = container_id
58
+
59
+ if container_id_val.empty? && cluster_name_val.empty?
60
+ OpenTelemetry.logger.debug('Neither cluster name nor container ID found on EKS process')
61
+ return OpenTelemetry::SDK::Resources::Resource.create({})
62
+ end
63
+
64
+ # Set resource attributes
65
+ resource_attributes[RESOURCE::CLOUD_PROVIDER] = 'aws'
66
+ resource_attributes[RESOURCE::CLOUD_PLATFORM] = 'aws_eks'
67
+ resource_attributes[RESOURCE::K8S_CLUSTER_NAME] = cluster_name_val unless cluster_name_val.empty?
68
+ resource_attributes[RESOURCE::CONTAINER_ID] = container_id_val unless container_id_val.empty?
69
+ rescue StandardError => e
70
+ OpenTelemetry.logger.debug("EKS resource detection failed: #{e.message}")
71
+ return OpenTelemetry::SDK::Resources::Resource.create({})
72
+ end
73
+
74
+ resource_attributes.delete_if { |_key, value| value.nil? || value.empty? }
75
+ OpenTelemetry::SDK::Resources::Resource.create(resource_attributes)
76
+ end
77
+
78
+ private
79
+
80
+ # Check if running on K8s
81
+ #
82
+ # @return [Boolean] true if running on K8s
83
+ def k8s?
84
+ File.exist?(TOKEN_PATH) && File.exist?(CERT_PATH)
85
+ end
86
+
87
+ # Get K8s token
88
+ #
89
+ # @return [String] K8s token
90
+ # @raise [StandardError] if token could not be read
91
+ def k8s_cred_value
92
+ token = File.read(TOKEN_PATH).strip
93
+ "Bearer #{token}"
94
+ rescue StandardError => e
95
+ OpenTelemetry.logger.debug("Failed to get k8s token: #{e.message}")
96
+ raise e
97
+ end
98
+
99
+ # Check if running on EKS
100
+ #
101
+ # @param cred_value [String] K8s credentials
102
+ # @return [Boolean] true if running on EKS
103
+ def eks?(cred_value)
104
+ # Just try to to access the aws-auth configmap
105
+ # If it exists and we can access it, we're on EKS
106
+ aws_http_request(AWS_AUTH_PATH, cred_value)
107
+ true
108
+ rescue StandardError
109
+ false
110
+ end
111
+
112
+ # Get EKS cluster name
113
+ #
114
+ # @param cred_value [String] K8s credentials
115
+ # @return [String] Cluster name or empty string if not found
116
+ def cluster_name(cred_value)
117
+ begin
118
+ response = aws_http_request(CLUSTER_INFO_PATH, cred_value)
119
+ cluster_info = JSON.parse(response)
120
+ return cluster_info['data']['cluster.name'] if cluster_info['data'] && cluster_info['data']['cluster.name']
121
+ rescue StandardError => e
122
+ OpenTelemetry.logger.debug("Cannot get cluster name on EKS: #{e.message}")
123
+ end
124
+ ''
125
+ end
126
+
127
+ # Get container ID from cgroup file
128
+ #
129
+ # @return [String] Container ID or empty string if not found
130
+ def container_id
131
+ begin
132
+ File.open('/proc/self/cgroup', 'r') do |file|
133
+ file.each_line do |line|
134
+ line = line.strip
135
+ # Look for container ID (64 chars) at the end of the line
136
+ return line[-CONTAINER_ID_LENGTH..-1] if line.length > CONTAINER_ID_LENGTH
137
+ end
138
+ end
139
+ rescue StandardError => e
140
+ OpenTelemetry.logger.debug("Failed to get container ID on EKS: #{e.message}")
141
+ end
142
+ ''
143
+ end
144
+
145
+ # Make HTTP GET request to K8s API
146
+ #
147
+ # @param path [String] API path
148
+ # @param cred_value [String] Authorization header value
149
+ # @return [String] Response body
150
+ # @raise [StandardError] if request fails
151
+ def aws_http_request(path, cred_value)
152
+ uri = URI.parse("https://kubernetes.default.svc#{path}")
153
+ http = Net::HTTP.new(uri.host, uri.port)
154
+ http.use_ssl = true
155
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
156
+ http.ca_file = CERT_PATH
157
+ http.open_timeout = HTTP_TIMEOUT
158
+ http.read_timeout = HTTP_TIMEOUT
159
+
160
+ request = Net::HTTP::Get.new(uri)
161
+ request['Authorization'] = cred_value
162
+
163
+ OpenTelemetry::Common::Utilities.untraced do
164
+ response = http.request(request)
165
+ raise "HTTP request failed with status #{response.code}" unless response.is_a?(Net::HTTPSuccess)
166
+
167
+ response.body
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ require 'opentelemetry/semantic_conventions/resource'
8
+
9
+ module OpenTelemetry
10
+ module Resource
11
+ module Detector
12
+ module AWS
13
+ # Lambda contains detect class method for determining Lambda resource attributes
14
+ module Lambda
15
+ extend self
16
+
17
+ # Create a constant for resource semantic conventions
18
+ RESOURCE = OpenTelemetry::SemanticConventions::Resource
19
+
20
+ def detect
21
+ # Return empty resource if not running on Lambda
22
+ return OpenTelemetry::SDK::Resources::Resource.create({}) unless lambda_environment?
23
+
24
+ resource_attributes = {}
25
+
26
+ begin
27
+ # Set Lambda-specific attributes from environment variables
28
+ resource_attributes[RESOURCE::CLOUD_PROVIDER] = 'aws'
29
+ resource_attributes[RESOURCE::CLOUD_PLATFORM] = 'aws_lambda'
30
+ resource_attributes[RESOURCE::CLOUD_REGION] = ENV.fetch('AWS_REGION', nil)
31
+ resource_attributes[RESOURCE::FAAS_NAME] = ENV.fetch('AWS_LAMBDA_FUNCTION_NAME', nil)
32
+ resource_attributes[RESOURCE::FAAS_VERSION] = ENV.fetch('AWS_LAMBDA_FUNCTION_VERSION', nil)
33
+ resource_attributes[RESOURCE::FAAS_INSTANCE] = ENV.fetch('AWS_LAMBDA_LOG_STREAM_NAME', nil)
34
+
35
+ # Convert memory size to integer
36
+ resource_attributes[RESOURCE::FAAS_MAX_MEMORY] = ENV['AWS_LAMBDA_FUNCTION_MEMORY_SIZE'].to_i if ENV['AWS_LAMBDA_FUNCTION_MEMORY_SIZE']
37
+ rescue StandardError => e
38
+ OpenTelemetry.handle_error(exception: e, message: 'Lambda resource detection failed')
39
+ return OpenTelemetry::SDK::Resources::Resource.create({})
40
+ end
41
+
42
+ # Filter out nil or empty values
43
+ # Note: we need to handle integers differently since they don't respond to empty?
44
+ resource_attributes.delete_if do |_key, value|
45
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
46
+ end
47
+
48
+ OpenTelemetry::SDK::Resources::Resource.create(resource_attributes)
49
+ end
50
+
51
+ private
52
+
53
+ # Determines if the current environment is AWS Lambda
54
+ #
55
+ # @return [Boolean] true if running on AWS Lambda
56
+ def lambda_environment?
57
+ # Check for Lambda-specific environment variables
58
+ !ENV['AWS_LAMBDA_FUNCTION_NAME'].nil? &&
59
+ !ENV['AWS_LAMBDA_FUNCTION_VERSION'].nil? &&
60
+ !ENV['AWS_LAMBDA_LOG_STREAM_NAME'].nil?
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -8,7 +8,7 @@ module OpenTelemetry
8
8
  module Resource
9
9
  module Detector
10
10
  module AWS
11
- VERSION = '0.2.0'
11
+ VERSION = '0.4.0'
12
12
  end
13
13
  end
14
14
  end
@@ -6,6 +6,8 @@
6
6
 
7
7
  require 'opentelemetry/resource/detector/aws/ec2'
8
8
  require 'opentelemetry/resource/detector/aws/ecs'
9
+ require 'opentelemetry/resource/detector/aws/lambda'
10
+ require 'opentelemetry/resource/detector/aws/eks'
9
11
 
10
12
  module OpenTelemetry
11
13
  module Resource
@@ -29,6 +31,10 @@ module OpenTelemetry
29
31
  EC2.detect
30
32
  when :ecs
31
33
  ECS.detect
34
+ when :eks
35
+ EKS.detect
36
+ when :lambda
37
+ Lambda.detect
32
38
  else
33
39
  OpenTelemetry.logger.warn("Unknown AWS resource detector: #{detector}")
34
40
  OpenTelemetry::SDK::Resources::Resource.create({})
@@ -4,4 +4,5 @@
4
4
  #
5
5
  # SPDX-License-Identifier: Apache-2.0
6
6
 
7
+ require 'opentelemetry/semantic_conventions/resource'
7
8
  require_relative 'opentelemetry/resource/detector'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opentelemetry-resource-detector-aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenTelemetry Authors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-29 00:00:00.000000000 Z
11
+ date: 2025-06-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opentelemetry-sdk
@@ -38,6 +38,8 @@ files:
38
38
  - lib/opentelemetry/resource/detector/aws.rb
39
39
  - lib/opentelemetry/resource/detector/aws/ec2.rb
40
40
  - lib/opentelemetry/resource/detector/aws/ecs.rb
41
+ - lib/opentelemetry/resource/detector/aws/eks.rb
42
+ - lib/opentelemetry/resource/detector/aws/lambda.rb
41
43
  - lib/opentelemetry/resource/detector/aws/version.rb
42
44
  homepage: https://github.com/open-telemetry/opentelemetry-ruby-contrib
43
45
  licenses: