skylight 1.7.2 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -33
  3. data/ext/extconf.rb +32 -6
  4. data/ext/libskylight.yml +6 -9
  5. data/ext/skylight_native.c +49 -18
  6. data/lib/skylight.rb +35 -1
  7. data/lib/skylight/api.rb +4 -2
  8. data/lib/skylight/cli.rb +1 -1
  9. data/lib/skylight/cli/doctor.rb +6 -4
  10. data/lib/skylight/config.rb +149 -518
  11. data/lib/skylight/data/cacert.pem +236 -812
  12. data/lib/skylight/helpers.rb +5 -1
  13. data/lib/skylight/instrumenter.rb +10 -241
  14. data/lib/skylight/middleware.rb +1 -89
  15. data/lib/skylight/native.rb +8 -6
  16. data/lib/skylight/native_ext_fetcher.rb +251 -0
  17. data/lib/skylight/normalizers/active_job/enqueue_at.rb +2 -20
  18. data/lib/skylight/probes/sinatra_add_middleware.rb +22 -0
  19. data/lib/skylight/railtie.rb +11 -131
  20. data/lib/skylight/sinatra.rb +1 -5
  21. data/lib/skylight/trace.rb +1 -229
  22. data/lib/skylight/util/http.rb +3 -3
  23. data/lib/skylight/vendor/cli/thor/actions/directory.rb +5 -15
  24. data/lib/skylight/version.rb +1 -1
  25. metadata +114 -91
  26. data/lib/skylight/compat.rb +0 -76
  27. data/lib/skylight/core.rb +0 -149
  28. data/lib/skylight/deprecation.rb +0 -55
  29. data/lib/skylight/formatters/http.rb +0 -20
  30. data/lib/skylight/gc.rb +0 -107
  31. data/lib/skylight/normalizers.rb +0 -192
  32. data/lib/skylight/normalizers/action_controller/process_action.rb +0 -50
  33. data/lib/skylight/normalizers/action_controller/send_file.rb +0 -50
  34. data/lib/skylight/normalizers/action_view/render_collection.rb +0 -22
  35. data/lib/skylight/normalizers/action_view/render_partial.rb +0 -21
  36. data/lib/skylight/normalizers/action_view/render_template.rb +0 -21
  37. data/lib/skylight/normalizers/active_model_serializers/render.rb +0 -26
  38. data/lib/skylight/normalizers/active_record/instantiation.rb +0 -17
  39. data/lib/skylight/normalizers/active_record/sql.rb +0 -55
  40. data/lib/skylight/normalizers/active_support/cache.rb +0 -51
  41. data/lib/skylight/normalizers/active_support/cache_clear.rb +0 -16
  42. data/lib/skylight/normalizers/active_support/cache_decrement.rb +0 -16
  43. data/lib/skylight/normalizers/active_support/cache_delete.rb +0 -16
  44. data/lib/skylight/normalizers/active_support/cache_exist.rb +0 -16
  45. data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +0 -16
  46. data/lib/skylight/normalizers/active_support/cache_generate.rb +0 -16
  47. data/lib/skylight/normalizers/active_support/cache_increment.rb +0 -16
  48. data/lib/skylight/normalizers/active_support/cache_read.rb +0 -16
  49. data/lib/skylight/normalizers/active_support/cache_read_multi.rb +0 -16
  50. data/lib/skylight/normalizers/active_support/cache_write.rb +0 -16
  51. data/lib/skylight/normalizers/coach/handler_finish.rb +0 -36
  52. data/lib/skylight/normalizers/coach/middleware_finish.rb +0 -23
  53. data/lib/skylight/normalizers/couch_potato/query.rb +0 -20
  54. data/lib/skylight/normalizers/default.rb +0 -27
  55. data/lib/skylight/normalizers/elasticsearch/request.rb +0 -20
  56. data/lib/skylight/normalizers/faraday/request.rb +0 -38
  57. data/lib/skylight/normalizers/grape/endpoint.rb +0 -30
  58. data/lib/skylight/normalizers/grape/endpoint_render.rb +0 -26
  59. data/lib/skylight/normalizers/grape/endpoint_run.rb +0 -33
  60. data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +0 -23
  61. data/lib/skylight/normalizers/moped/query.rb +0 -100
  62. data/lib/skylight/probes.rb +0 -129
  63. data/lib/skylight/probes/action_controller.rb +0 -64
  64. data/lib/skylight/probes/action_dispatch.rb +0 -30
  65. data/lib/skylight/probes/action_view.rb +0 -43
  66. data/lib/skylight/probes/active_model_serializers.rb +0 -55
  67. data/lib/skylight/probes/elasticsearch.rb +0 -37
  68. data/lib/skylight/probes/excon.rb +0 -26
  69. data/lib/skylight/probes/excon/middleware.rb +0 -68
  70. data/lib/skylight/probes/faraday.rb +0 -22
  71. data/lib/skylight/probes/grape.rb +0 -88
  72. data/lib/skylight/probes/httpclient.rb +0 -46
  73. data/lib/skylight/probes/middleware.rb +0 -68
  74. data/lib/skylight/probes/mongo.rb +0 -161
  75. data/lib/skylight/probes/mongoid.rb +0 -21
  76. data/lib/skylight/probes/moped.rb +0 -39
  77. data/lib/skylight/probes/net_http.rb +0 -58
  78. data/lib/skylight/probes/redis.rb +0 -71
  79. data/lib/skylight/probes/sequel.rb +0 -37
  80. data/lib/skylight/probes/sinatra.rb +0 -76
  81. data/lib/skylight/probes/tilt.rb +0 -31
  82. data/lib/skylight/subscriber.rb +0 -122
  83. data/lib/skylight/user_config.rb +0 -60
  84. data/lib/skylight/util.rb +0 -17
  85. data/lib/skylight/util/allocation_free.rb +0 -26
  86. data/lib/skylight/util/clock.rb +0 -54
  87. data/lib/skylight/util/deploy.rb +0 -132
  88. data/lib/skylight/util/gzip.rb +0 -21
  89. data/lib/skylight/util/inflector.rb +0 -112
  90. data/lib/skylight/util/logging.rb +0 -127
  91. data/lib/skylight/util/multi_io.rb +0 -21
  92. data/lib/skylight/util/native_ext_fetcher.rb +0 -253
  93. data/lib/skylight/util/platform.rb +0 -75
  94. data/lib/skylight/util/proxy.rb +0 -13
  95. data/lib/skylight/vendor/active_support/notifications.rb +0 -207
  96. data/lib/skylight/vendor/active_support/notifications/fanout.rb +0 -159
  97. data/lib/skylight/vendor/active_support/notifications/instrumenter.rb +0 -72
  98. data/lib/skylight/vendor/active_support/per_thread_registry.rb +0 -52
  99. data/lib/skylight/vendor/thread_safe.rb +0 -126
  100. data/lib/skylight/vendor/thread_safe/non_concurrent_cache_backend.rb +0 -133
  101. data/lib/skylight/vendor/thread_safe/synchronized_cache_backend.rb +0 -76
  102. data/lib/skylight/vm/gc.rb +0 -70
@@ -145,10 +145,14 @@ module Skylight
145
145
  title: #{title.inspect},
146
146
  description: #{desc.inspect})
147
147
 
148
+ meta = {}
148
149
  begin
149
150
  send(:before_instrument_#{name}, *args, &blk)
151
+ rescue Exception => e
152
+ meta[:exception_object] = e
153
+ raise e
150
154
  ensure
151
- Skylight.done(span) if span
155
+ Skylight.done(span, meta) if span
152
156
  end
153
157
  end
154
158
  RUBY
@@ -1,255 +1,24 @@
1
- require 'thread'
2
- require 'strscan'
3
- require 'skylight/api'
4
-
5
1
  module Skylight
6
- # @api private
7
- class Instrumenter
8
- KEY = :__skylight_current_trace
9
- LOCK = Mutex.new
10
-
11
- TOO_MANY_UNIQUES = "<too many unique descriptions>"
12
-
13
- include Util::Logging
14
-
15
- class TraceInfo
16
- def current
17
- Thread.current[KEY]
18
- end
19
-
20
- def current=(trace)
21
- Thread.current[KEY] = trace
22
- end
23
- end
24
-
25
- def self.instance
26
- @instance
27
- end
28
-
29
- # Do start
30
- # @param [Config] config The config
31
- def self.start!(config = nil)
32
- return @instance if @instance
33
-
34
- LOCK.synchronize do
35
- return @instance if @instance
36
- @instance = new(config).start!
37
- end
38
- rescue => e
39
- message = sprintf("[SKYLIGHT] [#{Skylight::VERSION}] Unable to start Instrumenter; msg=%s; class=%s", e.message, e.class)
40
- if config && config.respond_to?(:logger)
41
- config.logger.warn message
42
- else
43
- warn message
44
- end
45
- false
46
- end
47
-
48
- def self.stop!
49
- LOCK.synchronize do
50
- return unless @instance
51
- # This is only really helpful for getting specs to pass.
52
- @instance.current_trace = nil
53
-
54
- @instance.shutdown
55
- @instance = nil
56
- end
57
- end
58
-
59
- at_exit do
60
- stop!
61
- end
62
-
63
- attr_reader :config, :gc, :trace_info
64
-
65
- def self.new(config)
66
- config ||= {}
67
- config = Config.load(config) unless config.is_a?(Config)
68
- config.validate!
69
-
70
- inst = native_new(config.to_native_env)
71
- inst.send(:initialize, config)
72
- inst
73
- end
74
-
75
- def initialize(config)
76
- @gc = config.gc
77
- @config = config
78
- @subscriber = Subscriber.new(config, self)
79
-
80
- @trace_info = @config[:trace_info] || TraceInfo.new
81
- end
82
-
83
- def current_trace
84
- @trace_info.current
2
+ class Instrumenter < Core::Instrumenter
3
+ def self.trace_class
4
+ Trace
85
5
  end
86
6
 
87
- def current_trace=(trace)
88
- @trace_info.current = trace
89
- end
90
-
91
- def start!
7
+ def check_install!
92
8
  # Warn if there was an error installing Skylight.
93
- # We do this here since we can't report these issues via Gem install without stopping install entirely.
94
- Skylight.check_install_errors(config)
95
-
96
- unless Skylight.native?
97
- Skylight.warn_skylight_native_missing(config)
98
- return
99
- end
100
-
101
- t { "starting instrumenter" }
102
-
103
- unless config.validate_with_server
104
- log_error "invalid config"
105
- return
106
- end
107
-
108
- t { "starting native instrumenter" }
109
- unless native_start
110
- warn "failed to start instrumenter"
111
- return
112
- end
113
-
114
- config.gc.enable
115
- @subscriber.register!
116
-
117
- self
118
-
119
- rescue Exception => e
120
- log_error "failed to start instrumenter; msg=%s; config=%s", e.message, config.inspect
121
- t { e.backtrace.join("\n") }
122
- nil
123
- end
124
9
 
125
- def shutdown
126
- @subscriber.unregister!
127
- native_stop
128
- end
129
-
130
- def trace(endpoint, cat, title=nil, desc=nil)
131
- # If a trace is already in progress, continue with that one
132
- if trace = @trace_info.current
133
- return yield(trace) if block_given?
134
- return trace
135
- end
136
-
137
- begin
138
- trace = Trace.new(self, endpoint, Util::Clock.nanos, cat, title, desc)
139
- rescue Exception => e
140
- log_error e.message
141
- t { e.backtrace.join("\n") }
142
- return
143
- end
144
-
145
- @trace_info.current = trace
146
- return trace unless block_given?
147
-
148
- begin
149
- yield trace
150
-
151
- ensure
152
- @trace_info.current = nil
153
- t { "submitting trace" }
154
- trace.submit
155
- end
156
- end
157
-
158
- def disable
159
- @disabled = true
160
- yield
161
- ensure
162
- @disabled = false
163
- end
164
-
165
- def disabled?
166
- @disabled
167
- end
168
-
169
- @scanner = StringScanner.new('')
170
- def self.match?(string, regex)
171
- @scanner.string = string
172
- @scanner.match?(regex)
173
- end
174
-
175
- def match?(string, regex)
176
- self.class.match?(string, regex)
177
- end
178
-
179
- def done(span)
180
- return unless trace = @trace_info.current
181
- trace.done(span)
182
- end
183
-
184
- def broken!
185
- return unless trace = @trace_info.current
186
- trace.broken!
187
- end
188
-
189
- def instrument(cat, title=nil, desc=nil)
190
- raise ArgumentError, 'cat is required' unless cat
191
-
192
- unless trace = @trace_info.current
193
- return yield if block_given?
194
- return
195
- end
196
-
197
- cat = cat.to_s
198
-
199
- unless match?(cat, CATEGORY_REGEX)
200
- warn "invalid skylight instrumentation category; value=%s", cat
201
- return yield if block_given?
202
- return
10
+ if defined?(Skylight.check_install_errors)
11
+ Skylight.check_install_errors(config)
203
12
  end
204
13
 
205
- cat = "other.#{cat}" unless match?(cat, TIER_REGEX)
206
-
207
- unless sp = trace.instrument(cat, title, desc)
208
- return yield if block_given?
14
+ if !Skylight.native? && defined?(Skylight.warn_skylight_native_missing)
15
+ Skylight.warn_skylight_native_missing(config)
209
16
  return
210
17
  end
211
-
212
- return sp unless block_given?
213
-
214
- begin
215
- yield sp
216
- ensure
217
- trace.done(sp)
218
- end
219
18
  end
220
19
 
221
- def limited_description(description)
222
- endpoint = @trace_info.current.endpoint
223
-
224
- if description
225
- if native_track_desc(endpoint, description)
226
- description
227
- else
228
- TOO_MANY_UNIQUES
229
- end
230
- end
20
+ def process_sql(sql)
21
+ Skylight.lex_sql(sql, config[:use_old_sql_lexer])
231
22
  end
232
-
233
- def process(trace)
234
- t { fmt "processing trace" }
235
-
236
- if ignore?(trace)
237
- t { fmt "ignoring trace" }
238
- return false
239
- end
240
-
241
- begin
242
- native_submit_trace(trace)
243
- true
244
- rescue => e
245
- warn "failed to submit trace to worker; err=%s", e
246
- false
247
- end
248
- end
249
-
250
- def ignore?(trace)
251
- config.ignored_endpoints.include?(trace.endpoint.sub(%r{<sk-segment>.+</sk-segment>}, ''))
252
- end
253
-
254
23
  end
255
24
  end
@@ -1,92 +1,4 @@
1
1
  module Skylight
2
- # @api private
3
- class Middleware
4
-
5
- class BodyProxy
6
- def initialize(body, &block)
7
- @body, @block, @closed = body, block, false
8
- end
9
-
10
- def respond_to?(*args)
11
- return false if args.first.to_s =~ /^to_ary$/
12
- super or @body.respond_to?(*args)
13
- end
14
-
15
- def close
16
- return if @closed
17
- @closed = true
18
- begin
19
- @body.close if @body.respond_to? :close
20
- ensure
21
- @block.call
22
- end
23
- end
24
-
25
- def closed?
26
- @closed
27
- end
28
-
29
- # N.B. This method is a special case to address the bug described by
30
- # https://github.com/rack/rack/issues/434.
31
- # We are applying this special case for #each only. Future bugs of this
32
- # class will be handled by requesting users to patch their ruby
33
- # implementation, to save adding too many methods in this class.
34
- def each(*args, &block)
35
- @body.each(*args, &block)
36
- end
37
-
38
- def method_missing(*args, &block)
39
- super if args.first.to_s =~ /^to_ary$/
40
- @body.__send__(*args, &block)
41
- end
42
- end
43
-
44
- def self.with_after_close(resp, &block)
45
- # Responses should be arrays but in some situations they aren't
46
- # e.g. https://github.com/ruby-grape/grape/issues/1041
47
- # The safest approach seems to be to rely on implicit destructuring
48
- # since that is currently what Rack::Lint does.
49
- # See also https://github.com/rack/rack/issues/1239
50
- status, headers, body = resp
51
-
52
- [status, headers, BodyProxy.new(body, &block)]
53
- end
54
-
55
- include Util::Logging
56
-
57
- # For Util::Logging
58
- attr_reader :config
59
-
60
- def initialize(app, opts={})
61
- @app = app
62
- @config = opts[:config]
63
- end
64
-
65
- def call(env)
66
- if Skylight.tracing?
67
- error "Already instrumenting. Make sure the Middleware hasn't been added more than once."
68
- end
69
-
70
- if env["REQUEST_METHOD"] == "HEAD"
71
- t { "middleware skipping HEAD" }
72
- @app.call(env)
73
- else
74
- begin
75
- t { "middleware beginning trace" }
76
- trace = Skylight.trace "Rack", 'app.rack.request'
77
- resp = @app.call(env)
78
-
79
- if trace
80
- Middleware.with_after_close(resp) { trace.submit }
81
- else
82
- resp
83
- end
84
- rescue Exception
85
- t { "middleware exception: #{trace}"}
86
- trace.submit if trace
87
- raise
88
- end
89
- end
90
- end
2
+ class Middleware < Core::Middleware
91
3
  end
92
4
  end
@@ -1,4 +1,4 @@
1
- require 'skylight/util/platform'
1
+ require 'skylight/core/util/platform'
2
2
 
3
3
  module Skylight
4
4
  # @api private
@@ -10,14 +10,14 @@ module Skylight
10
10
  end
11
11
 
12
12
  def self.libskylight_path
13
- ENV['SKYLIGHT_LIB_PATH'] || File.expand_path("../native/#{Util::Platform.tuple}", __FILE__)
13
+ ENV['SKYLIGHT_LIB_PATH'] || File.expand_path("../native/#{Core::Util::Platform.tuple}", __FILE__)
14
14
  end
15
15
 
16
16
  skylight_required = ENV.key?("SKYLIGHT_REQUIRED") && ENV['SKYLIGHT_REQUIRED'] !~ /^false$/i
17
17
 
18
18
  begin
19
19
  unless ENV.key?("SKYLIGHT_DISABLE_AGENT") && ENV['SKYLIGHT_DISABLE_AGENT'] !~ /^false$/i
20
- lib = "#{libskylight_path}/libskylight.#{Util::Platform.libext}"
20
+ lib = "#{libskylight_path}/libskylight.#{Core::Util::Platform.libext}"
21
21
 
22
22
  if File.exist?(lib)
23
23
  # First attempt to require the native extension
@@ -39,7 +39,9 @@ module Skylight
39
39
  raise if skylight_required
40
40
  end
41
41
 
42
- unless Skylight.native?
42
+ if Skylight.native?
43
+ Skylight::Core::Util::Clock.use_native!
44
+ else
43
45
  class Instrumenter
44
46
  def self.native_new(*args)
45
47
  allocate
@@ -54,7 +56,7 @@ module Skylight
54
56
 
55
57
  if File.exist?(install_log) && File.read(install_log) =~ /ERROR/
56
58
  config.alert_logger.error \
57
- "[SKYLIGHT] [#{Skylight::VERSION}] The Skylight native extension failed to install. " \
59
+ "[SKYLIGHT] [#{Skylight::Core::VERSION}] The Skylight native extension failed to install. " \
58
60
  "Please check #{install_log} and notify support@skylight.io. " \
59
61
  "The missing extension will not affect the functioning of your application."
60
62
  end
@@ -63,7 +65,7 @@ module Skylight
63
65
  # @api private
64
66
  def self.warn_skylight_native_missing(config)
65
67
  config.alert_logger.error \
66
- "[SKYLIGHT] [#{Skylight::VERSION}] The Skylight native extension for " \
68
+ "[SKYLIGHT] [#{Skylight::Core::VERSION}] The Skylight native extension for " \
67
69
  "your platform wasn't found. Supported operating systems are " \
68
70
  "Linux 2.6.18+ and Mac OS X 10.8+. The missing extension will not " \
69
71
  "affect the functioning of your application. If you are on a " \
@@ -0,0 +1,251 @@
1
+ require 'uri'
2
+ require 'logger'
3
+ require 'net/http'
4
+ require 'fileutils'
5
+ require 'digest/sha2'
6
+ require 'skylight/util/ssl'
7
+ require 'skylight/core/util/proxy'
8
+
9
+ # Used from extconf.rb
10
+ module Skylight
11
+ # Utility class for fetching the native extension from a URL
12
+ class NativeExtFetcher
13
+ BASE_URL = "https://s3.amazonaws.com/skylight-agent-packages/skylight-native"
14
+ MAX_REDIRECTS = 5
15
+ MAX_RETRIES = 3
16
+
17
+ include FileUtils
18
+
19
+ class FetchError < StandardError; end
20
+
21
+ # Creates a new fetcher and fetches
22
+ # @param opts [Hash]
23
+ def self.fetch(opts = {})
24
+ fetcher = new(
25
+ opts[:source] || BASE_URL,
26
+ opts[:target],
27
+ opts[:version],
28
+ opts[:checksum],
29
+ opts[:arch],
30
+ opts[:required],
31
+ opts[:platform],
32
+ opts[:logger] || Logger.new(STDOUT))
33
+
34
+ fetcher.fetch
35
+ end
36
+
37
+ # @param source [String] the base url to download from
38
+ # @param target [String] file to download as
39
+ # @param version [String] version to download
40
+ # @param checksum [String] checksum of the archive
41
+ # @param arch [String] platform architecture, e.g. `linux-x86_64`
42
+ # @param required [Boolean] whether the download is required to be successful
43
+ # @param platform
44
+ # @param log [Logger]
45
+ def initialize(source, target, version, checksum, arch, required, platform, log)
46
+ raise "source required" unless source
47
+ raise "target required" unless target
48
+ raise "checksum required" unless checksum
49
+ raise "arch required" unless arch
50
+
51
+ @source = source
52
+ @target = target
53
+ @version = version
54
+ @checksum = checksum
55
+ @required = required
56
+ @platform = platform
57
+ @arch = arch
58
+ @log = log
59
+ end
60
+
61
+ # Fetch the native extension, verify, inflate, and save (if applicable)
62
+ #
63
+ # @return [String] the inflated archive
64
+ def fetch
65
+ log "fetching native ext; curr-platform=#{@platform}; " \
66
+ "requested-arch=#{@arch}; version=#{@version}"
67
+
68
+ tar_gz = "#{@target}/#{basename}"
69
+
70
+ unless sha2 = fetch_native_ext(source_uri, tar_gz, MAX_RETRIES, MAX_REDIRECTS)
71
+ maybe_raise "could not fetch native extension"
72
+ return
73
+ end
74
+
75
+ unless verify_checksum(sha2)
76
+ maybe_raise "could not verify checksum"
77
+ return
78
+ end
79
+
80
+ Dir.chdir File.dirname(tar_gz) do
81
+ system "tar xzvf #{tar_gz}"
82
+ end
83
+
84
+ true
85
+ ensure
86
+ rm_f tar_gz if tar_gz
87
+ end
88
+
89
+ def fetch_native_ext(uri, out, attempts, redirects)
90
+ redirects.times do |i|
91
+ # Ensure the location is available
92
+ mkdir_p File.dirname(out)
93
+ rm_f out
94
+
95
+ remaining_attempts = attempts
96
+
97
+ log "attempting to fetch from remote; uri=#{uri}"
98
+
99
+ begin
100
+ host, port, use_ssl, path = deconstruct_uri(uri)
101
+
102
+ File.open out, 'w' do |f|
103
+ res, extra = http_get(host, port, use_ssl, path, f)
104
+
105
+ case res
106
+ when :success
107
+ log "successfully downloaded native ext; out=#{out}"
108
+ return extra
109
+ when :redirect
110
+ log "fetching native ext; uri=#{uri}; redirected=#{res}"
111
+ uri = extra
112
+
113
+ next
114
+ end
115
+ end
116
+ rescue => e
117
+ remaining_attempts -= 1
118
+
119
+ error "failed to fetch native extension; uri=#{uri}; msg=#{e.message}; remaining-attempts=#{remaining_attempts}", e
120
+
121
+ if remaining_attempts > 0
122
+ sleep 2
123
+ retry
124
+ end
125
+
126
+ return
127
+ end
128
+ end
129
+
130
+ log "exceeded max redirects"
131
+ return
132
+ end
133
+
134
+ # Get with `Net::HTTP`
135
+ #
136
+ # @param host [String] host for `Net::HTTP` request
137
+ # @param port [String,Integer] port for `Net::HTTP` request
138
+ # @param use_ssl [Boolean] whether SSL should be used for this request
139
+ # @param path [String] the path to request
140
+ # @param out [IO]
141
+ #
142
+ # If `ENV['HTTP_PROXY']` is set, it will be used as a proxy for this request.
143
+ def http_get(host, port, use_ssl, path, out)
144
+ if http_proxy = Core::Util::Proxy.detect_url(ENV)
145
+ log "connecting with proxy: #{http_proxy}"
146
+ uri = URI.parse(http_proxy)
147
+ p_host, p_port = uri.host, uri.port
148
+ p_user, p_pass = uri.userinfo.split(/:/) if uri.userinfo
149
+ end
150
+
151
+ opts = {}
152
+ opts[:use_ssl] = use_ssl
153
+
154
+ if use_ssl
155
+ opts[:ca_file] = Util::SSL.ca_cert_file_or_default
156
+ end
157
+
158
+ Net::HTTP.start(host, port, p_host, p_port, p_user, p_pass, use_ssl: use_ssl) do |http|
159
+ http.request_get path do |resp|
160
+ case resp
161
+ when Net::HTTPSuccess
162
+ digest = Digest::SHA2.new
163
+
164
+ resp.read_body do |chunk|
165
+ digest << chunk
166
+ out.write chunk
167
+ end
168
+
169
+ return [ :success, digest.hexdigest ]
170
+ when Net::HTTPRedirection
171
+ unless location = resp['location']
172
+ raise "received redirect but no location"
173
+ end
174
+
175
+ return [ :redirect, location ]
176
+ else
177
+ raise "received HTTP status code #{resp.code}"
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ # Verify the checksum of the archive
184
+ #
185
+ # @param actual [String]
186
+ # @return [Boolean] whether the checksum matches
187
+ def verify_checksum(actual)
188
+ unless @checksum == actual
189
+ log "checksum mismatch; expected=#{@checksum}; actual=#{actual}"
190
+ return false
191
+ end
192
+
193
+ true
194
+ rescue Exception => e
195
+ error "failed to read skylight agent archive; e=#{e.message}"
196
+ false
197
+ end
198
+
199
+ def basename
200
+ "skylight_#{@arch}.tar.gz"
201
+ end
202
+
203
+ # The url that will be fetched
204
+ #
205
+ # @return String
206
+ def source_uri
207
+ "#{@source}/#{@version}/#{basename}"
208
+ end
209
+
210
+ # Split the uri string into its component parts
211
+ #
212
+ # @param uri [String] the uri
213
+ # @return [Array<String>] the host, port, scheme, and request_uri
214
+ def deconstruct_uri(uri)
215
+ uri = URI(uri)
216
+ [ uri.host, uri.port, uri.scheme == 'https', uri.request_uri ]
217
+ end
218
+
219
+ # Log an error and raise if `required` is `true`
220
+ #
221
+ # @param err [String]
222
+ # @return [void]
223
+ def maybe_raise(err)
224
+ error err
225
+
226
+ if @required
227
+ raise err
228
+ end
229
+ end
230
+
231
+ # Log an `info` to the `logger`
232
+ #
233
+ # @param msg [String]
234
+ # @return [void]
235
+ def log(msg)
236
+ msg = "[SKYLIGHT] #{msg}"
237
+ @log.info msg
238
+ end
239
+
240
+ # Log an `error` to the `logger`
241
+ #
242
+ # @param msg [String]
243
+ # @param e [Exception] the exception associated with the error
244
+ # @return [void]
245
+ def error(msg, e=nil)
246
+ msg = "[SKYLIGHT] #{msg}"
247
+ msg << "\n#{e.backtrace.join("\n")}" if e
248
+ @log.error msg
249
+ end
250
+ end
251
+ end