Skip to content

Commit 17618fc

Browse files
Merge branch 'main' into dependabot/github_actions/ossf/scorecard-action-2.4.2
2 parents 2f4b09d + 185dfdd commit 17618fc

File tree

8 files changed

+523
-6
lines changed

8 files changed

+523
-6
lines changed

instrumentation/faraday/README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ To install the instrumentation, call `use` with the name of the instrumentation.
1818

1919
```ruby
2020
OpenTelemetry::SDK.configure do |c|
21-
c.use 'OpenTelemetry::Instrumentation::Faraday'
21+
c.use 'OpenTelemetry::Instrumentation::Faraday', {
22+
enable_internal_instrumentation: false
23+
}
2224
end
2325
```
2426

@@ -30,6 +32,11 @@ OpenTelemetry::SDK.configure do |c|
3032
end
3133
```
3234

35+
### Configuration options
36+
This instrumentation offers the following configuration options:
37+
* `enable_internal_instrumentation` (default: `false`): When set to `true`, any spans with
38+
span kind of `internal` are included in traces.
39+
3340
## Examples
3441

3542
Example usage of faraday can be seen in the [`./example/faraday.rb` file](https://github.com/open-telemetry/opentelemetry-ruby-contrib/blob/main/instrumentation/faraday/example/faraday.rb)

instrumentation/faraday/lib/opentelemetry/instrumentation/faraday/instrumentation.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base
2828

2929
option :span_kind, default: :client, validate: %i[client internal]
3030
option :peer_service, default: nil, validate: :string
31+
option :enable_internal_instrumentation, default: false, validate: :boolean
3132

3233
private
3334

instrumentation/faraday/lib/opentelemetry/instrumentation/faraday/middlewares/tracer_middleware.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,13 @@ def call(env)
4040
) do |span|
4141
OpenTelemetry.propagation.inject(env.request_headers)
4242

43-
app.call(env).on_complete { |resp| trace_response(span, resp.status) }
43+
if config[:enable_internal_instrumentation] == false
44+
OpenTelemetry::Common::Utilities.untraced do
45+
app.call(env).on_complete { |resp| trace_response(span, resp.status) }
46+
end
47+
else
48+
app.call(env).on_complete { |resp| trace_response(span, resp.status) }
49+
end
4450
rescue ::Faraday::Error => e
4551
trace_response(span, e.response[:status]) if e.response
4652

resources/aws/README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,12 @@ require 'opentelemetry/resource/detector'
3131

3232
OpenTelemetry::SDK.configure do |c|
3333
# Specify which AWS resource detectors to use
34-
c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:ec2, :ecs, :lambda])
34+
c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:ec2, :ecs, :eks, :lambda])
3535

3636
# Or use just one detector
3737
c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:ec2])
3838
c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:ecs])
39+
c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:eks])
3940
c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:lambda])
4041
end
4142
```
@@ -76,6 +77,21 @@ Populates `cloud`, `container`, and AWS ECS-specific attributes for processes ru
7677
| `aws.log.stream.names` | The CloudWatch log stream names (if awslogs driver is used) |
7778
| `aws.log.stream.arns` | The CloudWatch log stream ARNs (if awslogs driver is used) |
7879

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+
7995
### AWS Lambda Detector
8096
Populates `cloud` and `faas` (Function as a Service) attributes for processes running on AWS Lambda.
8197
| Resource Attribute | Description |
@@ -88,8 +104,6 @@ Populates `cloud` and `faas` (Function as a Service) attributes for processes ru
88104
| `faas.instance` | The Lambda function instance ID from the `AWS_LAMBDA_LOG_STREAM_NAME` environment variable |
89105
| `faas.max_memory` | The Lambda function memory size in MB from the `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` environment variable |
90106

91-
Additional AWS platforms (EKS) will be supported in future versions.
92-
93107
## License
94108

95109
The `opentelemetry-resource-detector-aws` gem is distributed under the Apache 2.0 license. See LICENSE for more information.

resources/aws/lib/opentelemetry/resource/detector/aws.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
require 'opentelemetry/resource/detector/aws/ec2'
88
require 'opentelemetry/resource/detector/aws/ecs'
99
require 'opentelemetry/resource/detector/aws/lambda'
10+
require 'opentelemetry/resource/detector/aws/eks'
1011

1112
module OpenTelemetry
1213
module Resource
@@ -30,6 +31,8 @@ def detect(detectors = [])
3031
EC2.detect
3132
when :ecs
3233
ECS.detect
34+
when :eks
35+
EKS.detect
3336
when :lambda
3437
Lambda.detect
3538
else
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 commit comments

Comments
 (0)