spectre-core 1.8.0
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 +7 -0
- data/exe/spectre +487 -0
- data/lib/spectre.rb +434 -0
- data/lib/spectre/assertion.rb +277 -0
- data/lib/spectre/bag.rb +19 -0
- data/lib/spectre/curl.rb +368 -0
- data/lib/spectre/database/postgres.rb +78 -0
- data/lib/spectre/diagnostic.rb +29 -0
- data/lib/spectre/environment.rb +26 -0
- data/lib/spectre/ftp.rb +195 -0
- data/lib/spectre/helpers.rb +64 -0
- data/lib/spectre/http.rb +343 -0
- data/lib/spectre/http/basic_auth.rb +22 -0
- data/lib/spectre/http/keystone.rb +98 -0
- data/lib/spectre/logger.rb +144 -0
- data/lib/spectre/logger/console.rb +142 -0
- data/lib/spectre/logger/file.rb +96 -0
- data/lib/spectre/mixin.rb +41 -0
- data/lib/spectre/mysql.rb +97 -0
- data/lib/spectre/reporter/console.rb +103 -0
- data/lib/spectre/reporter/junit.rb +98 -0
- data/lib/spectre/resources.rb +46 -0
- data/lib/spectre/ssh.rb +149 -0
- metadata +140 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
module Spectre
|
2
|
+
module Diagnostic
|
3
|
+
module Stopwatch
|
4
|
+
@@duration = 0.0
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def start_watch
|
8
|
+
@@start_time = Time.now
|
9
|
+
end
|
10
|
+
|
11
|
+
def stop_watch
|
12
|
+
@@end_time = Time.now
|
13
|
+
end
|
14
|
+
|
15
|
+
def measure
|
16
|
+
start_watch
|
17
|
+
yield
|
18
|
+
stop_watch
|
19
|
+
end
|
20
|
+
|
21
|
+
def duration
|
22
|
+
@@end_time - @@start_time
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Spectre.delegate :start_watch, :stop_watch, :duration, :measure, to: Stopwatch
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
def to_recursive_ostruct(hash)
|
4
|
+
OpenStruct.new(hash.each_with_object({}) do |(key, val), memo|
|
5
|
+
memo[key] = val.is_a?(Hash) ? to_recursive_ostruct(val) : val
|
6
|
+
end)
|
7
|
+
end
|
8
|
+
|
9
|
+
module Spectre
|
10
|
+
module Environment
|
11
|
+
class << self
|
12
|
+
@@environment = OpenStruct.new
|
13
|
+
|
14
|
+
def env
|
15
|
+
@@environment
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Spectre.register do |config|
|
20
|
+
@@environment = to_recursive_ostruct(config)
|
21
|
+
@@environment.freeze
|
22
|
+
end
|
23
|
+
|
24
|
+
Spectre.delegate :env, to: Environment
|
25
|
+
end
|
26
|
+
end
|
data/lib/spectre/ftp.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'net/ftp'
|
2
|
+
require 'net/sftp'
|
3
|
+
require 'logger'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
|
7
|
+
module Spectre
|
8
|
+
module FTP
|
9
|
+
@@cfg = {}
|
10
|
+
|
11
|
+
class FTPConnection < DslClass
|
12
|
+
def initialize host, username, password, opts, logger
|
13
|
+
@__logger = logger
|
14
|
+
@__session = nil
|
15
|
+
|
16
|
+
@__host = host
|
17
|
+
@__username = username
|
18
|
+
@__password = password
|
19
|
+
@__opts = opts
|
20
|
+
end
|
21
|
+
|
22
|
+
def connect!
|
23
|
+
return unless @__session == nil or @__session.closed?
|
24
|
+
@__logger.info "Connecting to '#{@__host}' with user '#{@__username}'"
|
25
|
+
@__session = Net::FTP.new(@__host, @__opts)
|
26
|
+
@__session.login @__username, @__password
|
27
|
+
end
|
28
|
+
|
29
|
+
def close
|
30
|
+
return unless @__session and not @__session.closed?
|
31
|
+
@__session.close
|
32
|
+
end
|
33
|
+
|
34
|
+
def can_connect?
|
35
|
+
begin
|
36
|
+
connect!
|
37
|
+
return true
|
38
|
+
rescue
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def download remotefile, to: File.basename(remotefile)
|
44
|
+
connect!
|
45
|
+
@__logger.info "Downloading '#{@__username}@#{@__host}:#{File.join @__session.pwd, remotefile}' to '#{File.expand_path to}'"
|
46
|
+
@__session.getbinaryfile(remotefile, to)
|
47
|
+
end
|
48
|
+
|
49
|
+
def upload localfile, to: File.basename(localfile)
|
50
|
+
connect!
|
51
|
+
@__logger.info "Uploading '#{File.expand_path localfile}' to '#{@__username}@#{@__host}:#{File.join @__session.pwd, to}'"
|
52
|
+
@__session.putbinaryfile(localfile, to)
|
53
|
+
end
|
54
|
+
|
55
|
+
def list
|
56
|
+
connect!
|
57
|
+
file_list = @__session.list
|
58
|
+
@__logger.info "Listing file in #{@__session.pwd}\n#{file_list}"
|
59
|
+
file_list
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
class SFTPConnection < DslClass
|
65
|
+
def initialize host, username, opts, logger
|
66
|
+
opts[:non_interactive] = true
|
67
|
+
|
68
|
+
@__logger = logger
|
69
|
+
@__session = nil
|
70
|
+
@__host = host
|
71
|
+
@__username = username
|
72
|
+
@__opts = opts
|
73
|
+
end
|
74
|
+
|
75
|
+
def connect!
|
76
|
+
return unless @__session == nil or @__session.closed?
|
77
|
+
@__logger.info "Connecting to '#{@__host}' with user '#{@__username}'"
|
78
|
+
@__session = Net::SFTP.start(@__host, @__username, @__opts)
|
79
|
+
@__session.connect!
|
80
|
+
end
|
81
|
+
|
82
|
+
def close
|
83
|
+
return unless @__session and not @__session.closed?
|
84
|
+
# @__session.close!
|
85
|
+
end
|
86
|
+
|
87
|
+
def can_connect?
|
88
|
+
begin
|
89
|
+
connect!
|
90
|
+
return true
|
91
|
+
rescue
|
92
|
+
return false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def download remotefile, to: File.basename(remotefile)
|
97
|
+
@__logger.info "Downloading '#{@__username}@#{@__host}:#{remotefile}' to '#{File.expand_path to}'"
|
98
|
+
connect!
|
99
|
+
@__session.download!(remotefile, to)
|
100
|
+
end
|
101
|
+
|
102
|
+
def upload localfile, to: File.basename(localfile)
|
103
|
+
@__logger.info "Uploading '#{File.expand_path localfile}' to '#{@__username}@#{@__host}:#{to}'"
|
104
|
+
connect!
|
105
|
+
@__session.upload!(localfile, to)
|
106
|
+
end
|
107
|
+
|
108
|
+
def stat path
|
109
|
+
connect!
|
110
|
+
file_info = @__session.stat! path
|
111
|
+
@__logger.info "Stat '#{path}'\n#{JSON.pretty_generate file_info.attributes}"
|
112
|
+
file_info.attributes
|
113
|
+
end
|
114
|
+
|
115
|
+
def exists path
|
116
|
+
begin
|
117
|
+
file_info = @__session.stat! path
|
118
|
+
|
119
|
+
rescue Net::SFTP::StatusException => e
|
120
|
+
return false if e.description == 'no such file'
|
121
|
+
raise e
|
122
|
+
end
|
123
|
+
|
124
|
+
return true
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
class << self
|
130
|
+
def ftp name, config={}, &block
|
131
|
+
raise "FTP connection '#{name}' not configured" unless @@cfg.has_key?(name) or config.count > 0
|
132
|
+
cfg = @@cfg[name] || {}
|
133
|
+
|
134
|
+
host = config[:host] || cfg['host'] || name
|
135
|
+
username = config[:username] || cfg['username']
|
136
|
+
password = config[:password] || cfg['password']
|
137
|
+
|
138
|
+
opts = {}
|
139
|
+
opts[:ssl] = config[:ssl]
|
140
|
+
opts[:port] = config[:port] || cfg['port'] || 21
|
141
|
+
|
142
|
+
@@logger.info "Connecting to #{host} with user #{username}"
|
143
|
+
|
144
|
+
ftp_conn = FTPConnection.new(host, username, password, opts, @@logger)
|
145
|
+
|
146
|
+
begin
|
147
|
+
ftp_conn.instance_eval &block
|
148
|
+
ensure
|
149
|
+
ftp_conn.close
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def sftp name, config={}, &block
|
154
|
+
raise "FTP connection '#{name}' not configured" unless @@cfg.has_key?(name) or config.count > 0
|
155
|
+
|
156
|
+
cfg = @@cfg[name] || {}
|
157
|
+
|
158
|
+
host = config[:host] || cfg['host'] || name
|
159
|
+
username = config[:username] || cfg['username']
|
160
|
+
password = config[:password] || cfg['password']
|
161
|
+
|
162
|
+
opts = {}
|
163
|
+
opts[:password] = password
|
164
|
+
opts[:port] = config[:port] || cfg['port'] || 22
|
165
|
+
opts[:keys] = [cfg['key']] if cfg.has_key? 'key'
|
166
|
+
opts[:passphrase] = cfg['passphrase'] if cfg.has_key? 'passphrase'
|
167
|
+
|
168
|
+
opts[:auth_methods] = []
|
169
|
+
opts[:auth_methods].push 'publickey' if opts[:keys]
|
170
|
+
opts[:auth_methods].push 'password' if opts[:password]
|
171
|
+
|
172
|
+
sftp_con = SFTPConnection.new(host, username, opts, @@logger)
|
173
|
+
|
174
|
+
begin
|
175
|
+
sftp_con.instance_eval &block
|
176
|
+
ensure
|
177
|
+
sftp_con.close
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
Spectre.register do |config|
|
183
|
+
@@logger = ::Logger.new config['log_file'], progname: 'spectre/ftp'
|
184
|
+
|
185
|
+
if config.has_key? 'ftp'
|
186
|
+
|
187
|
+
config['ftp'].each do |name, cfg|
|
188
|
+
@@cfg[name] = cfg
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
Spectre.delegate :ftp, :sftp, to: self
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'json'
|
3
|
+
require 'date'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
class ::String
|
7
|
+
def as_json
|
8
|
+
JSON.parse(self, object_class: OpenStruct)
|
9
|
+
end
|
10
|
+
|
11
|
+
def as_date
|
12
|
+
DateTime.parse(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def content with: nil
|
16
|
+
fail "'#{self}' is not a file path, or the file does not exist." if !File.exists? self
|
17
|
+
file_content = File.read(self)
|
18
|
+
|
19
|
+
if with
|
20
|
+
with.each do |key, value|
|
21
|
+
file_content = file_content.gsub '#{' + key.to_s + '}', value.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
file_content
|
26
|
+
end
|
27
|
+
|
28
|
+
def exists?
|
29
|
+
File.exists? self
|
30
|
+
end
|
31
|
+
|
32
|
+
def remove!
|
33
|
+
fail "'#{self}' is not a file path, or the file does not exist." if !File.exists? self
|
34
|
+
|
35
|
+
File.delete self
|
36
|
+
end
|
37
|
+
|
38
|
+
def trim count=50
|
39
|
+
if (self.length + 3) > count
|
40
|
+
return self[0..count] + '...'
|
41
|
+
end
|
42
|
+
|
43
|
+
self
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
class ::OpenStruct
|
49
|
+
def to_json *args, **kwargs
|
50
|
+
self.to_h.inject({}) { |memo, (k,v)| memo[k] = v.is_a?(OpenStruct) ? v.to_h : v; memo }.to_json(*args, **kwargs)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
class ::Hash
|
56
|
+
def symbolize_keys
|
57
|
+
self.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def uuid length = 5
|
63
|
+
SecureRandom.uuid().gsub('-', '')[0..length]
|
64
|
+
end
|
data/lib/spectre/http.rb
ADDED
@@ -0,0 +1,343 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'openssl'
|
3
|
+
require 'json'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'logger'
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
|
9
|
+
module Spectre
|
10
|
+
module Http
|
11
|
+
DEFAULT_HTTP_CONFIG = {
|
12
|
+
'method' => 'GET',
|
13
|
+
'path' => '',
|
14
|
+
'host' => nil,
|
15
|
+
'port' => 80,
|
16
|
+
'scheme' => 'http',
|
17
|
+
'use_ssl' => false,
|
18
|
+
'cert' => nil,
|
19
|
+
'headers' => nil,
|
20
|
+
'query' => nil,
|
21
|
+
'content_type' => '',
|
22
|
+
}
|
23
|
+
|
24
|
+
@@modules = []
|
25
|
+
|
26
|
+
class SpectreHttpRequest < Spectre::DslClass
|
27
|
+
def initialize request
|
28
|
+
@__req = request
|
29
|
+
end
|
30
|
+
|
31
|
+
def method method_name
|
32
|
+
@__req['method'] = method_name.upcase
|
33
|
+
end
|
34
|
+
|
35
|
+
def url base_url
|
36
|
+
@__req['base_url'] = base_url
|
37
|
+
end
|
38
|
+
|
39
|
+
def path url_path
|
40
|
+
@__req['path'] = url_path
|
41
|
+
end
|
42
|
+
|
43
|
+
def header name, value
|
44
|
+
@__req['headers'] = [] if not @__req['headers']
|
45
|
+
@__req['headers'].append [name, value.to_s.strip]
|
46
|
+
end
|
47
|
+
|
48
|
+
def param name, value
|
49
|
+
@__req['query'] = [] if not @__req['query']
|
50
|
+
@__req['query'].append [name, value.to_s.strip]
|
51
|
+
end
|
52
|
+
|
53
|
+
def content_type media_type
|
54
|
+
@__req['headers'] = [] if not @__req['headers']
|
55
|
+
@__req['headers'].append ['Content-Type', media_type]
|
56
|
+
end
|
57
|
+
|
58
|
+
def json data
|
59
|
+
data = data.to_h if data.is_a? OpenStruct
|
60
|
+
body JSON.pretty_generate(data)
|
61
|
+
content_type 'application/json'
|
62
|
+
end
|
63
|
+
|
64
|
+
def body body_content
|
65
|
+
raise 'Body value must be a string' if not body_content.is_a? String
|
66
|
+
@__req['body'] = body_content
|
67
|
+
end
|
68
|
+
|
69
|
+
def ensure_success!
|
70
|
+
@__req['ensure_success'] = true
|
71
|
+
end
|
72
|
+
|
73
|
+
def ensure_success?
|
74
|
+
@__req['ensure_success']
|
75
|
+
end
|
76
|
+
|
77
|
+
def authenticate method
|
78
|
+
@__req['auth'] = method
|
79
|
+
end
|
80
|
+
|
81
|
+
def certificate path
|
82
|
+
@__req['cert'] = path
|
83
|
+
use_ssl!
|
84
|
+
end
|
85
|
+
|
86
|
+
def use_ssl!
|
87
|
+
@__req['use_ssl'] = true
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_s
|
91
|
+
@__req.to_s
|
92
|
+
end
|
93
|
+
|
94
|
+
alias_method :auth, :authenticate
|
95
|
+
alias_method :cert, :certificate
|
96
|
+
alias_method :media_type, :content_type
|
97
|
+
end
|
98
|
+
|
99
|
+
class SpectreHttpHeader
|
100
|
+
def initialize headers
|
101
|
+
@headers = headers || {}
|
102
|
+
end
|
103
|
+
|
104
|
+
def [] key
|
105
|
+
@headers[key.downcase].first
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_s
|
109
|
+
@headers.to_s
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class SpectreHttpResponse
|
114
|
+
def initialize res
|
115
|
+
@res = res
|
116
|
+
@data = nil
|
117
|
+
@headers = SpectreHttpHeader.new @res[:headers]
|
118
|
+
end
|
119
|
+
|
120
|
+
def code
|
121
|
+
@res[:code]
|
122
|
+
end
|
123
|
+
|
124
|
+
def message
|
125
|
+
@res[:message]
|
126
|
+
end
|
127
|
+
|
128
|
+
def headers
|
129
|
+
@headers
|
130
|
+
end
|
131
|
+
|
132
|
+
def body
|
133
|
+
@res[:body]
|
134
|
+
end
|
135
|
+
|
136
|
+
def json
|
137
|
+
return nil if not @res[:body]
|
138
|
+
|
139
|
+
if @data == nil
|
140
|
+
begin
|
141
|
+
@data = JSON.parse(@res[:body], object_class: OpenStruct)
|
142
|
+
rescue
|
143
|
+
raise "Body content is not a valid JSON:\n#{@res[:body]}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
@data
|
148
|
+
end
|
149
|
+
|
150
|
+
def success?
|
151
|
+
@res[:code] < 400
|
152
|
+
end
|
153
|
+
|
154
|
+
def to_s
|
155
|
+
@res.to_s
|
156
|
+
end
|
157
|
+
|
158
|
+
def pretty
|
159
|
+
@res.pretty
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
class << self
|
165
|
+
@@http_cfg = {}
|
166
|
+
@@response = nil
|
167
|
+
@@request = nil
|
168
|
+
@@modules = []
|
169
|
+
|
170
|
+
def https name, &block
|
171
|
+
http(name, secure: true, &block)
|
172
|
+
end
|
173
|
+
|
174
|
+
def http name, secure: nil, &block
|
175
|
+
req = {}
|
176
|
+
|
177
|
+
if @@http_cfg.has_key? name
|
178
|
+
req.merge! @@http_cfg[name]
|
179
|
+
raise "No `base_url' set for HTTP client '#{name}'. Check your HTTP config in your environment." if !req['base_url']
|
180
|
+
else
|
181
|
+
req['base_url'] = name
|
182
|
+
end
|
183
|
+
|
184
|
+
req['user_ssl'] = secure if secure != nil
|
185
|
+
|
186
|
+
SpectreHttpRequest.new(req).instance_eval(&block) if block_given?
|
187
|
+
|
188
|
+
invoke(req)
|
189
|
+
end
|
190
|
+
|
191
|
+
def request
|
192
|
+
raise 'No request has been invoked yet' unless @@request
|
193
|
+
@@request
|
194
|
+
end
|
195
|
+
|
196
|
+
def response
|
197
|
+
raise 'There is no response. No request has been invoked yet.' unless @@response
|
198
|
+
@@response
|
199
|
+
end
|
200
|
+
|
201
|
+
def register mod
|
202
|
+
raise 'Module must not be nil' unless mod
|
203
|
+
@@modules << mod
|
204
|
+
end
|
205
|
+
|
206
|
+
private
|
207
|
+
|
208
|
+
def try_format_json str, pretty: false
|
209
|
+
return str unless str or str.empty?
|
210
|
+
|
211
|
+
begin
|
212
|
+
json = JSON.parse str
|
213
|
+
|
214
|
+
if pretty
|
215
|
+
str = JSON.pretty_generate(json)
|
216
|
+
else
|
217
|
+
str = JSON.dump(json)
|
218
|
+
end
|
219
|
+
rescue
|
220
|
+
# do nothing
|
221
|
+
end
|
222
|
+
|
223
|
+
str
|
224
|
+
end
|
225
|
+
|
226
|
+
def invoke req
|
227
|
+
@@request = nil
|
228
|
+
|
229
|
+
if req['cert'] or req['use_ssl']
|
230
|
+
scheme = 'https'
|
231
|
+
else
|
232
|
+
scheme = 'http'
|
233
|
+
end
|
234
|
+
|
235
|
+
base_url = req['base_url']
|
236
|
+
|
237
|
+
if not base_url.match /http(?:s)?:\/\//
|
238
|
+
base_url = scheme + '://' + base_url
|
239
|
+
end
|
240
|
+
|
241
|
+
if req['path']
|
242
|
+
base_url = base_url + '/' if not base_url.end_with? '/'
|
243
|
+
base_url += req['path']
|
244
|
+
end
|
245
|
+
|
246
|
+
uri = URI(base_url)
|
247
|
+
|
248
|
+
raise "'#{uri}' is not a valid uri" if not uri.host
|
249
|
+
|
250
|
+
uri.query = URI.encode_www_form(req['query']) unless not req['query'] or req['query'].empty?
|
251
|
+
|
252
|
+
net_http = Net::HTTP.new(uri.host, uri.port)
|
253
|
+
|
254
|
+
if uri.scheme == 'https'
|
255
|
+
net_http.use_ssl = true
|
256
|
+
|
257
|
+
if req.has_key? 'cert'
|
258
|
+
raise "Certificate '#{req['cert']}' does not exist" unless File.exists? req['cert']
|
259
|
+
net_http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
260
|
+
net_http.ca_file = req['cert']
|
261
|
+
else
|
262
|
+
net_http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
net_req = Net::HTTPGenericRequest.new(req['method'], true, true, uri)
|
267
|
+
net_req.body = req['body']
|
268
|
+
net_req.content_type = req['content_type'] if req['content_type'] and not req['content_type'].empty?
|
269
|
+
|
270
|
+
if req['headers']
|
271
|
+
req['headers'].each do |header|
|
272
|
+
net_req[header[0]] = header[1]
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
req_id = SecureRandom.uuid()[0..5]
|
277
|
+
|
278
|
+
# Log request
|
279
|
+
|
280
|
+
req_log = "[>] #{req_id} #{req['method']} #{uri}"
|
281
|
+
req['headers'].each do |header|
|
282
|
+
req_log += "\n#{header[0].to_s.ljust(30, '.')}: #{header[1].to_s}"
|
283
|
+
end if req['headers']
|
284
|
+
req_log += "\n" + try_format_json(req['body'], pretty: true) if req['body'] != nil and not req['body'].empty?
|
285
|
+
|
286
|
+
@@logger.info req_log
|
287
|
+
|
288
|
+
# Request
|
289
|
+
|
290
|
+
start_time = Time.now
|
291
|
+
|
292
|
+
@@modules.each do |mod|
|
293
|
+
mod.on_req(net_http, net_req, req) if mod.respond_to? :on_req
|
294
|
+
end
|
295
|
+
|
296
|
+
net_res = net_http.request(net_req)
|
297
|
+
|
298
|
+
end_time = Time.now
|
299
|
+
|
300
|
+
@@modules.each do |mod|
|
301
|
+
mod.on_res(net_http, net_res, req) if mod.respond_to? :on_res
|
302
|
+
end
|
303
|
+
|
304
|
+
# Log response
|
305
|
+
|
306
|
+
res_log = "[<] #{req_id} #{net_res.code} #{net_res.message} (#{end_time - start_time}s)\n"
|
307
|
+
net_res.each_header do |header, value|
|
308
|
+
res_log += "#{header.to_s.ljust(30, '.')}: #{value}\n"
|
309
|
+
end
|
310
|
+
res_log += try_format_json(net_res.body, pretty: true) if net_res.body != nil and !net_res.body.empty?
|
311
|
+
|
312
|
+
@@logger.info(res_log)
|
313
|
+
|
314
|
+
if req['ensure_success']
|
315
|
+
code = Integer(net_res.code)
|
316
|
+
fail "Response code of #{req_id} did not indicate success: #{net_res.code} #{net_res.message}" if code >= 400
|
317
|
+
end
|
318
|
+
|
319
|
+
@@request = OpenStruct.new(req)
|
320
|
+
@@response = SpectreHttpResponse.new({
|
321
|
+
code: net_res.code.to_i,
|
322
|
+
message: net_res.message,
|
323
|
+
headers: net_res.to_hash,
|
324
|
+
body: net_res.body
|
325
|
+
})
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
Spectre.register do |config|
|
330
|
+
@@logger = ::Logger.new config['log_file'], progname: 'spectre/http'
|
331
|
+
|
332
|
+
if config.has_key? 'http'
|
333
|
+
@@http_cfg = {}
|
334
|
+
|
335
|
+
config['http'].each do |name, cfg|
|
336
|
+
@@http_cfg[name] = cfg
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
Spectre.delegate :http, :https, :request, :response, to: self
|
342
|
+
end
|
343
|
+
end
|