skylight 3.1.5 → 4.0.0.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -9
- data/bin/skylight +1 -1
- data/ext/extconf.rb +74 -76
- data/ext/libskylight.yml +7 -6
- data/lib/skylight.rb +15 -13
- data/lib/skylight/api.rb +40 -37
- data/lib/skylight/cli.rb +55 -60
- data/lib/skylight/cli/doctor.rb +13 -14
- data/lib/skylight/cli/helpers.rb +20 -22
- data/lib/skylight/cli/merger.rb +119 -116
- data/lib/skylight/config.rb +110 -96
- data/lib/skylight/errors.rb +8 -10
- data/lib/skylight/helpers.rb +35 -37
- data/lib/skylight/native.rb +13 -13
- data/lib/skylight/native_ext_fetcher.rb +30 -37
- data/lib/skylight/probes/sinatra_add_middleware.rb +2 -2
- data/lib/skylight/railtie.rb +32 -8
- data/lib/skylight/sinatra.rb +1 -1
- data/lib/skylight/trace.rb +4 -5
- data/lib/skylight/util/component.rb +76 -11
- data/lib/skylight/util/deploy.rb +10 -21
- data/lib/skylight/util/hostname.rb +4 -4
- data/lib/skylight/util/http.rb +134 -136
- data/lib/skylight/util/ssl.rb +6 -6
- data/lib/skylight/version.rb +1 -1
- metadata +51 -50
data/lib/skylight/util/deploy.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "json"
|
2
|
+
require "skylight/core/util/logging"
|
3
3
|
|
4
4
|
module Skylight
|
5
5
|
module Util
|
6
|
-
|
7
6
|
module Deploy
|
8
|
-
|
9
7
|
def self.build(config)
|
10
8
|
DEPLOY_TYPES.each do |type|
|
11
9
|
deploy = type.new(config)
|
@@ -15,7 +13,6 @@ module Skylight
|
|
15
13
|
end
|
16
14
|
|
17
15
|
class EmptyDeploy
|
18
|
-
|
19
16
|
attr_reader :config
|
20
17
|
attr_reader :timestamp
|
21
18
|
|
@@ -41,11 +38,10 @@ module Skylight
|
|
41
38
|
timestamp: timestamp,
|
42
39
|
deploy_id: id.to_s[0..100] # Keep this sane
|
43
40
|
}
|
44
|
-
hash[:git_sha] = git_sha
|
41
|
+
hash[:git_sha] = git_sha[0..40] if git_sha # A valid SHA will never exceed 40
|
45
42
|
hash[:description] = description[0..255] if description # Avoid massive descriptions
|
46
43
|
hash
|
47
44
|
end
|
48
|
-
|
49
45
|
end
|
50
46
|
|
51
47
|
class DefaultDeploy < EmptyDeploy
|
@@ -69,26 +65,24 @@ module Skylight
|
|
69
65
|
def description
|
70
66
|
config.get(:'deploy.description')
|
71
67
|
end
|
72
|
-
|
73
68
|
end
|
74
69
|
|
75
70
|
class HerokuDeploy < EmptyDeploy
|
76
|
-
|
77
71
|
def initialize(*)
|
78
72
|
super
|
79
73
|
@info = get_info
|
80
74
|
end
|
81
75
|
|
82
76
|
def id
|
83
|
-
@info ? @info[
|
77
|
+
@info ? @info["id"] : nil
|
84
78
|
end
|
85
79
|
|
86
80
|
def git_sha
|
87
|
-
@info ? @info[
|
81
|
+
@info ? @info["commit"] : nil
|
88
82
|
end
|
89
83
|
|
90
84
|
def description
|
91
|
-
@info ? @info[
|
85
|
+
@info ? @info["description"] : nil
|
92
86
|
end
|
93
87
|
|
94
88
|
private
|
@@ -97,16 +91,14 @@ module Skylight
|
|
97
91
|
info_path = config[:'heroku.dyno_info_path']
|
98
92
|
|
99
93
|
if File.exist?(info_path)
|
100
|
-
if info = JSON.parse(File.read(info_path))
|
101
|
-
info[
|
94
|
+
if (info = JSON.parse(File.read(info_path)))
|
95
|
+
info["release"]
|
102
96
|
end
|
103
97
|
end
|
104
98
|
end
|
105
|
-
|
106
99
|
end
|
107
100
|
|
108
101
|
class GitDeploy < EmptyDeploy
|
109
|
-
|
110
102
|
attr_reader :git_sha, :description
|
111
103
|
|
112
104
|
def initialize(*)
|
@@ -119,15 +111,12 @@ module Skylight
|
|
119
111
|
def get_info
|
120
112
|
Dir.chdir(config.root) do
|
121
113
|
info = `git log -1 --pretty="%H %s" 2>&1`
|
122
|
-
info.split(" ", 2).map(&:strip) if
|
114
|
+
info.split(" ", 2).map(&:strip) if $CHILD_STATUS.success?
|
123
115
|
end
|
124
116
|
end
|
125
|
-
|
126
117
|
end
|
127
118
|
|
128
|
-
DEPLOY_TYPES = [DefaultDeploy, HerokuDeploy, GitDeploy]
|
129
|
-
|
119
|
+
DEPLOY_TYPES = [DefaultDeploy, HerokuDeploy, GitDeploy].freeze
|
130
120
|
end
|
131
|
-
|
132
121
|
end
|
133
122
|
end
|
@@ -1,13 +1,13 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "socket"
|
2
|
+
require "securerandom"
|
3
3
|
|
4
4
|
module Skylight
|
5
5
|
module Util
|
6
6
|
module Hostname
|
7
7
|
def self.default_hostname
|
8
|
-
if hostname = Socket.gethostname
|
8
|
+
if (hostname = Socket.gethostname)
|
9
9
|
hostname.strip!
|
10
|
-
hostname = nil if hostname ==
|
10
|
+
hostname = nil if hostname == ""
|
11
11
|
end
|
12
12
|
|
13
13
|
hostname || "gen-#{SecureRandom.uuid}"
|
data/lib/skylight/util/http.rb
CHANGED
@@ -1,42 +1,38 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
1
|
+
require "uri"
|
2
|
+
require "json"
|
3
|
+
require "openssl"
|
4
|
+
require "net/http"
|
5
|
+
require "net/https"
|
6
|
+
require "skylight/core/util/gzip"
|
7
|
+
require "skylight/util/ssl"
|
8
8
|
|
9
9
|
module Skylight
|
10
10
|
module Util
|
11
11
|
class HTTP
|
12
|
-
CONTENT_ENCODING =
|
13
|
-
CONTENT_LENGTH =
|
14
|
-
CONTENT_TYPE =
|
15
|
-
ACCEPT =
|
16
|
-
X_VERSION_HDR =
|
17
|
-
APPLICATION_JSON =
|
18
|
-
AUTHORIZATION =
|
19
|
-
DEFLATE =
|
20
|
-
GZIP =
|
12
|
+
CONTENT_ENCODING = "content-encoding".freeze
|
13
|
+
CONTENT_LENGTH = "content-length".freeze
|
14
|
+
CONTENT_TYPE = "content-type".freeze
|
15
|
+
ACCEPT = "Accept".freeze
|
16
|
+
X_VERSION_HDR = "x-skylight-agent-version".freeze
|
17
|
+
APPLICATION_JSON = "application/json".freeze
|
18
|
+
AUTHORIZATION = "authorization".freeze
|
19
|
+
DEFLATE = "deflate".freeze
|
20
|
+
GZIP = "gzip".freeze
|
21
21
|
|
22
22
|
include Core::Util::Logging
|
23
23
|
|
24
24
|
attr_accessor :authentication
|
25
25
|
attr_reader :host, :port, :config
|
26
26
|
|
27
|
-
READ_EXCEPTIONS = [Timeout::Error, EOFError]
|
28
|
-
# This doesn't exist on Ruby 1.9.3
|
29
|
-
READ_EXCEPTIONS << Net::ReadTimeout if defined?(Net::ReadTimeout)
|
30
|
-
READ_EXCEPTIONS.freeze
|
27
|
+
READ_EXCEPTIONS = [Timeout::Error, EOFError, Net::ReadTimeout].freeze
|
31
28
|
|
32
29
|
class StartError < StandardError
|
33
30
|
attr_reader :original
|
34
31
|
|
35
|
-
def initialize(
|
36
|
-
@original =
|
37
|
-
super
|
32
|
+
def initialize(error)
|
33
|
+
@original = error
|
34
|
+
super error.inspect
|
38
35
|
end
|
39
|
-
|
40
36
|
end
|
41
37
|
|
42
38
|
class ReadResponseError < StandardError; end
|
@@ -44,27 +40,28 @@ module Skylight
|
|
44
40
|
def initialize(config, service = :auth, opts = {})
|
45
41
|
@config = config
|
46
42
|
|
47
|
-
unless url = config["#{service}_url"]
|
43
|
+
unless (url = config["#{service}_url"])
|
48
44
|
raise ArgumentError, "no URL specified for #{service}"
|
49
45
|
end
|
50
46
|
|
51
47
|
url = URI.parse(url)
|
52
48
|
|
53
|
-
@ssl = url.scheme ==
|
49
|
+
@ssl = url.scheme == "https"
|
54
50
|
@host = url.host
|
55
51
|
@port = url.port
|
56
52
|
|
57
|
-
if proxy_url = config[:proxy_url]
|
53
|
+
if (proxy_url = config[:proxy_url])
|
58
54
|
proxy_url = URI.parse(proxy_url)
|
59
|
-
@proxy_addr
|
60
|
-
@
|
55
|
+
@proxy_addr = proxy_url.host
|
56
|
+
@proxy_port = proxy_url.port
|
57
|
+
@proxy_user, @proxy_pass = (proxy_url.userinfo || "").split(/:/)
|
61
58
|
end
|
62
59
|
|
63
60
|
@open_timeout = get_timeout(:connect, config, service, opts)
|
64
61
|
@read_timeout = get_timeout(:read, config, service, opts)
|
65
62
|
|
66
63
|
@deflate = config["#{service}_http_deflate"]
|
67
|
-
@authentication = config[:
|
64
|
+
@authentication = config[:authentication]
|
68
65
|
end
|
69
66
|
|
70
67
|
def get(endpoint, hdrs = {})
|
@@ -82,146 +79,147 @@ module Skylight
|
|
82
79
|
execute(request, body)
|
83
80
|
end
|
84
81
|
|
85
|
-
|
82
|
+
private
|
86
83
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
84
|
+
def get_timeout(type, config, service, opts)
|
85
|
+
config.duration_ms("#{service}_http_#{type}_timeout") ||
|
86
|
+
opts[:timeout] || 15
|
87
|
+
end
|
91
88
|
|
92
|
-
|
93
|
-
|
89
|
+
def build_request(type, endpoint, hdrs, length = nil)
|
90
|
+
headers = {}
|
94
91
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
92
|
+
headers[CONTENT_LENGTH] = length.to_s if length
|
93
|
+
headers[AUTHORIZATION] = authentication if authentication
|
94
|
+
headers[ACCEPT] = APPLICATION_JSON
|
95
|
+
headers[X_VERSION_HDR] = VERSION
|
96
|
+
headers[CONTENT_ENCODING] = GZIP if length && @deflate
|
97
|
+
|
98
|
+
hdrs.each do |k, v|
|
99
|
+
headers[k] = v
|
100
|
+
end
|
100
101
|
|
101
|
-
|
102
|
-
headers[k] = v
|
102
|
+
type.new(endpoint, headers)
|
103
103
|
end
|
104
104
|
|
105
|
-
|
106
|
-
|
105
|
+
def do_request(http, req)
|
106
|
+
begin
|
107
|
+
client = http.start
|
108
|
+
rescue => e
|
109
|
+
# TODO: Retry here
|
110
|
+
raise StartError, e
|
111
|
+
end
|
107
112
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
raise StartError, e
|
114
|
-
end
|
113
|
+
begin
|
114
|
+
res = client.request(req)
|
115
|
+
rescue *READ_EXCEPTIONS => e
|
116
|
+
raise ReadResponseError, e.inspect
|
117
|
+
end
|
115
118
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
raise ReadResponseError, e.inspect
|
119
|
+
yield res
|
120
|
+
ensure
|
121
|
+
client.finish if client
|
120
122
|
end
|
121
123
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
124
|
+
def execute(req, body = nil)
|
125
|
+
t do
|
126
|
+
fmt "executing HTTP request; host=%s; port=%s; path=%s, body=%s",
|
127
|
+
@host, @port, req.path, body && body.bytesize
|
128
|
+
end
|
126
129
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
+
if body
|
131
|
+
body = Gzip.compress(body) if @deflate
|
132
|
+
req.body = body
|
133
|
+
end
|
130
134
|
|
131
|
-
|
132
|
-
|
133
|
-
req.body = body
|
134
|
-
end
|
135
|
+
http = Net::HTTP.new(@host, @port,
|
136
|
+
@proxy_addr, @proxy_port, @proxy_user, @proxy_pass)
|
135
137
|
|
136
|
-
|
137
|
-
|
138
|
+
http.open_timeout = @open_timeout
|
139
|
+
http.read_timeout = @read_timeout
|
138
140
|
|
139
|
-
|
140
|
-
|
141
|
+
if @ssl
|
142
|
+
http.use_ssl = true
|
141
143
|
|
142
|
-
|
143
|
-
|
144
|
+
unless SSL.ca_cert_file?
|
145
|
+
http.ca_file = SSL.ca_cert_file_or_default
|
146
|
+
end
|
144
147
|
|
145
|
-
|
146
|
-
http.ca_file = SSL.ca_cert_file_or_default
|
148
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
147
149
|
end
|
148
150
|
|
149
|
-
http
|
150
|
-
|
151
|
+
do_request(http, req) do |res|
|
152
|
+
unless res.code =~ /2\d\d/
|
153
|
+
debug "server responded with #{res.code}"
|
154
|
+
t { fmt "body=%s", res.body }
|
155
|
+
end
|
151
156
|
|
152
|
-
|
153
|
-
unless res.code =~ /2\d\d/
|
154
|
-
debug "server responded with #{res.code}"
|
155
|
-
t { fmt "body=%s", res.body }
|
157
|
+
Response.new(res.code.to_i, res, res.body)
|
156
158
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
@body = body
|
159
|
+
rescue Exception => e
|
160
|
+
error "http %s %s failed; error=%s; msg=%s", req.method, req.path, e.class, e.message
|
161
|
+
t { e.backtrace.join("\n") }
|
162
|
+
ErrorResponse.new(req, e)
|
163
|
+
end
|
164
|
+
|
165
|
+
class Response
|
166
|
+
attr_reader :status, :headers, :body, :exception
|
167
|
+
|
168
|
+
def initialize(status, headers, body)
|
169
|
+
@status = status
|
170
|
+
@headers = headers
|
171
|
+
|
172
|
+
if (headers[CONTENT_TYPE] || "").include?(APPLICATION_JSON)
|
173
|
+
begin
|
174
|
+
@body = JSON.parse(body)
|
175
|
+
rescue JSON::ParserError
|
176
|
+
@body = body # not really JSON I guess
|
177
|
+
end
|
178
|
+
else
|
179
|
+
@body = body
|
178
180
|
end
|
179
|
-
else
|
180
|
-
@body = body
|
181
181
|
end
|
182
|
-
end
|
183
182
|
|
184
|
-
|
185
|
-
|
186
|
-
|
183
|
+
def success?
|
184
|
+
status >= 200 && status < 300
|
185
|
+
end
|
187
186
|
|
188
|
-
|
189
|
-
|
190
|
-
|
187
|
+
def to_s
|
188
|
+
body.to_s
|
189
|
+
end
|
191
190
|
|
192
|
-
|
193
|
-
|
191
|
+
def get(key)
|
192
|
+
return nil unless body.is_a?(Hash)
|
194
193
|
|
195
|
-
|
196
|
-
|
197
|
-
|
194
|
+
res = body
|
195
|
+
key.split(".").each do |part|
|
196
|
+
return unless (res = res[part])
|
197
|
+
end
|
198
|
+
res
|
198
199
|
end
|
199
|
-
res
|
200
|
-
end
|
201
200
|
|
202
|
-
|
203
|
-
|
204
|
-
|
201
|
+
def respond_to_missing?(name, include_all = false)
|
202
|
+
super || body.respond_to?(name, include_all)
|
203
|
+
end
|
205
204
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
205
|
+
def method_missing(name, *args, &blk)
|
206
|
+
if respond_to_missing?(name)
|
207
|
+
body.send(name, *args, &blk)
|
208
|
+
else
|
209
|
+
super
|
210
|
+
end
|
211
211
|
end
|
212
212
|
end
|
213
|
-
end
|
214
213
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
214
|
+
ErrorResponse = Struct.new(:request, :exception) do
|
215
|
+
def status
|
216
|
+
nil
|
217
|
+
end
|
219
218
|
|
220
|
-
|
221
|
-
|
219
|
+
def success?
|
220
|
+
false
|
221
|
+
end
|
222
222
|
end
|
223
|
-
end
|
224
|
-
|
225
223
|
end
|
226
224
|
end
|
227
225
|
end
|