elastic-apm 3.9.0 → 3.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.asciidoc +66 -0
  3. data/Gemfile +1 -3
  4. data/docs/api.asciidoc +2 -1
  5. data/docs/configuration.asciidoc +1 -0
  6. data/lib/elastic_apm.rb +14 -2
  7. data/lib/elastic_apm/config.rb +31 -5
  8. data/lib/elastic_apm/config/options.rb +2 -1
  9. data/lib/elastic_apm/config/round_float.rb +31 -0
  10. data/lib/elastic_apm/config/wildcard_pattern_list.rb +13 -1
  11. data/lib/elastic_apm/context_builder.rb +1 -1
  12. data/lib/elastic_apm/instrumenter.rb +10 -3
  13. data/lib/elastic_apm/metadata.rb +3 -1
  14. data/lib/elastic_apm/metadata/cloud_info.rb +128 -0
  15. data/lib/elastic_apm/metadata/system_info.rb +5 -3
  16. data/lib/elastic_apm/metadata/system_info/container_info.rb +28 -4
  17. data/lib/elastic_apm/middleware.rb +16 -5
  18. data/lib/elastic_apm/span.rb +19 -3
  19. data/lib/elastic_apm/spies/delayed_job.rb +6 -2
  20. data/lib/elastic_apm/spies/elasticsearch.rb +7 -1
  21. data/lib/elastic_apm/spies/faraday.rb +1 -0
  22. data/lib/elastic_apm/spies/http.rb +1 -0
  23. data/lib/elastic_apm/spies/mongo.rb +6 -2
  24. data/lib/elastic_apm/spies/net_http.rb +1 -0
  25. data/lib/elastic_apm/spies/rake.rb +4 -2
  26. data/lib/elastic_apm/spies/resque.rb +4 -2
  27. data/lib/elastic_apm/spies/sequel.rb +10 -1
  28. data/lib/elastic_apm/spies/shoryuken.rb +2 -0
  29. data/lib/elastic_apm/spies/sidekiq.rb +2 -0
  30. data/lib/elastic_apm/spies/sneakers.rb +2 -0
  31. data/lib/elastic_apm/spies/sucker_punch.rb +2 -0
  32. data/lib/elastic_apm/trace_context.rb +1 -1
  33. data/lib/elastic_apm/trace_context/traceparent.rb +2 -4
  34. data/lib/elastic_apm/trace_context/tracestate.rb +112 -9
  35. data/lib/elastic_apm/transaction.rb +39 -6
  36. data/lib/elastic_apm/transport/connection.rb +1 -0
  37. data/lib/elastic_apm/transport/filters/hash_sanitizer.rb +9 -16
  38. data/lib/elastic_apm/transport/filters/secrets_filter.rb +8 -6
  39. data/lib/elastic_apm/transport/serializers.rb +8 -6
  40. data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +43 -3
  41. data/lib/elastic_apm/transport/serializers/span_serializer.rb +3 -1
  42. data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +2 -0
  43. data/lib/elastic_apm/transport/user_agent.rb +3 -3
  44. data/lib/elastic_apm/transport/worker.rb +5 -0
  45. data/lib/elastic_apm/util.rb +2 -0
  46. data/lib/elastic_apm/util/precision_validator.rb +46 -0
  47. data/lib/elastic_apm/version.rb +1 -1
  48. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb0d3e0244586e4fe0b9c89beacd267272c0d6b8467457fcce9fde228b22742e
4
- data.tar.gz: e3279992b31d2826b6e53c8c66802c1641d30a8b8134671298280b370fa2f8e1
3
+ metadata.gz: 0c810b4e9b4f1149873235af4d1ddd6b4a85acf7734c491a42d21777443d57a0
4
+ data.tar.gz: f63af16c444a3a5499cd9246b1d11bba2cd9a7a31efed58bfd23d5b1c8b3e0d3
5
5
  SHA512:
6
- metadata.gz: a81c23c3e427e727484854c89072c17704c11fbccca518169ae75a7aa8a45bc04966226044cebcd9c1d36fe7c986053e74e1c1b0456cc6cb23bcd0207204ef9d
7
- data.tar.gz: de7d4efea50ee03db830ac3570cadc40277fd67ce630c4388f942cfbca005a8b0af72f81b0712eb38972aa7d38c01971fe4697888a75f55ffeba51b98fc51b6b
6
+ metadata.gz: ed48f7f3870b346a17ec5e263c90c67f68b1f355a4900c3a79ae998bf689b1b549e6658d7c4165b934091669734bea561acf9991b82446a6ca4164558a488301
7
+ data.tar.gz: f87f347bf3a72e8d331e95d95ceca640d12754cb049ce4c39097b03b75d022c347f95062d3e311e0fb0cbc90f61050449ed1bd6f39f8682c95e935fccf7509f9
@@ -35,9 +35,75 @@ endif::[]
35
35
  [[release-notes-3.x]]
36
36
  === Ruby Agent version 3.x
37
37
 
38
+ [[release-notes-3.12.0]]
39
+ ==== 3.12.0 (2020-11-10)
40
+
41
+ [float]
42
+ ===== Added
43
+
44
+ - Add outcome to transactions and spans {pull}883[#883]
45
+
46
+ [[release-notes-3.11.1]]
47
+ ==== 3.11.1 (2020-11-05)
48
+
49
+ [float]
50
+ ===== Fixed
51
+
52
+ - Fix reporting from Kubernetes based deploys to APM Server 7.9.x {pull}885[#885]
53
+
54
+ [[release-notes-3.11.0]]
55
+ ==== 3.11.0 (2020-10-27)
56
+
57
+ [float]
58
+ ===== Added
59
+
60
+ - Add and read sampling info from Tracestate headers {pull}858[#858]
61
+ - Add information about cloud hosting environment if available {pull}871[#871]
62
+
63
+ [float]
64
+ ===== Changed
65
+
66
+ - Align the default value of `sanitize_field_names` with other agents {pull}867[#867]
67
+ - Ensure max 4 digits of precision for `sample_rate` as per agent spec {pull}880[#880]
68
+
69
+ [float]
70
+ ===== Fixed
71
+
72
+ - Fix Delayed::Job class names when used through ActiveJob {pull}869[#869]
73
+ - Fix Delayed::Job when run without the agent running {pull}874[#874]
74
+ - Fix Kubernetes related metadata {pull}882[#882]
75
+
76
+ [[release-notes-3.10.1]]
77
+ ==== 3.10.1 (2020-08-26)
78
+
79
+ [float]
80
+ ===== Fixed
81
+
82
+ - Remove secrets from cookies in errors {pull}863[#863]
83
+ - Silence deprecation warning when setting `ignore_url_patterns` to default {pull}865[#865]
84
+
85
+ [[release-notes-3.10.0]]
86
+ ==== 3.10.0 (2020-08-26)
87
+
88
+ [float]
89
+ ===== Added
90
+
91
+ - Config option `transaction_ignore_urls` to replace `ignore_url_patterns` {pull}844[#844]
92
+ - Prepend `(?-i)` to patterns to make them case-sensitive {pull}846[#846]
93
+
94
+ [float]
95
+ ===== Fixed
96
+
97
+ - Reverted {pull}839[#839]
98
+ - Improved Kubernetes pod ID detection {pull}859[#859]
99
+
38
100
  [[release-notes-3.9.0]]
39
101
  ==== 3.9.0 (2020-08-04)
40
102
 
103
+ [float]
104
+ ===== Fixed
105
+ - Scrub request body of illegal UTF-8 characters {pull}832[#832]
106
+
41
107
  [float]
42
108
  ===== Added
43
109
 
data/Gemfile CHANGED
@@ -22,14 +22,11 @@ source 'https://rubygems.org'
22
22
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
23
23
 
24
24
  # Tools
25
- gem 'bootsnap', require: false
26
25
  gem 'cucumber', require: false
27
26
  gem 'pry'
28
27
  gem 'rack-test'
29
28
  gem 'rspec', '~> 3'
30
29
  gem 'rspec-its'
31
- gem 'rubocop', require: nil
32
- gem 'rubocop-performance', require: nil
33
30
  gem 'timecop'
34
31
  gem 'webmock'
35
32
 
@@ -42,6 +39,7 @@ gem 'faraday', require: nil
42
39
  gem 'graphql', require: nil
43
40
  gem 'google-protobuf', '< 3.12' if !defined?(JRUBY_VERSION) && RUBY_VERSION < '2.5'
44
41
  gem 'grpc' if !defined?(JRUBY_VERSION)
42
+ gem 'json'
45
43
  gem 'json-schema', require: nil
46
44
  gem 'mongo', require: nil
47
45
  gem 'opentracing', require: nil
@@ -433,6 +433,7 @@ end
433
433
  - `name`: String
434
434
  - `type`: String
435
435
  - `result`: String
436
+ - `outcome`: String ('unknown', 'success', 'failure', nil)
436
437
  - `trace_id`: String (readonly)
437
438
 
438
439
  [float]
@@ -457,7 +458,7 @@ If your service generates the HTML page dynamically, initializing the
457
458
  JavaScript RUM agent with the value of this method allows analyzing the time
458
459
  spent in the browser vs in the backend services.
459
460
 
460
- To enable the JavaScript RUM agent, initilialize the RUM agent with the Ruby
461
+ To enable the JavaScript RUM agent, initialize the RUM agent with the Ruby
461
462
  agent's current transaction:
462
463
 
463
464
  [source,html]
@@ -853,6 +853,7 @@ To reduce overhead and storage requirements, you can set the sample rate to a va
853
853
  between `0.0` and `1.0`.
854
854
  We still record overall time and the result for unsampled transactions, but no
855
855
  context information, tags, or spans.
856
+ Note that the sample rate will be rounded to 4 digits of precision.
856
857
 
857
858
  [float]
858
859
  [[config-use-experimental-sql-parser]]
@@ -183,7 +183,12 @@ module ElasticAPM
183
183
  context: context,
184
184
  trace_context: trace_context
185
185
  )
186
- yield transaction
186
+ result = yield transaction
187
+ transaction&.outcome ||= Transaction::Outcome::SUCCESS
188
+ result
189
+ rescue
190
+ transaction&.outcome ||= Transaction::Outcome::FAILURE
191
+ raise
187
192
  ensure
188
193
  end_transaction
189
194
  end
@@ -287,7 +292,14 @@ module ElasticAPM
287
292
  parent: parent,
288
293
  sync: sync
289
294
  )
290
- yield span
295
+ result = yield span
296
+ span&.outcome =
297
+ Span::Outcome::SUCCESS unless span&.outcome
298
+ result
299
+ rescue
300
+ span&.outcome =
301
+ Span::Outcome::FAILURE unless span&.outcome
302
+ raise
291
303
  ensure
292
304
  end_span
293
305
  end
@@ -17,9 +17,10 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
- require 'elastic_apm/config/options'
21
- require 'elastic_apm/config/duration'
22
20
  require 'elastic_apm/config/bytes'
21
+ require 'elastic_apm/config/duration'
22
+ require 'elastic_apm/config/options'
23
+ require 'elastic_apm/config/round_float'
23
24
  require 'elastic_apm/config/regexp_list'
24
25
  require 'elastic_apm/config/wildcard_pattern_list'
25
26
 
@@ -30,6 +31,11 @@ module ElasticAPM
30
31
 
31
32
  DEPRECATED_OPTIONS = %i[].freeze
32
33
 
34
+ # DEPRECATED: To align with other agents, change on next major bump to:
35
+ # "password, passwd, pwd, secret, *key, *token*, *session*, *credit*, *card*, authorization, set-cookie"
36
+ SANITIZE_FIELD_NAMES_DEFAULT =
37
+ %w[*password* *passwd* *pwd* *secret* *key* *token* *session* *credit* *card* *authorization* *set-cookie*]
38
+
33
39
  # rubocop:disable Metrics/LineLength, Layout/ExtraSpacing
34
40
  option :config_file, type: :string, default: 'config/elastic_apm.yml'
35
41
  option :server_url, type: :url, default: 'http://localhost:8200'
@@ -45,6 +51,7 @@ module ElasticAPM
45
51
  option :capture_elasticsearch_queries, type: :bool, default: false
46
52
  option :capture_env, type: :bool, default: true
47
53
  option :central_config, type: :bool, default: true
54
+ option :cloud_provider, type: :string, default: 'auto'
48
55
  option :current_user_email_method, type: :string, default: 'email'
49
56
  option :current_user_id_method, type: :string, default: 'id'
50
57
  option :current_user_username_method, type: :string, default: 'username'
@@ -76,7 +83,8 @@ module ElasticAPM
76
83
  option :proxy_port, type: :int
77
84
  option :proxy_username, type: :string
78
85
  option :recording, type: :bool, default: true
79
- option :sanitize_field_names, type: :list, default: [], converter: WildcardPatternList.new
86
+ option :sanitize_field_names, type: :list,
87
+ default: SANITIZE_FIELD_NAMES_DEFAULT, converter: WildcardPatternList.new
80
88
  option :server_ca_cert, type: :string
81
89
  option :service_name, type: :string
82
90
  option :service_node_name, type: :string
@@ -87,8 +95,9 @@ module ElasticAPM
87
95
  option :source_lines_span_library_frames, type: :int, default: 0
88
96
  option :span_frames_min_duration, type: :float, default: '5ms', converter: Duration.new(default_unit: 'ms')
89
97
  option :stack_trace_limit, type: :int, default: 999_999
98
+ option :transaction_ignore_urls, type: :list, default: [], converter: WildcardPatternList.new
90
99
  option :transaction_max_spans, type: :int, default: 500
91
- option :transaction_sample_rate, type: :float, default: 1.0
100
+ option :transaction_sample_rate, type: :float, default: 1.0, converter: RoundFloat.new
92
101
  option :use_elastic_traceparent_header, type: :bool, default: true
93
102
  option :use_legacy_sql_parser, type: :bool, default: false
94
103
  option :verify_server_cert, type: :bool, default: true
@@ -188,6 +197,14 @@ module ElasticAPM
188
197
  metrics_interval > 0
189
198
  end
190
199
 
200
+ # DEPRECATED: Remove this in next major version
201
+ def sanitize_field_names=(value)
202
+ list = WildcardPatternList.new.call(value)
203
+ defaults = WildcardPatternList.new.call(SANITIZE_FIELD_NAMES_DEFAULT)
204
+ get(:sanitize_field_names).value =
205
+ defaults.concat(list).uniq(&:pattern) # use regex pattern for comparisons
206
+ end
207
+
191
208
  def span_frames_min_duration?
192
209
  span_frames_min_duration != 0
193
210
  end
@@ -234,6 +251,15 @@ module ElasticAPM
234
251
  self.default_labels = value
235
252
  end
236
253
 
254
+ def ignore_url_patterns=(value)
255
+ unless value == self.class.schema[:ignore_url_patterns][:default]
256
+ warn '[DEPRECATED] The option ignore_url_patterns is being removed. ' \
257
+ 'Consider using transaction_ignore_urls instead.'
258
+ end
259
+
260
+ set(:ignore_url_patterns, value)
261
+ end
262
+
237
263
  def custom_key_filters=(value)
238
264
  unless value == self.class.schema[:custom_key_filters][:default]
239
265
  warn '[DEPRECATED] The option custom_key_filters is being removed. ' \
@@ -318,7 +344,7 @@ module ElasticAPM
318
344
  self.logger ||= ::Rails.logger
319
345
 
320
346
  self.__root_path = ::Rails.root.to_s
321
- self.__view_paths = ::ActionController::Base.view_paths.map(&:to_s)
347
+ self.__view_paths = app.config.paths['app/views'].existent + [::Rails.root.to_s]
322
348
  end
323
349
 
324
350
  def rails_app_name(app)
@@ -38,7 +38,8 @@ module ElasticAPM
38
38
  set(value || default)
39
39
  end
40
40
 
41
- attr_reader :key, :value, :default, :type
41
+ attr_reader :key, :default, :type, :converter
42
+ attr_accessor :value
42
43
 
43
44
  def set(value)
44
45
  @value = normalize(value)
@@ -0,0 +1,31 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ require 'elastic_apm/util/precision_validator'
21
+
22
+ module ElasticAPM
23
+ class Config
24
+ # @api private
25
+ class RoundFloat
26
+ def call(value)
27
+ Util::PrecisionValidator.validate(value, precision: 4, minimum: 0.0001)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -27,6 +27,8 @@ module ElasticAPM
27
27
  @pattern = convert(str)
28
28
  end
29
29
 
30
+ attr_reader :pattern
31
+
30
32
  def match?(other)
31
33
  !!@pattern.match(other)
32
34
  end
@@ -36,12 +38,22 @@ module ElasticAPM
36
38
  private
37
39
 
38
40
  def convert(str)
41
+ case_sensitive = false
42
+
43
+ if str.start_with?('(?-i)')
44
+ str = str.gsub(/^\(\?-\i\)/, '')
45
+ case_sensitive = true
46
+ end
47
+
39
48
  parts =
40
49
  str.chars.each_with_object([]) do |char, arr|
41
50
  arr << (char == '*' ? '.*' : Regexp.escape(char))
42
51
  end
43
52
 
44
- Regexp.new('\A' + parts.join + '\Z', Regexp::IGNORECASE)
53
+ Regexp.new(
54
+ '\A' + parts.join + '\Z',
55
+ case_sensitive ? nil : Regexp::IGNORECASE
56
+ )
45
57
  end
46
58
  end
47
59
 
@@ -76,7 +76,7 @@ module ElasticAPM
76
76
  else
77
77
  body = req.body.read
78
78
  req.body.rewind
79
- body.byteslice(0, MAX_BODY_LENGTH).force_encoding('utf-8')
79
+ body.byteslice(0, MAX_BODY_LENGTH).force_encoding('utf-8').scrub
80
80
  end
81
81
  end
82
82
 
@@ -119,7 +119,13 @@ module ElasticAPM
119
119
  "Already inside #{transaction.inspect}"
120
120
  end
121
121
 
122
- sampled = trace_context ? trace_context.recorded? : random_sample?(config)
122
+ if trace_context
123
+ sampled = trace_context.recorded?
124
+ sample_rate = trace_context.tracestate.sample_rate
125
+ else
126
+ sampled = random_sample?(config)
127
+ sample_rate = config.transaction_sample_rate
128
+ end
123
129
 
124
130
  transaction =
125
131
  Transaction.new(
@@ -128,6 +134,7 @@ module ElasticAPM
128
134
  context: context,
129
135
  trace_context: trace_context,
130
136
  sampled: sampled,
137
+ sample_rate: sample_rate,
131
138
  config: config
132
139
  )
133
140
 
@@ -259,7 +266,7 @@ module ElasticAPM
259
266
  end
260
267
 
261
268
  def update_transaction_metrics(transaction)
262
- return unless transaction.collect_metrics
269
+ return unless transaction.collect_metrics?
263
270
 
264
271
  tags = {
265
272
  'transaction.name': transaction.name,
@@ -298,7 +305,7 @@ module ElasticAPM
298
305
  end
299
306
 
300
307
  def update_span_metrics(span)
301
- return unless span.transaction.breakdown_metrics
308
+ return unless span.transaction.collect_metrics?
302
309
 
303
310
  tags = {
304
311
  'span.type': span.type,
@@ -25,12 +25,14 @@ module ElasticAPM
25
25
  @process = ProcessInfo.new(config)
26
26
  @system = SystemInfo.new(config)
27
27
  @labels = config.global_labels
28
+ @cloud = CloudInfo.new(config).fetch!
28
29
  end
29
30
 
30
- attr_reader :service, :process, :system, :labels
31
+ attr_reader :service, :process, :system, :cloud, :labels
31
32
  end
32
33
  end
33
34
 
34
35
  require 'elastic_apm/metadata/service_info'
35
36
  require 'elastic_apm/metadata/system_info'
36
37
  require 'elastic_apm/metadata/process_info'
38
+ require 'elastic_apm/metadata/cloud_info'
@@ -0,0 +1,128 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ require "http"
21
+
22
+ module ElasticAPM
23
+ class Metadata
24
+ # @api private
25
+ class CloudInfo
26
+ include Logging
27
+
28
+ AWS_URI = "http://169.254.169.254/latest/dynamic/instance-identity/document"
29
+ GCP_URI = "http://metadata.google.internal/computeMetadata/v1/?recursive=true"
30
+ AZURE_URI = "http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15"
31
+
32
+ def initialize(config)
33
+ @config = config
34
+ @client = HTTP.timeout(0.1)
35
+ end
36
+
37
+ attr_reader :config
38
+
39
+ attr_accessor(
40
+ :account_id,
41
+ :account_name,
42
+ :instance_id,
43
+ :instance_name,
44
+ :machine_type,
45
+ :project_id,
46
+ :project_name,
47
+ :availability_zone,
48
+ :provider,
49
+ :region
50
+ )
51
+
52
+ def fetch!
53
+ case config.cloud_provider
54
+ when "aws"
55
+ fetch_aws
56
+ when "gcp"
57
+ fetch_gcp
58
+ when "azure"
59
+ fetch_azure
60
+ when "auto"
61
+ fetch_aws || fetch_gcp || fetch_azure
62
+ when "none"
63
+ nil
64
+ else
65
+ error("Unknown setting for cloud_provider '#{config.cloud_provider}'")
66
+ end
67
+
68
+ self
69
+ end
70
+
71
+ private
72
+
73
+ def fetch_aws
74
+ resp = @client.get(AWS_URI)
75
+
76
+ return unless resp.status === 200
77
+ return unless (metadata = JSON.parse(resp.body))
78
+
79
+ self.provider = "aws"
80
+ self.account_id = metadata["accountId"]
81
+ self.instance_id = metadata["instanceId"]
82
+ self.availability_zone = metadata["availabilityZone"]
83
+ self.machine_type = metadata["instanceType"]
84
+ self.region = metadata["region"]
85
+ rescue HTTP::TimeoutError, HTTP::ConnectionError
86
+ nil
87
+ end
88
+
89
+ def fetch_gcp
90
+ resp = @client.headers("Metadata-Flavor" => "Google").get(GCP_URI)
91
+
92
+ return unless resp.status === 200
93
+ return unless (metadata = JSON.parse(resp.body))
94
+
95
+ zone = metadata["instance"]["zone"]&.split("/")&.at(-1)
96
+
97
+ self.provider = "gcp"
98
+ self.instance_id = metadata["instance"]["id"]
99
+ self.instance_name = metadata["instance"]["name"]
100
+ self.project_id = metadata["project"]["numericProjectId"]
101
+ self.project_name = metadata["project"]["projectId"]
102
+ self.availability_zone = zone
103
+ self.region = zone.split("-")[0..-2].join("-")
104
+ self.machine_type = metadata["instance"]["machineType"]
105
+ rescue HTTP::TimeoutError, HTTP::ConnectionError
106
+ nil
107
+ end
108
+
109
+ def fetch_azure
110
+ resp = @client.headers("Metadata" => "true").get(AZURE_URI)
111
+
112
+ return unless resp.status === 200
113
+ return unless (metadata = JSON.parse(resp.body))
114
+
115
+ self.provider = 'azure'
116
+ self.account_id = metadata["subscriptionId"]
117
+ self.instance_id = metadata["vmId"]
118
+ self.instance_name = metadata["name"]
119
+ self.project_name = metadata["resourceGroupName"]
120
+ self.availability_zone = metadata["zone"]
121
+ self.machine_type = metadata["vmSize"]
122
+ self.region = metadata["location"]
123
+ rescue HTTP::TimeoutError, HTTP::ConnectionError
124
+ nil
125
+ end
126
+ end
127
+ end
128
+ end