Skip to content

Commit 89a31d4

Browse files
authored
Merge pull request #182 from solarwinds/NH-105128
NH-105128: init add endpoint logic
2 parents 0595edf + 2b2aec8 commit 89a31d4

File tree

10 files changed

+372
-12
lines changed

10 files changed

+372
-12
lines changed

gemfiles/test_gems.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ group :development, :test do
3232
gem 'simplecov', require: false, group: :test
3333
gem 'simplecov-console'
3434
gem 'webmock' if RUBY_VERSION >= '2.0.0'
35+
gem 'grpc', '~> 1.71'
3536

3637
gem 'opentelemetry-sdk'
3738
gem 'opentelemetry-instrumentation-all'

lib/oboe_metal.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def start
3939
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe # creates an array with the options in the right order
4040
SolarWindsAPM.reporter = Oboe_metal::Reporter.new(*options)
4141
SolarWindsAPM.loaded = true
42-
report_init if (options[22]).zero? # report init at beginning if no after fork enabled
42+
report_init if options[22].zero? # report init at beginning if no after fork enabled
4343
rescue StandardError => e
4444
warn e.message
4545
SolarWindsAPM.loaded = false

lib/solarwinds_apm/oboe_init_options.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,12 @@ def reporter_and_host
126126

127127
def read_and_validate_service_key
128128
service_key_checker = SolarWindsAPM::ServiceKeyChecker.new(@reporter, @lambda_env)
129-
service_key = service_key_checker.read_and_validate_service_key
130-
@service_name = service_key.split(':', 2).last # instance variable used in testing
131-
service_key
129+
if service_key_checker.token.nil?
130+
''
131+
else
132+
@service_name = service_key_checker.service_name # instance variable used in testing
133+
"#{service_key_checker.token}:#{service_key_checker.service_name}"
134+
end
132135
end
133136

134137
def read_and_validate_ec2_md_timeout

lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def create_xtraceoptions_response_value(liboboe_decision, parent_span_context, x
221221
if xtraceoptions.trigger_trace
222222
# If a traceparent header was provided then oboe does not generate the message
223223
tracestring = Utils.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
224-
trigger_msg = tracestring && (liboboe_decision['decision_type']).zero? ? XTRACEOPTIONS_RESP_TRIGGER_IGNORED : liboboe_decision['status_msg']
224+
trigger_msg = tracestring && liboboe_decision['decision_type'].zero? ? XTRACEOPTIONS_RESP_TRIGGER_IGNORED : liboboe_decision['status_msg']
225225
SolarWindsAPM.logger.debug do
226226
"[#{self.class}/#{__method__}] tracestring: #{tracestring}; trigger_msg: #{trigger_msg}"
227227
end
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# frozen_string_literal: true
2+
3+
# © 2023 SolarWinds Worldwide, LLC. All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at:http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8+
9+
module SolarWindsAPM
10+
# OTLPEndPoint
11+
class OTLPEndPoint
12+
SWO_APM_ENDPOINT_REGEX = /^apm\.collector\.([a-z]{2}-\d{2})\.([^.]+)\.solarwinds\.com(?::\d+)?$/
13+
SWO_APM_ENDPOINT_DEFAULT = 'apm.collector.na-01.cloud.solarwinds.com:443'
14+
15+
SWO_OTLP_GENERAL_ENDPOINT_REGEX = %r{^https://otel\.collector\.[a-z0-9-]+\.[a-z0-9-]+\.solarwinds\.com(?::\d+)?$}
16+
SWO_OTLP_SIGNAL_ENDPOINT_REGEX = %r{^https://otel\.collector\.[a-z0-9-]+\.[a-z0-9-]+\.solarwinds\.com(?::\d+)?/v1/(?:logs|metrics|traces)$}
17+
SWO_OTLP_ENDPOINT_DEFAULT = 'https://otel.collector.na-01.cloud.solarwinds.com:443'
18+
19+
OTEL_SIGNAL_TYPE = %w[TRACES METRICS LOGS].freeze
20+
21+
def initialize
22+
@token = nil
23+
end
24+
25+
def config_otlp_token_and_endpoint
26+
matches = ENV['SW_APM_COLLECTOR'].to_s.match(SWO_APM_ENDPOINT_REGEX)
27+
28+
resolve_get_setting_endpoint(matches)
29+
30+
service_key_checker = SolarWindsAPM::ServiceKeyChecker.new('ssl', SolarWindsAPM::Utils.determine_lambda_env)
31+
@token = service_key_checker.token unless service_key_checker.token.nil?
32+
33+
OTEL_SIGNAL_TYPE.each do |data_type|
34+
config_token(data_type)
35+
configure_otlp_endpoint(data_type, matches)
36+
end
37+
end
38+
39+
# APM Libraries should only set the bearer token header as a convenience if:
40+
# The OTEL config for exporter OTLP headers is not already set, i.e. explicitly configured by the end user, AND
41+
# The OTLP export endpoint is SWO, i.e. host is otel.collector.*.*.solarwinds.com
42+
def config_token(data_type)
43+
data_type_upper = data_type.upcase
44+
45+
return unless @token
46+
47+
# puts "#{ENV["OTEL_EXPORTER_OTLP_#{data_type_upper}_ENDPOINT"].to_s.match?(SWO_OTLP_SIGNAL_ENDPOINT_REGEX)}"
48+
# puts "#{ENV['OTEL_EXPORTER_OTLP_ENDPOINT'].to_s.match?(SWO_OTLP_GENERAL_ENDPOINT_REGEX)}"
49+
if ENV["OTEL_EXPORTER_OTLP_#{data_type_upper}_HEADERS"].to_s.empty? && ENV["OTEL_EXPORTER_OTLP_#{data_type_upper}_ENDPOINT"].to_s.match?(SWO_OTLP_SIGNAL_ENDPOINT_REGEX)
50+
ENV["OTEL_EXPORTER_OTLP_#{data_type_upper}_HEADERS"] = "authorization=Bearer #{@token}"
51+
elsif ENV['OTEL_EXPORTER_OTLP_HEADERS'].to_s.empty? && ENV['OTEL_EXPORTER_OTLP_ENDPOINT'].to_s.match?(SWO_OTLP_GENERAL_ENDPOINT_REGEX)
52+
ENV['OTEL_EXPORTER_OTLP_HEADERS'] = "authorization=Bearer #{@token}"
53+
end
54+
end
55+
56+
def configure_otlp_endpoint(data_type, matches)
57+
data_type_upper = data_type.upcase
58+
data_type_lower = data_type.downcase
59+
otlp_endpoint_source = if ENV["OTEL_EXPORTER_OTLP_#{data_type_upper}_ENDPOINT"]
60+
"#{data_type_lower}_endpoint"
61+
elsif ENV['OTEL_EXPORTER_OTLP_ENDPOINT']
62+
'general_endpoint'
63+
else
64+
'no_endpoint'
65+
end
66+
67+
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] otlp_endpoint_source: #{otlp_endpoint_source}" }
68+
69+
return unless otlp_endpoint_source == 'no_endpoint'
70+
71+
if matches&.size == 3
72+
region = matches[1]
73+
env = matches[2]
74+
ENV["OTEL_EXPORTER_OTLP_#{data_type_upper}_ENDPOINT"] = "https://otel.collector.#{region}.#{env}.solarwinds.com:443/v1/#{data_type_lower}"
75+
else
76+
ENV["OTEL_EXPORTER_OTLP_#{data_type_upper}_ENDPOINT"] = "https://otel.collector.na-01.cloud.solarwinds.com:443/v1/#{data_type_lower}"
77+
end
78+
end
79+
80+
# only valid value is apm.collector.*.*.solarwinds.com
81+
# If SW APM config for collector is not set, the fallback: apm.collector.na-01.cloud.solarwinds.com
82+
def resolve_get_setting_endpoint(matches)
83+
return if matches&.size == 3
84+
85+
SolarWindsAPM.logger.warn { "[#{self.class}/#{__method__}] SW_APM_COLLECTOR format invalid: #{ENV.fetch('SW_APM_COLLECTOR', nil)}. Valid formt: apm.collector.*.*.solarwinds.com" }
86+
ENV['SW_APM_COLLECTOR'] = SWO_APM_ENDPOINT_DEFAULT
87+
end
88+
end
89+
end

lib/solarwinds_apm/support/service_key_checker.rb

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,30 @@ module SolarWindsAPM
1010
# ServiceKeyChecker
1111
# It is a service that validate the service_key
1212
class ServiceKeyChecker
13+
attr_reader :token, :service_name
14+
1315
def initialize(reporter, is_lambda)
1416
@reporter = reporter
1517
@is_lambda = is_lambda
18+
read_and_validate_service_key
1619
end
1720

1821
def read_and_validate_service_key
19-
return '' unless @reporter == 'ssl'
20-
return '' if @is_lambda
22+
return unless @reporter == 'ssl'
23+
return if @is_lambda
2124

2225
service_key = fetch_service_key
2326
if service_key.empty?
2427
SolarWindsAPM.logger.error { "[#{self.class}/#{__method__}] SW_APM_SERVICE_KEY not configured." }
25-
return ''
28+
return
2629
end
2730

2831
token, _, service_name = parse_service_key(service_key)
2932
if token.empty?
3033
SolarWindsAPM.logger.error do
3134
"[#{self.class}/#{__method__}] SW_APM_SERVICE_KEY problem. API Token in wrong format. Masked token: #{token[0..3]}...#{token[-4..]}"
3235
end
33-
return ''
36+
return
3437
end
3538

3639
# if no service_name from service_key, then the SW_APM_SERVICE_KEY is not right format, return
@@ -39,7 +42,7 @@ def read_and_validate_service_key
3942
SolarWindsAPM.logger.warn do
4043
"[#{self.class}/#{__method__}] SW_APM_SERVICE_KEY format problem. Service Name is missing."
4144
end
42-
return ''
45+
return
4346
end
4447

4548
service_name = transform_service_name(service_name)
@@ -55,7 +58,8 @@ def read_and_validate_service_key
5558
service_name = transform_service_name(otel_service_name)
5659
end
5760

58-
"#{token}:#{service_name}"
61+
@token = token
62+
@service_name = service_name
5963
end
6064

6165
private

lib/solarwinds_apm/support/utils.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,14 @@ def self.traceparent_from_context(span_context)
2929
end
3030
xtr
3131
end
32+
33+
def self.determine_lambda_env
34+
if ENV['LAMBDA_TASK_ROOT'].to_s.empty? && ENV['AWS_LAMBDA_FUNCTION_NAME'].to_s.empty?
35+
false
36+
else
37+
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] lambda environment - LAMBDA_TASK_ROOT: #{ENV.fetch('LAMBDA_TASK_ROOT', nil)}; AWS_LAMBDA_FUNCTION_NAME: #{ENV.fetch('AWS_LAMBDA_FUNCTION_NAME', nil)}" }
38+
true
39+
end
40+
end
3241
end
3342
end

test/minitest_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
require 'lumberjack'
1919
require 'logging'
2020
require 'bson'
21+
require 'grpc'
2122

2223
require './lib/solarwinds_apm/version'
2324
require './lib/solarwinds_apm/logger'

0 commit comments

Comments
 (0)