instana 1.197.0 → 1.199.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a25aa837fcbb248cb39db1f57b7bdc8904810d5e63d87be7e12d912b7b9aea67
4
- data.tar.gz: 836dc1f8d7ffc4c8c19e3fa90867a306fe7d407fb88a14481d44bde1dc482285
3
+ metadata.gz: 59560cefe64546d0f6b072839e04cc21d5abe77087111a66acd48b16433bd6b3
4
+ data.tar.gz: f56219f6107b11353e51bac144119d698baa3e4d5859929cd59a47627761cb02
5
5
  SHA512:
6
- metadata.gz: b8f3ba0fbf4a015ff1522a3c24e6b219594d2c266bc46427a93979e6ee159272d2c23a8fc53e4720021c5c531ad64ffd1d1f2644b8eb41497aaf8950c99ed8b6
7
- data.tar.gz: 44e5b93aff42f542eaee494128e973a9447210fbffde21c4b361f1b00729c8b3378634241d64039453ea3dda0dc2a0d207473a0c5631f1f09e77726b2a827532
6
+ metadata.gz: b3c9cc9ab3175e4f47cf722c91a8c40189fe27fd21fd4c71fbcbf1d82c65af205c3afb9636add9418ba2c52a3adf05940c948819700491c9a51d719b40566781
7
+ data.tar.gz: 0daca1718f70837be8504ab239a034432c50cf46a1380bfcf46b40059638a31455b8904c2b5019b7f880787acbe0462ada2457f135f451a680fa2253b68a0ce0
@@ -15,7 +15,15 @@ module Instana
15
15
  end
16
16
 
17
17
  def setup
18
- @delegate = if @fargate_metadata_uri && ENV.key?('INSTANA_ENDPOINT_URL')
18
+ @delegate = if ENV.key?('_HANDLER')
19
+ ServerlessAgent.new([Snapshot::LambdaFunction.new])
20
+ elsif ENV.key?('K_REVISION') && ENV.key?('INSTANA_ENDPOINT_URL')
21
+ ServerlessAgent.new([
22
+ Snapshot::GoogleCloudRunProcess.new,
23
+ Snapshot::GoogleCloudRunInstance.new,
24
+ Snapshot::RubyProcess.new
25
+ ])
26
+ elsif @fargate_metadata_uri && ENV.key?('INSTANA_ENDPOINT_URL')
19
27
  ServerlessAgent.new(fargate_snapshots)
20
28
  else
21
29
  HostAgent.new
@@ -30,6 +30,9 @@ module Instana
30
30
  end
31
31
  end
32
32
 
33
+ alias start spawn_background_thread
34
+ alias after_fork spawn_background_thread
35
+
33
36
  # @return [Boolean] true if the agent able to send spans to the backend
34
37
  def ready?
35
38
  ENV.key?('INSTANA_TEST') || !@discovery.value.nil?
@@ -33,9 +33,10 @@ module Instana
33
33
  end
34
34
 
35
35
  def initialize(host, port, use_ssl: false)
36
+ timeout = Integer(ENV.fetch('INSTANA_TIMEOUT', 500))
36
37
  @host = host
37
38
  @port = port
38
- @client = Net::HTTP.start(host, port, use_ssl: use_ssl)
39
+ @client = Net::HTTP.start(host, port, use_ssl: use_ssl, read_timeout: timeout)
39
40
  end
40
41
 
41
42
  # Send a request to the backend. If data is a {Hash},
@@ -39,12 +39,10 @@ module Instana
39
39
 
40
40
  # @return [Hash, NilClass] the backend friendly description of the current in process collector
41
41
  def source
42
- return @source if @source
43
-
44
42
  snapshot = @snapshots.detect { |s| s.respond_to?(:source) }
45
43
 
46
44
  if snapshot
47
- @source = snapshot.source
45
+ snapshot.source
48
46
  else
49
47
  @logger.warn('Unable to find a snapshot which provides a source.')
50
48
  {}
@@ -58,21 +56,10 @@ module Instana
58
56
 
59
57
  # @return [Hash] values which are removed from urls sent to the backend
60
58
  def secret_values
61
- # TODO: Parse from env
62
59
  matcher, *keys = @secrets.split(/[:,]/)
63
60
  {'matcher' => matcher, 'list' => keys}
64
61
  end
65
62
 
66
- private
67
-
68
- def request_headers
69
- {
70
- 'X-Instana-Host' => host_name,
71
- 'X-Instana-Key' => ENV['INSTANA_AGENT_KEY'],
72
- 'X-Instana-Time' => (Time.now.to_i * 1000).to_s
73
- }
74
- end
75
-
76
63
  def send_bundle
77
64
  spans = @processor.queued_spans
78
65
  bundle = {
@@ -90,6 +77,16 @@ module Instana
90
77
  @logger.warn("Recived a `#{response.code}` when sending data.")
91
78
  end
92
79
 
80
+ private
81
+
82
+ def request_headers
83
+ {
84
+ 'X-Instana-Host' => host_name,
85
+ 'X-Instana-Key' => ENV['INSTANA_AGENT_KEY'],
86
+ 'X-Instana-Time' => (Time.now.to_i * 1000).to_s
87
+ }
88
+ end
89
+
93
90
  def agent_snapshots
94
91
  @snapshots.map do |snapshot|
95
92
  begin # rubocop:disable Style/RedundantBegin, Lint/RedundantCopDisableDirective
@@ -102,12 +99,10 @@ module Instana
102
99
  end
103
100
 
104
101
  def host_name
105
- return @host_name if @host_name
106
-
107
102
  snapshot = @snapshots.detect { |s| s.respond_to?(:host_name) }
108
103
 
109
104
  if snapshot
110
- @host_name = snapshot.host_name
105
+ snapshot.host_name
111
106
  else
112
107
  @logger.warn('Unable to find a snapshot which provides a host_name.')
113
108
  ''
data/lib/instana/base.rb CHANGED
@@ -13,6 +13,7 @@ module Instana
13
13
  attr_accessor :config
14
14
  attr_accessor :pid
15
15
  attr_reader :secrets
16
+ attr_reader :serverless
16
17
 
17
18
  ##
18
19
  # setup
@@ -25,6 +26,7 @@ module Instana
25
26
  @tracer = ::Instana::Tracer.new
26
27
  @processor = ::Instana::Processor.new
27
28
  @secrets = ::Instana::Secrets.new
29
+ @serverless = ::Instana::Serverless.new
28
30
  end
29
31
 
30
32
  def logger
@@ -13,21 +13,31 @@ module Instana
13
13
  def remove_from_query(str, secret_values = Instana.agent.secret_values)
14
14
  return str unless secret_values
15
15
 
16
- url = URI(str)
17
- params = url.scheme ? CGI.parse(url.query || '') : CGI.parse(url.to_s)
16
+ begin
17
+ url = URI(str)
18
+ params = url.scheme ? CGI.parse(url.query || '') : CGI.parse(url.to_s)
19
+
20
+ redacted = redact(params, secret_values)
21
+
22
+ url.query = URI.encode_www_form(redacted)
23
+ url.scheme ? CGI.unescape(url.to_s) : CGI.unescape(url.query)
24
+ rescue URI::InvalidURIError => _e
25
+ params = CGI.parse(str || '')
26
+ redacted = redact(params, secret_values)
27
+ CGI.unescape(URI.encode_www_form(redacted))
28
+ end
29
+ end
30
+
31
+ private
18
32
 
19
- redacted = params.map do |k, v|
33
+ def redact(params, secret_values)
34
+ params.map do |k, v|
20
35
  needs_redaction = secret_values['list']
21
36
  .any? { |t| matcher(secret_values['matcher']).(t,k) }
22
37
  [k, needs_redaction ? '<redacted>' : v]
23
38
  end
24
-
25
- url.query = URI.encode_www_form(redacted)
26
- url.scheme ? CGI.unescape(url.to_s) : CGI.unescape(url.query)
27
39
  end
28
40
 
29
- private
30
-
31
41
  def matcher(name)
32
42
  case name
33
43
  when 'equals-ignore-case'
@@ -0,0 +1,139 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ require 'base64'
5
+ require 'zlib'
6
+
7
+ # :nocov:
8
+ begin
9
+ require 'instana/instrumentation/instrumented_request'
10
+ rescue LoadError => _e
11
+ Instana.logger.warn("Unable to Instana::InstrumentedRequest. HTTP based triggers won't generate spans.")
12
+ end
13
+ # :nocov:
14
+
15
+ module Instana
16
+ # @since 1.198.0
17
+ class Serverless
18
+ def initialize(agent: ::Instana.agent, tracer: ::Instana.tracer, logger: ::Instana.logger)
19
+ @agent = agent
20
+ @tracer = tracer
21
+ @logger = logger
22
+ end
23
+
24
+ def wrap_aws(event, context, &block)
25
+ Thread.current[:instana_function_arn] = [context.invoked_function_arn, context.function_version].join(':')
26
+ trigger, event_tags, span_context = trigger_from_event(event)
27
+
28
+ tags = {
29
+ lambda: {
30
+ arn: context.invoked_function_arn,
31
+ functionName: context.function_name,
32
+ functionVersion: context.function_version,
33
+ runtime: 'ruby',
34
+ trigger: trigger
35
+ }
36
+ }
37
+
38
+ if event_tags.key?(:http)
39
+ tags = tags.merge(event_tags)
40
+ else
41
+ tags[:lambda] = tags[:lambda].merge(event_tags)
42
+ end
43
+
44
+ @tracer.start_or_continue_trace(:'aws.lambda.entry', tags, span_context, &block)
45
+ ensure
46
+ begin
47
+ @agent.send_bundle
48
+ rescue StandardError => e
49
+ @logger.error(e.message)
50
+ end
51
+ Thread.current[:instana_function_arn] = nil
52
+ end
53
+
54
+ private
55
+
56
+ def trigger_from_event(event) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
57
+ case event
58
+ when ->(e) { defined?(::Instana::InstrumentedRequest) && e.is_a?(Hash) && e.key?('requestContext') && e['requestContext'].key?('elb') }
59
+ request = InstrumentedRequest.new(event_to_rack(event))
60
+ ['aws:application.load.balancer', {http: request.request_tags}, request.incoming_context]
61
+ when ->(e) { defined?(::Instana::InstrumentedRequest) && e.is_a?(Hash) && e.key?('httpMethod') && e.key?('path') && e.key?('headers') }
62
+ request = InstrumentedRequest.new(event_to_rack(event))
63
+ ['aws:api.gateway', {http: request.request_tags}, request.incoming_context]
64
+ when ->(e) { e.is_a?(Hash) && e['source'] == 'aws.events' && e['detail-type'] == 'Scheduled Event' }
65
+ tags = decode_cloudwatch_events(event)
66
+ ['aws:cloudwatch.events', {cw: tags}, {}]
67
+ when ->(e) { e.is_a?(Hash) && e.key?('awslogs') }
68
+ tags = decode_cloudwatch_logs(event)
69
+ ['aws:cloudwatch.logs', {cw: tags}, {}]
70
+ when ->(e) { e.is_a?(Hash) && e.key?('Records') && e['Records'].is_a?(Array) && e['Records'].first && e['Records'].first['source'] == 'aws:s3' }
71
+ tags = decode_s3(event)
72
+ ['aws:s3', {s3: tags}, {}]
73
+ when ->(e) { e.is_a?(Hash) && e.key?('Records') && e['Records'].is_a?(Array) && e['Records'].first && e['Records'].first['source'] == 'aws:sqs' }
74
+ tags = decode_sqs(event)
75
+ ['aws:sqs', {sqs: tags}, {}]
76
+ else
77
+ ['aws:api.gateway.noproxy', {}, {}]
78
+ end
79
+ end
80
+
81
+ def event_to_rack(event)
82
+ event['headers']
83
+ .transform_keys { |k| "HTTP_#{k.gsub('-', '_').upcase}" }
84
+ .merge(
85
+ 'QUERY_STRING' => URI.encode_www_form(event['queryStringParameters'] || {}),
86
+ 'PATH_INFO' => event['path'],
87
+ 'REQUEST_METHOD' => event['httpMethod']
88
+ )
89
+ end
90
+
91
+ def decode_cloudwatch_events(event)
92
+ {
93
+ events: {
94
+ id: event['id'],
95
+ resources: event['resources']
96
+ }
97
+ }
98
+ end
99
+
100
+ def decode_cloudwatch_logs(event)
101
+ logs = begin
102
+ payload = JSON.parse(Zlib::Inflate.inflate(Base64.decode64(event['awslogs']['data'])))
103
+
104
+ {
105
+ group: payload['logGroup'],
106
+ stream: payload['logStream']
107
+ }
108
+ rescue StandardError => e
109
+ {
110
+ decodingError: e.message
111
+ }
112
+ end
113
+
114
+ {logs: logs}
115
+ end
116
+
117
+ def decode_s3(event)
118
+ span_events = event['Records'].map do |record|
119
+ {
120
+ name: record['eventName'],
121
+ bucket: record['s3'] && record['s3']['bucket'] ? record['s3']['bucket']['name'] : nil,
122
+ object: record['s3'] && record['s3']['object'] ? record['s3']['object']['key'] : nil
123
+ }
124
+ end
125
+
126
+ {events: span_events}
127
+ end
128
+
129
+ def decode_sqs(event)
130
+ span_events = event['Records'].map do |record|
131
+ {
132
+ queue: record['eventSourceARN']
133
+ }
134
+ end
135
+
136
+ {messages: span_events}
137
+ end
138
+ end
139
+ end
data/lib/instana/setup.rb CHANGED
@@ -9,6 +9,8 @@ require "instana/secrets"
9
9
  require "instana/tracer"
10
10
  require "instana/tracing/processor"
11
11
 
12
+ require 'instana/serverless'
13
+
12
14
  require 'instana/activator'
13
15
 
14
16
  require 'instana/backend/request_client'
@@ -21,6 +23,9 @@ require 'instana/snapshot/fargate_process'
21
23
  require 'instana/snapshot/fargate_task'
22
24
  require 'instana/snapshot/fargate_container'
23
25
  require 'instana/snapshot/docker_container'
26
+ require 'instana/snapshot/lambda_function'
27
+ require 'instana/snapshot/google_cloud_run_instance'
28
+ require 'instana/snapshot/google_cloud_run_process'
24
29
 
25
30
  require 'instana/backend/host_agent_lookup'
26
31
  require 'instana/backend/host_agent_activation_observer'
@@ -0,0 +1,69 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ module Snapshot
6
+ # @since 1.199
7
+ class GoogleCloudRunInstance
8
+ ID = 'com.instana.plugin.gcp.run.revision.instance'.freeze
9
+
10
+ def initialize(metadata_uri: 'http://metadata.google.internal')
11
+ @metadata_uri = URI(metadata_uri)
12
+ @client = Backend::RequestClient.new(@metadata_uri.host, @metadata_uri.port, use_ssl: @metadata_uri.scheme == "https")
13
+ end
14
+
15
+ def entity_id
16
+ lookup('/computeMetadata/v1/instance/id')
17
+ end
18
+
19
+ def data
20
+ {
21
+ runtime: 'ruby',
22
+ region: gcp_region,
23
+ service: ENV['K_SERVICE'],
24
+ configuration: ENV['K_CONFIGURATION'],
25
+ revision: ENV['K_REVISION'],
26
+ instanceId: entity_id,
27
+ port: ENV['PORT'],
28
+ numericProjectId: lookup('/computeMetadata/v1/project/numeric-project-id'),
29
+ projectId: lookup('/computeMetadata/v1/project/project-id')
30
+ }.compact
31
+ end
32
+
33
+ def snapshot
34
+ {
35
+ name: ID,
36
+ entityId: entity_id,
37
+ data: data
38
+ }
39
+ end
40
+
41
+ def source
42
+ {
43
+ hl: true,
44
+ cp: 'gcp',
45
+ e: entity_id
46
+ }
47
+ end
48
+
49
+ def host_name
50
+ "gcp:cloud-run:revision:#{ENV['K_REVISION']}"
51
+ end
52
+
53
+ private
54
+
55
+ def gcp_region
56
+ lookup('/computeMetadata/v1/instance/zone').split('/').last
57
+ end
58
+
59
+ def lookup(resource)
60
+ path = @metadata_uri.path + resource
61
+ response = @client.send_request('GET', path, nil, {'Metadata-Flavor' => 'Google'})
62
+
63
+ raise "Unable to get `#{path}`. Got `#{response.code}` `#{response['location']}`." unless response.ok?
64
+
65
+ response.body
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,58 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ module Snapshot
6
+ # @since 1.199.0
7
+ class GoogleCloudRunProcess
8
+ ID = 'com.instana.plugin.process'.freeze
9
+
10
+ def initialize(metadata_uri: 'http://metadata.google.internal')
11
+ @metadata_uri = URI(metadata_uri)
12
+ @client = Backend::RequestClient.new(@metadata_uri.host, @metadata_uri.port, use_ssl: @metadata_uri.scheme == "https")
13
+ @start_time = Time.now
14
+ end
15
+
16
+ def entity_id
17
+ Process.pid.to_s
18
+ end
19
+
20
+ def data
21
+ proc_table = Sys::ProcTable.ps(pid: Process.pid)
22
+ process = Backend::ProcessInfo.new(proc_table)
23
+
24
+ {
25
+ pid: process.pid.to_i,
26
+ env: ENV.to_h,
27
+ exec: process.name,
28
+ args: process.arguments,
29
+ user: process.uid,
30
+ group: process.gid,
31
+ start: @start_time.to_i * 1000,
32
+ containerType: 'gcpCloudRunInstance',
33
+ container: lookup('/computeMetadata/v1/instance/id'),
34
+ "com.instana.plugin.host.name": "gcp:cloud-run:revision:#{ENV['K_REVISION']}"
35
+ }
36
+ end
37
+
38
+ def snapshot
39
+ {
40
+ name: ID,
41
+ entityId: entity_id,
42
+ data: data
43
+ }
44
+ end
45
+
46
+ private
47
+
48
+ def lookup(resource)
49
+ path = @metadata_uri.path + resource
50
+ response = @client.send_request('GET', path, nil, {'Metadata-Flavor' => 'Google'})
51
+
52
+ raise "Unable to get `#{path}`. Got `#{response.code}` `#{response['location']}`." unless response.ok?
53
+
54
+ response.body
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,5 +1,8 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
1
4
  module Instana
2
- module Backend
5
+ module Snapshot
3
6
  # @since 1.198.0
4
7
  class LambdaFunction
5
8
  ID = "com.instana.plugin.aws.lambda".freeze
@@ -22,15 +25,15 @@ module Instana
22
25
 
23
26
  def source
24
27
  {
25
-
28
+ hl: true,
29
+ cp: "aws",
30
+ e: entity_id
26
31
  }
27
32
  end
28
33
 
29
34
  def host_name
30
35
  entity_id
31
36
  end
32
-
33
-
34
37
  end
35
38
  end
36
39
  end
@@ -6,8 +6,8 @@ module Instana
6
6
  REGISTERED_SPANS = [ :actioncontroller, :actionview, :activerecord, :excon,
7
7
  :memcache, :'net-http', :rack, :render, :'rpc-client',
8
8
  :'rpc-server', :'sidekiq-client', :'sidekiq-worker',
9
- :redis, :'resque-client', :'resque-worker', :'graphql.server', :dynamodb, :s3, :sns, :sqs ].freeze
10
- ENTRY_SPANS = [ :rack, :'resque-worker', :'rpc-server', :'sidekiq-worker', :'graphql.server', :sqs ].freeze
9
+ :redis, :'resque-client', :'resque-worker', :'graphql.server', :dynamodb, :s3, :sns, :sqs, :'aws.lambda.entry' ].freeze
10
+ ENTRY_SPANS = [ :rack, :'resque-worker', :'rpc-server', :'sidekiq-worker', :'graphql.server', :sqs, :'aws.lambda.entry' ].freeze
11
11
  EXIT_SPANS = [ :activerecord, :excon, :'net-http', :'resque-client',
12
12
  :'rpc-client', :'sidekiq-client', :redis, :dynamodb, :s3, :sns, :sqs ].freeze
13
13
  HTTP_SPANS = [ :rack, :excon, :'net-http' ].freeze
@@ -2,6 +2,6 @@
2
2
  # (c) Copyright Instana Inc. 2016
3
3
 
4
4
  module Instana
5
- VERSION = "1.197.0"
5
+ VERSION = "1.199.2"
6
6
  VERSION_FULL = "instana-#{VERSION}"
7
7
  end
@@ -43,6 +43,32 @@ class AgentTest < Minitest::Test
43
43
  ENV['ECS_CONTAINER_METADATA_URI'] = nil
44
44
  end
45
45
 
46
+ def test_lambda
47
+ ENV['_HANDLER'] = 'TEST_FUNCTION'
48
+ ENV['INSTANA_ENDPOINT_URL'] = 'http://example.com'
49
+
50
+ subject = Instana::Backend::Agent.new
51
+ assert_nil subject.delegate
52
+ subject.setup
53
+ assert subject.delegate.is_a?(Instana::Backend::ServerlessAgent)
54
+ ensure
55
+ ENV['_HANDLER'] = nil
56
+ ENV['INSTANA_ENDPOINT_URL'] = nil
57
+ end
58
+
59
+ def test_google_cloud
60
+ ENV['K_REVISION'] = 'TEST'
61
+ ENV['INSTANA_ENDPOINT_URL'] = 'http://example.com'
62
+
63
+ subject = Instana::Backend::Agent.new
64
+ assert_nil subject.delegate
65
+ subject.setup
66
+ assert subject.delegate.is_a?(Instana::Backend::ServerlessAgent)
67
+ ensure
68
+ ENV['K_REVISION'] = nil
69
+ ENV['INSTANA_ENDPOINT_URL'] = nil
70
+ end
71
+
46
72
  def test_delegate_super
47
73
  subject = Instana::Backend::Agent.new
48
74
  assert_raises NoMethodError do
data/test/secrets_test.rb CHANGED
@@ -78,6 +78,16 @@ class SecretsTest < Minitest::Test
78
78
  assert_redacted @subject.remove_from_query(url, sample_config), %w(instantiate)
79
79
  end
80
80
 
81
+ def test_without_url
82
+ sample_config = {
83
+ "matcher"=>"contains",
84
+ "list"=>["stan"]
85
+ }
86
+
87
+ url = 'filter[instantiate]=true'
88
+ assert_redacted @subject.remove_from_query(url, sample_config), %w(filter[instantiate]), raw_str: true
89
+ end
90
+
81
91
  private
82
92
 
83
93
  def url_for(keys)
@@ -86,9 +96,9 @@ class SecretsTest < Minitest::Test
86
96
  url.to_s
87
97
  end
88
98
 
89
- def assert_redacted(str, keys)
90
- url = URI(str)
91
- params = CGI.parse(url.query)
99
+ def assert_redacted(str, keys, raw_str: false)
100
+ params = raw_str ? CGI.parse(str) : CGI.parse(URI(str).query)
101
+ pp params
92
102
 
93
103
  assert_equal keys, params.select { |_, v| v == %w(<redacted>) }.keys, 'to be redacted'
94
104
  end
@@ -0,0 +1,323 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ require 'test_helper'
5
+
6
+ class ServerlessTest < Minitest::Test
7
+ def setup
8
+ @mock_agent = Minitest::Mock.new
9
+ @mock_agent.expect(:send_bundle, true, [])
10
+ @subject = Instana::Serverless.new(agent: @mock_agent)
11
+ end
12
+
13
+ def teardown
14
+ @mock_agent.verify
15
+ end
16
+
17
+ def test_lambda_send_error
18
+ mock_logger = Minitest::Mock.new
19
+ mock_logger.expect(:error, true, [String])
20
+
21
+ @mock_agent.expect(:send_bundle, true) { |_args| raise StandardError, 'error' }
22
+
23
+ mock_context = OpenStruct.new(
24
+ invoked_function_arn: 'test_arn',
25
+ function_name: 'test_function',
26
+ function_version: '$TEST'
27
+ )
28
+
29
+ subject = Instana::Serverless.new(agent: @mock_agent, logger: mock_logger)
30
+ subject.wrap_aws(nil, mock_context) { 0 }
31
+ subject.wrap_aws(nil, mock_context) { 0 }
32
+
33
+ mock_logger.verify
34
+ end
35
+
36
+ def test_lambda_data
37
+ clear_all!
38
+
39
+ mock_context = OpenStruct.new(
40
+ invoked_function_arn: 'test_arn',
41
+ function_name: 'test_function',
42
+ function_version: '$TEST'
43
+ )
44
+
45
+ @subject.wrap_aws(nil, mock_context) { 0 }
46
+
47
+ lambda_span, *rest = Instana.processor.queued_spans
48
+ assert rest.empty?
49
+
50
+ data = lambda_span[:data][:lambda]
51
+
52
+ assert_equal 'aws:api.gateway.noproxy', lambda_span[:data][:lambda][:trigger]
53
+
54
+ assert_equal mock_context.invoked_function_arn, data[:arn]
55
+ assert_equal mock_context.function_name, data[:functionName]
56
+ assert_equal mock_context.function_version, data[:functionVersion]
57
+ assert_equal 'ruby', data[:runtime]
58
+ end
59
+
60
+ def test_lambda_http
61
+ clear_all!
62
+
63
+ mock_id = Instana::Util.generate_id
64
+ mock_context = OpenStruct.new(
65
+ invoked_function_arn: 'test_arn',
66
+ function_name: 'test_function',
67
+ function_version: '$TEST'
68
+ )
69
+ mock_http = {
70
+ "headers" => {
71
+ "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
72
+ "Accept-Encoding" => "gzip, deflate",
73
+ "Accept-Language" => "en-US,en;q=0.5",
74
+ "Connection" => "keep-alive",
75
+ "Host" => "127.0.0.1:3000",
76
+ "Upgrade-Insecure-Requests" => "1",
77
+ "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0",
78
+ "X-Forwarded-Port" => "3000",
79
+ "X-Forwarded-Proto" => "http",
80
+ 'X-Instana-S' => mock_id,
81
+ 'X-Instana-T' => mock_id,
82
+ 'X-Instana-L' => '1'
83
+ },
84
+ "httpMethod" => "GET",
85
+ "path" => "/hello",
86
+ "queryStringParameters" => {"test" => "abcde"}
87
+ }
88
+
89
+ @subject.wrap_aws(mock_http, mock_context) { 0 }
90
+
91
+ lambda_span, *rest = Instana.processor.queued_spans
92
+ assert rest.empty?
93
+
94
+ data = lambda_span[:data][:http]
95
+
96
+ assert_equal 'aws:api.gateway', lambda_span[:data][:lambda][:trigger]
97
+ assert_equal mock_id, lambda_span[:t]
98
+ assert_equal mock_id, lambda_span[:p]
99
+
100
+ assert_equal 'GET', data[:method]
101
+ assert_equal '/hello', data[:url]
102
+ assert_equal '127.0.0.1:3000', data[:host]
103
+ assert_equal 'test=abcde', data[:params]
104
+ end
105
+
106
+ def test_lambda_alb
107
+ clear_all!
108
+
109
+ mock_id = Instana::Util.generate_id
110
+ mock_context = OpenStruct.new(
111
+ invoked_function_arn: 'test_arn',
112
+ function_name: 'test_function',
113
+ function_version: '$TEST'
114
+ )
115
+ mock_http = {
116
+ "headers" => {
117
+ "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
118
+ "Accept-Encoding" => "gzip, deflate",
119
+ "Accept-Language" => "en-US,en;q=0.5",
120
+ "Connection" => "keep-alive",
121
+ "Host" => "127.0.0.1:3000",
122
+ "Upgrade-Insecure-Requests" => "1",
123
+ "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0",
124
+ "X-Forwarded-Port" => "3000",
125
+ "X-Forwarded-Proto" => "http",
126
+ 'X-Instana-S' => mock_id,
127
+ 'X-Instana-T' => mock_id,
128
+ 'X-Instana-L' => '1'
129
+ },
130
+ "httpMethod" => "GET",
131
+ "path" => "/hello",
132
+ "requestContext" => { "elb" => {} }
133
+ }
134
+
135
+ @subject.wrap_aws(mock_http, mock_context) { 0 }
136
+
137
+ lambda_span, *rest = Instana.processor.queued_spans
138
+ assert rest.empty?
139
+
140
+ data = lambda_span[:data][:http]
141
+
142
+ assert_equal 'aws:application.load.balancer', lambda_span[:data][:lambda][:trigger]
143
+ assert_equal mock_id, lambda_span[:t]
144
+ assert_equal mock_id, lambda_span[:p]
145
+
146
+ assert_equal 'GET', data[:method]
147
+ assert_equal '/hello', data[:url]
148
+ assert_equal '127.0.0.1:3000', data[:host]
149
+ assert_equal '', data[:params]
150
+ end
151
+
152
+ def test_lambda_cw_event
153
+ clear_all!
154
+
155
+ mock_context = OpenStruct.new(
156
+ invoked_function_arn: 'test_arn',
157
+ function_name: 'test_function',
158
+ function_version: '$TEST'
159
+ )
160
+ mock_event = {
161
+ "detail-type" => "Scheduled Event",
162
+ "source" => "aws.events",
163
+ "id" => "test",
164
+ "resources" => ["test"]
165
+ }
166
+
167
+ @subject.wrap_aws(mock_event, mock_context) { 0 }
168
+
169
+ lambda_span, *rest = Instana.processor.queued_spans
170
+ assert rest.empty?
171
+
172
+ data = lambda_span[:data][:lambda][:cw][:events]
173
+
174
+ assert_equal 'aws:cloudwatch.events', lambda_span[:data][:lambda][:trigger]
175
+ assert_equal 'test', data[:id]
176
+ assert_equal ["test"], data[:resources]
177
+ end
178
+
179
+ def test_lambda_cw_logs
180
+ clear_all!
181
+
182
+ mock_context = OpenStruct.new(
183
+ invoked_function_arn: 'test_arn',
184
+ function_name: 'test_function',
185
+ function_version: '$TEST'
186
+ )
187
+ mock_event = {
188
+ "awslogs" => {"data" => File.read('test/support/serverless/cloudwatch_log.bin')}
189
+ }
190
+
191
+ @subject.wrap_aws(mock_event, mock_context) { 0 }
192
+
193
+ lambda_span, *rest = Instana.processor.queued_spans
194
+ assert rest.empty?
195
+
196
+ data = lambda_span[:data][:lambda][:cw][:logs]
197
+
198
+ assert_equal 'aws:cloudwatch.logs', lambda_span[:data][:lambda][:trigger]
199
+ assert_equal '/aws/lambda/echo-nodejs', data[:group]
200
+ assert_equal '2019/03/13/[$LATEST]94fa867e5374431291a7fc14e2f56ae7', data[:stream]
201
+ end
202
+
203
+ def test_lambda_cw_error
204
+ clear_all!
205
+
206
+ mock_context = OpenStruct.new(
207
+ invoked_function_arn: 'test_arn',
208
+ function_name: 'test_function',
209
+ function_version: '$TEST'
210
+ )
211
+ mock_event = {
212
+ "awslogs" => {"data" => "error"}
213
+ }
214
+
215
+ @subject.wrap_aws(mock_event, mock_context) { 0 }
216
+
217
+ lambda_span, *rest = Instana.processor.queued_spans
218
+ assert rest.empty?
219
+
220
+ data = lambda_span[:data][:lambda][:cw][:logs]
221
+
222
+ assert_equal 'aws:cloudwatch.logs', lambda_span[:data][:lambda][:trigger]
223
+ assert_equal 'incorrect header check', data[:decodingError]
224
+ end
225
+
226
+ def test_lambda_s3
227
+ clear_all!
228
+
229
+ mock_context = OpenStruct.new(
230
+ invoked_function_arn: 'test_arn',
231
+ function_name: 'test_function',
232
+ function_version: '$TEST'
233
+ )
234
+ mock_event = {
235
+ "Records" => [
236
+ {
237
+ "source" => "aws:s3",
238
+ "eventName" => "test",
239
+ "s3" => {
240
+ "bucket" => {"name" => "test_bucket"},
241
+ "object" => {"key" => "test_key"}
242
+ }
243
+ }
244
+ ]
245
+ }
246
+
247
+ @subject.wrap_aws(mock_event, mock_context) { 0 }
248
+
249
+ lambda_span, *rest = Instana.processor.queued_spans
250
+ assert rest.empty?
251
+
252
+ data = lambda_span[:data][:lambda][:s3]
253
+
254
+ assert_equal 'aws:s3', lambda_span[:data][:lambda][:trigger]
255
+ assert_equal 1, data[:events].length
256
+
257
+ assert_equal 'test', data[:events].first[:name]
258
+ assert_equal 'test_bucket', data[:events].first[:bucket]
259
+ assert_equal 'test_key', data[:events].first[:object]
260
+ end
261
+
262
+ def test_lambda_s3_no_object
263
+ clear_all!
264
+
265
+ mock_context = OpenStruct.new(
266
+ invoked_function_arn: 'test_arn',
267
+ function_name: 'test_function',
268
+ function_version: '$TEST'
269
+ )
270
+ mock_event = {
271
+ "Records" => [
272
+ {
273
+ "source" => "aws:s3",
274
+ "eventName" => "test"
275
+ }
276
+ ]
277
+ }
278
+
279
+ @subject.wrap_aws(mock_event, mock_context) { 0 }
280
+
281
+ lambda_span, *rest = Instana.processor.queued_spans
282
+ assert rest.empty?
283
+
284
+ data = lambda_span[:data][:lambda][:s3]
285
+
286
+ assert_equal 'aws:s3', lambda_span[:data][:lambda][:trigger]
287
+ assert_equal 1, data[:events].length
288
+
289
+ assert_equal 'test', data[:events].first[:name]
290
+ assert_nil data[:events].first[:bucket]
291
+ assert_nil data[:events].first[:object]
292
+ end
293
+
294
+ def test_lambda_sqs
295
+ clear_all!
296
+
297
+ mock_context = OpenStruct.new(
298
+ invoked_function_arn: 'test_arn',
299
+ function_name: 'test_function',
300
+ function_version: '$TEST'
301
+ )
302
+ mock_event = {
303
+ "Records" => [
304
+ {
305
+ "source" => "aws:sqs",
306
+ "eventSourceARN" => "test_arn"
307
+ }
308
+ ]
309
+ }
310
+
311
+ @subject.wrap_aws(mock_event, mock_context) { 0 }
312
+
313
+ lambda_span, *rest = Instana.processor.queued_spans
314
+ assert rest.empty?
315
+
316
+ data = lambda_span[:data][:lambda][:sqs]
317
+
318
+ assert_equal 'aws:sqs', lambda_span[:data][:lambda][:trigger]
319
+ assert_equal 1, data[:messages].length
320
+
321
+ assert_equal 'test_arn', data[:messages].first[:queue]
322
+ end
323
+ end
@@ -0,0 +1,74 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ require 'test_helper'
5
+
6
+ class GoogleCloudRunInstanceTest < Minitest::Test
7
+ def test_snapshot
8
+ ENV['K_SERVICE'] = 'test_service'
9
+ ENV['K_CONFIGURATION'] = 'test_config'
10
+ ENV['K_REVISION'] = 'test_revision'
11
+ ENV['PORT'] = 'test_port'
12
+
13
+ stub_request(:get, 'http://10.10.10.10//computeMetadata/v1/instance/id')
14
+ .to_return(status: 200, body: 'test_instance_id')
15
+ stub_request(:get, 'http://10.10.10.10//computeMetadata/v1/instance/zone')
16
+ .to_return(status: 200, body: 'region/number/test_region')
17
+ stub_request(:get, 'http://10.10.10.10//computeMetadata/v1/project/numeric-project-id')
18
+ .to_return(status: 200, body: 'numericProjectId')
19
+ stub_request(:get, 'http://10.10.10.10//computeMetadata/v1/project/project-id')
20
+ .to_return(status: 200, body: 'projectId')
21
+
22
+ subject = Instana::Snapshot::GoogleCloudRunInstance.new(metadata_uri: 'http://10.10.10.10/')
23
+ snapshot = subject.snapshot
24
+
25
+ assert_equal Instana::Snapshot::GoogleCloudRunInstance::ID, snapshot[:name]
26
+ assert_equal 'test_instance_id', snapshot[:entityId]
27
+
28
+ assert_equal "ruby", snapshot[:data][:runtime]
29
+ assert_equal "test_region", snapshot[:data][:region]
30
+ assert_equal "test_service", snapshot[:data][:service]
31
+ assert_equal "test_config", snapshot[:data][:configuration]
32
+ assert_equal "test_revision", snapshot[:data][:revision]
33
+ assert_equal "test_instance_id", snapshot[:data][:instanceId]
34
+ assert_equal "test_port", snapshot[:data][:port]
35
+ assert_equal "numericProjectId", snapshot[:data][:numericProjectId]
36
+ assert_equal "projectId", snapshot[:data][:projectId]
37
+ ensure
38
+ ENV['K_SERVICE'] = nil
39
+ ENV['K_CONFIGURATION'] = nil
40
+ ENV['K_REVISION'] = nil
41
+ ENV['PORT'] = nil
42
+ end
43
+
44
+ def test_snapshot_error
45
+ stub_request(:get, 'http://10.10.10.10//computeMetadata/v1/instance/id')
46
+ .to_return(status: 500)
47
+
48
+ subject = Instana::Snapshot::GoogleCloudRunInstance.new(metadata_uri: 'http://10.10.10.10/')
49
+
50
+ assert_raises do
51
+ subject.snapshot
52
+ end
53
+ end
54
+
55
+ def test_source
56
+ stub_request(:get, 'http://10.10.10.10//computeMetadata/v1/instance/id')
57
+ .to_return(status: 200, body: 'test_instance_id')
58
+ subject = Instana::Snapshot::GoogleCloudRunInstance.new(metadata_uri: 'http://10.10.10.10/')
59
+ source = subject.source
60
+
61
+ assert source[:hl]
62
+ assert_equal 'gcp', source[:cp]
63
+ assert_equal 'test_instance_id', source[:e]
64
+ end
65
+
66
+ def test_host_name
67
+ ENV['K_REVISION'] = 'test_revision'
68
+ subject = Instana::Snapshot::GoogleCloudRunInstance.new(metadata_uri: 'http://10.10.10.10/')
69
+
70
+ assert_equal 'gcp:cloud-run:revision:test_revision', subject.host_name
71
+ ensure
72
+ ENV['K_REVISION'] = nil
73
+ end
74
+ end
@@ -0,0 +1,33 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ require 'test_helper'
5
+
6
+ class GoogleCloudRunProcessTest < Minitest::Test
7
+ def test_snapshot
8
+ ENV['K_REVISION'] = 'test'
9
+ stub_request(:get, 'http://10.10.10.10//computeMetadata/v1/instance/id')
10
+ .to_return(status: 200, body: 'test_instance_id')
11
+
12
+ subject = Instana::Snapshot::GoogleCloudRunProcess.new(metadata_uri: 'http://10.10.10.10/')
13
+ snapshot = subject.snapshot
14
+
15
+ assert_equal Instana::Snapshot::GoogleCloudRunProcess::ID, snapshot[:name]
16
+ assert_equal 'test_instance_id', snapshot[:data][:container]
17
+ assert_equal 'gcpCloudRunInstance', snapshot[:data][:containerType]
18
+ assert_equal 'gcp:cloud-run:revision:test', snapshot[:data][:'com.instana.plugin.host.name']
19
+ ensure
20
+ ENV['K_REVISION'] = nil
21
+ end
22
+
23
+ def test_snapshot_error
24
+ stub_request(:get, 'http://10.10.10.10//computeMetadata/v1/instance/id')
25
+ .to_return(status: 500)
26
+
27
+ subject = Instana::Snapshot::GoogleCloudRunProcess.new(metadata_uri: 'http://10.10.10.10/')
28
+
29
+ assert_raises do
30
+ subject.snapshot
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ require 'test_helper'
5
+
6
+ class LambdaFunctionTest < Minitest::Test
7
+ def setup
8
+ @subject = Instana::Snapshot::LambdaFunction.new
9
+ end
10
+
11
+ def test_snapshot
12
+ Thread.current[:instana_function_arn] = 'test'
13
+
14
+ assert_equal Instana::Snapshot::LambdaFunction::ID, @subject.snapshot[:name]
15
+ assert_equal Thread.current[:instana_function_arn], @subject.snapshot[:entityId]
16
+ ensure
17
+ Thread.current[:instana_function_arn] = nil
18
+ end
19
+
20
+ def test_source
21
+ Thread.current[:instana_function_arn] = 'test'
22
+
23
+ assert @subject.source[:hl]
24
+ assert_equal 'aws', @subject.source[:cp]
25
+ assert_equal Thread.current[:instana_function_arn], @subject.source[:e]
26
+ ensure
27
+ Thread.current[:instana_function_arn] = nil
28
+ end
29
+
30
+ def test_host_name
31
+ Thread.current[:instana_function_arn] = 'test'
32
+
33
+ assert_equal Thread.current[:instana_function_arn], @subject.host_name
34
+ ensure
35
+ Thread.current[:instana_function_arn] = nil
36
+ end
37
+ end
@@ -68,6 +68,10 @@ class TracerIDMgmtTest < Minitest::Test
68
68
  # Bogus Array arg
69
69
  bogus_result = Instana::Util.header_to_id([1234])
70
70
  assert_equal '', bogus_result
71
+
72
+ # Invalid characters/length
73
+ bogus_result = Instana::Util.header_to_id('qwerty')
74
+ assert_equal '', bogus_result
71
75
  end
72
76
 
73
77
  def test_long_id_trim
@@ -15,7 +15,8 @@ class SpanContextTest < Minitest::Test
15
15
  end
16
16
 
17
17
  def test_flags_level_zero
18
- subject = Instana::SpanContext.new('trace', 'span', 0)
18
+ subject = Instana::SpanContext.new('trace', 'span', 0, {external_state: 'cn=test'})
19
19
  assert_equal '00-000000000000000000000000000trace-000000000000span-00', subject.trace_parent_header
20
+ assert_equal 'cn=test', subject.trace_state_header
20
21
  end
21
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: instana
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.197.0
4
+ version: 1.199.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Giacomo Lombardo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-06 00:00:00.000000000 Z
11
+ date: 2021-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -255,12 +255,15 @@ files:
255
255
  - lib/instana/open_tracing/instana_tracer.rb
256
256
  - lib/instana/rack.rb
257
257
  - lib/instana/secrets.rb
258
+ - lib/instana/serverless.rb
258
259
  - lib/instana/setup.rb
259
260
  - lib/instana/snapshot/deltable.rb
260
261
  - lib/instana/snapshot/docker_container.rb
261
262
  - lib/instana/snapshot/fargate_container.rb
262
263
  - lib/instana/snapshot/fargate_process.rb
263
264
  - lib/instana/snapshot/fargate_task.rb
265
+ - lib/instana/snapshot/google_cloud_run_instance.rb
266
+ - lib/instana/snapshot/google_cloud_run_process.rb
264
267
  - lib/instana/snapshot/lambda_function.rb
265
268
  - lib/instana/snapshot/ruby_process.rb
266
269
  - lib/instana/tracer.rb
@@ -307,11 +310,15 @@ files:
307
310
  - test/instrumentation/sidekiq-client_test.rb
308
311
  - test/instrumentation/sidekiq-worker_test.rb
309
312
  - test/secrets_test.rb
313
+ - test/serverless_test.rb
310
314
  - test/snapshot/deltable_test.rb
311
315
  - test/snapshot/docker_container_test.rb
312
316
  - test/snapshot/fargate_container_test.rb
313
317
  - test/snapshot/fargate_process_test.rb
314
318
  - test/snapshot/fargate_task_test.rb
319
+ - test/snapshot/google_cloud_run_instance_test.rb
320
+ - test/snapshot/google_cloud_run_process_test.rb
321
+ - test/snapshot/lambda_function_test.rb
315
322
  - test/snapshot/ruby_process_test.rb
316
323
  - test/support/apps/active_record/active_record.rb
317
324
  - test/support/apps/grpc/boot.rb
@@ -368,6 +375,7 @@ specification_version: 4
368
375
  summary: Ruby Distributed Tracing & Metrics Sensor for Instana
369
376
  test_files:
370
377
  - test/config_test.rb
378
+ - test/serverless_test.rb
371
379
  - test/activator_test.rb
372
380
  - test/tracing/span_context_test.rb
373
381
  - test/tracing/span_test.rb
@@ -382,7 +390,10 @@ test_files:
382
390
  - test/snapshot/deltable_test.rb
383
391
  - test/snapshot/fargate_task_test.rb
384
392
  - test/snapshot/ruby_process_test.rb
393
+ - test/snapshot/google_cloud_run_process_test.rb
394
+ - test/snapshot/lambda_function_test.rb
385
395
  - test/snapshot/fargate_container_test.rb
396
+ - test/snapshot/google_cloud_run_instance_test.rb
386
397
  - test/backend/host_agent_activation_observer_test.rb
387
398
  - test/backend/host_agent_reporting_observer_test.rb
388
399
  - test/backend/gc_snapshot_test.rb