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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -33
- data/ext/extconf.rb +32 -6
- data/ext/libskylight.yml +6 -9
- data/ext/skylight_native.c +49 -18
- data/lib/skylight.rb +35 -1
- data/lib/skylight/api.rb +4 -2
- data/lib/skylight/cli.rb +1 -1
- data/lib/skylight/cli/doctor.rb +6 -4
- data/lib/skylight/config.rb +149 -518
- data/lib/skylight/data/cacert.pem +236 -812
- data/lib/skylight/helpers.rb +5 -1
- data/lib/skylight/instrumenter.rb +10 -241
- data/lib/skylight/middleware.rb +1 -89
- data/lib/skylight/native.rb +8 -6
- data/lib/skylight/native_ext_fetcher.rb +251 -0
- data/lib/skylight/normalizers/active_job/enqueue_at.rb +2 -20
- data/lib/skylight/probes/sinatra_add_middleware.rb +22 -0
- data/lib/skylight/railtie.rb +11 -131
- data/lib/skylight/sinatra.rb +1 -5
- data/lib/skylight/trace.rb +1 -229
- data/lib/skylight/util/http.rb +3 -3
- data/lib/skylight/vendor/cli/thor/actions/directory.rb +5 -15
- data/lib/skylight/version.rb +1 -1
- metadata +114 -91
- data/lib/skylight/compat.rb +0 -76
- data/lib/skylight/core.rb +0 -149
- data/lib/skylight/deprecation.rb +0 -55
- data/lib/skylight/formatters/http.rb +0 -20
- data/lib/skylight/gc.rb +0 -107
- data/lib/skylight/normalizers.rb +0 -192
- data/lib/skylight/normalizers/action_controller/process_action.rb +0 -50
- data/lib/skylight/normalizers/action_controller/send_file.rb +0 -50
- data/lib/skylight/normalizers/action_view/render_collection.rb +0 -22
- data/lib/skylight/normalizers/action_view/render_partial.rb +0 -21
- data/lib/skylight/normalizers/action_view/render_template.rb +0 -21
- data/lib/skylight/normalizers/active_model_serializers/render.rb +0 -26
- data/lib/skylight/normalizers/active_record/instantiation.rb +0 -17
- data/lib/skylight/normalizers/active_record/sql.rb +0 -55
- data/lib/skylight/normalizers/active_support/cache.rb +0 -51
- data/lib/skylight/normalizers/active_support/cache_clear.rb +0 -16
- data/lib/skylight/normalizers/active_support/cache_decrement.rb +0 -16
- data/lib/skylight/normalizers/active_support/cache_delete.rb +0 -16
- data/lib/skylight/normalizers/active_support/cache_exist.rb +0 -16
- data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +0 -16
- data/lib/skylight/normalizers/active_support/cache_generate.rb +0 -16
- data/lib/skylight/normalizers/active_support/cache_increment.rb +0 -16
- data/lib/skylight/normalizers/active_support/cache_read.rb +0 -16
- data/lib/skylight/normalizers/active_support/cache_read_multi.rb +0 -16
- data/lib/skylight/normalizers/active_support/cache_write.rb +0 -16
- data/lib/skylight/normalizers/coach/handler_finish.rb +0 -36
- data/lib/skylight/normalizers/coach/middleware_finish.rb +0 -23
- data/lib/skylight/normalizers/couch_potato/query.rb +0 -20
- data/lib/skylight/normalizers/default.rb +0 -27
- data/lib/skylight/normalizers/elasticsearch/request.rb +0 -20
- data/lib/skylight/normalizers/faraday/request.rb +0 -38
- data/lib/skylight/normalizers/grape/endpoint.rb +0 -30
- data/lib/skylight/normalizers/grape/endpoint_render.rb +0 -26
- data/lib/skylight/normalizers/grape/endpoint_run.rb +0 -33
- data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +0 -23
- data/lib/skylight/normalizers/moped/query.rb +0 -100
- data/lib/skylight/probes.rb +0 -129
- data/lib/skylight/probes/action_controller.rb +0 -64
- data/lib/skylight/probes/action_dispatch.rb +0 -30
- data/lib/skylight/probes/action_view.rb +0 -43
- data/lib/skylight/probes/active_model_serializers.rb +0 -55
- data/lib/skylight/probes/elasticsearch.rb +0 -37
- data/lib/skylight/probes/excon.rb +0 -26
- data/lib/skylight/probes/excon/middleware.rb +0 -68
- data/lib/skylight/probes/faraday.rb +0 -22
- data/lib/skylight/probes/grape.rb +0 -88
- data/lib/skylight/probes/httpclient.rb +0 -46
- data/lib/skylight/probes/middleware.rb +0 -68
- data/lib/skylight/probes/mongo.rb +0 -161
- data/lib/skylight/probes/mongoid.rb +0 -21
- data/lib/skylight/probes/moped.rb +0 -39
- data/lib/skylight/probes/net_http.rb +0 -58
- data/lib/skylight/probes/redis.rb +0 -71
- data/lib/skylight/probes/sequel.rb +0 -37
- data/lib/skylight/probes/sinatra.rb +0 -76
- data/lib/skylight/probes/tilt.rb +0 -31
- data/lib/skylight/subscriber.rb +0 -122
- data/lib/skylight/user_config.rb +0 -60
- data/lib/skylight/util.rb +0 -17
- data/lib/skylight/util/allocation_free.rb +0 -26
- data/lib/skylight/util/clock.rb +0 -54
- data/lib/skylight/util/deploy.rb +0 -132
- data/lib/skylight/util/gzip.rb +0 -21
- data/lib/skylight/util/inflector.rb +0 -112
- data/lib/skylight/util/logging.rb +0 -127
- data/lib/skylight/util/multi_io.rb +0 -21
- data/lib/skylight/util/native_ext_fetcher.rb +0 -253
- data/lib/skylight/util/platform.rb +0 -75
- data/lib/skylight/util/proxy.rb +0 -13
- data/lib/skylight/vendor/active_support/notifications.rb +0 -207
- data/lib/skylight/vendor/active_support/notifications/fanout.rb +0 -159
- data/lib/skylight/vendor/active_support/notifications/instrumenter.rb +0 -72
- data/lib/skylight/vendor/active_support/per_thread_registry.rb +0 -52
- data/lib/skylight/vendor/thread_safe.rb +0 -126
- data/lib/skylight/vendor/thread_safe/non_concurrent_cache_backend.rb +0 -133
- data/lib/skylight/vendor/thread_safe/synchronized_cache_backend.rb +0 -76
- data/lib/skylight/vm/gc.rb +0 -70
data/lib/skylight/helpers.rb
CHANGED
@@ -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
|
-
|
7
|
-
|
8
|
-
|
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
|
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
|
-
|
126
|
-
|
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
|
-
|
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
|
222
|
-
|
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
|
data/lib/skylight/middleware.rb
CHANGED
@@ -1,92 +1,4 @@
|
|
1
1
|
module Skylight
|
2
|
-
|
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
|
data/lib/skylight/native.rb
CHANGED
@@ -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
|
-
|
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
|