spectre-core 1.9.0 → 1.12.1

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.
data/lib/spectre/curl.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require_relative '../spectre'
2
+
1
3
  require 'open3'
2
4
  require 'ostruct'
3
5
 
@@ -21,17 +23,17 @@ module Spectre::Curl
21
23
  end
22
24
 
23
25
  def header name, value
24
- @__req['headers'] = [] if not @__req['headers']
26
+ @__req['headers'] = [] unless @__req['headers']
25
27
  @__req['headers'].append [name, value.to_s.strip]
26
28
  end
27
29
 
28
30
  def param name, value
29
- @__req['query'] = [] if not @__req['query']
31
+ @__req['query'] = [] unless @__req['query']
30
32
  @__req['query'].append [name, value.to_s.strip]
31
33
  end
32
34
 
33
35
  def content_type media_type
34
- @__req['headers'] = [] if not @__req['headers']
36
+ @__req['headers'] = [] unless @__req['headers']
35
37
  @__req['headers'].append ['Content-Type', media_type]
36
38
  end
37
39
 
@@ -111,7 +113,7 @@ module Spectre::Curl
111
113
  end
112
114
 
113
115
  def json
114
- return nil if not @res[:body]
116
+ return nil unless @res[:body]
115
117
 
116
118
  if @data == nil
117
119
  begin
@@ -161,28 +163,31 @@ module Spectre::Curl
161
163
 
162
164
  if @@http_cfg.key? name
163
165
  req.merge! @@http_cfg[name]
164
- raise "No `base_url' set for HTTP client '#{name}'. Check your HTTP config in your environment." if !req['base_url']
166
+ raise "No `base_url' set for HTTP client '#{name}'. Check your HTTP config in your environment." unless req['base_url']
165
167
  else
166
168
  req['base_url'] = name
167
169
  end
168
170
 
169
- SpectreHttpRequest.new(req).instance_eval(&block) if block_given?
171
+ SpectreHttpRequest.new(req)._evaluate(&block) if block_given?
170
172
 
171
173
  invoke(req)
172
174
  end
173
175
 
174
176
  def curl_request
175
177
  raise 'No request has been invoked yet' unless @@request
178
+
176
179
  @@request
177
180
  end
178
181
 
179
182
  def curl_response
180
183
  raise 'There is no response. No request has been invoked yet.' unless @@response
184
+
181
185
  @@response
182
186
  end
183
187
 
184
188
  def register mod
185
189
  raise 'Module must not be nil' unless mod
190
+
186
191
  @@modules << mod
187
192
  end
188
193
 
@@ -192,7 +197,8 @@ module Spectre::Curl
192
197
  return str unless str or str.empty?
193
198
 
194
199
  begin
195
- json = JSON.parse str
200
+ json = JSON.parse(str)
201
+ json.obfuscate!(@@secure_keys) unless @@debug
196
202
 
197
203
  if pretty
198
204
  str = JSON.pretty_generate(json)
@@ -206,6 +212,25 @@ module Spectre::Curl
206
212
  str
207
213
  end
208
214
 
215
+ def secure? key
216
+ @@secure_keys.any? { |x| key.to_s.downcase.include? x.downcase }
217
+ end
218
+
219
+ def header_to_s headers
220
+ s = ''
221
+
222
+ return s unless headers
223
+
224
+ headers.each do |header|
225
+ key = header[0].to_s
226
+ value = header[1].to_s
227
+ value = '*****' if secure?(key) and not @@debug
228
+ s += "#{key.ljust(30, '.')}: #{value}\n"
229
+ end
230
+
231
+ s
232
+ end
233
+
209
234
  def invoke req
210
235
  cmd = [@@curl_path]
211
236
 
@@ -217,12 +242,12 @@ module Spectre::Curl
217
242
 
218
243
  uri = req['base_url']
219
244
 
220
- if not uri.match /http(?:s)?:\/\//
245
+ unless uri.match /http(?:s)?:\/\//
221
246
  uri = scheme + '://' + uri
222
247
  end
223
248
 
224
249
  if req['path']
225
- uri += '/' if !uri.end_with? '/'
250
+ uri += '/' unless uri.end_with? '/'
226
251
  uri += req['path']
227
252
  end
228
253
 
@@ -230,11 +255,11 @@ module Spectre::Curl
230
255
  uri += '?'
231
256
  uri += req['query']
232
257
  .map { |x| x.join '='}
233
- .join '&'
258
+ .join('&')
234
259
  end
235
260
 
236
- cmd.append '"' + uri + '"'
237
- cmd.append '-X', req['method'] unless req['method'] == 'GET' or (req['body'] and req['method'] == 'POST')
261
+ cmd.append('"' + uri + '"')
262
+ cmd.append('-X', req['method']) unless req['method'] == 'GET' or (req['body'] and req['method'] == 'POST')
238
263
 
239
264
  # Call all registered modules
240
265
  @@modules.each do |mod|
@@ -243,43 +268,42 @@ module Spectre::Curl
243
268
 
244
269
  # Add headers to curl command
245
270
  req['headers'].each do |header|
246
- cmd.append '-H', '"' + header.join(':') + '"'
271
+ cmd.append('-H', '"' + header.join(':') + '"')
247
272
  end if req['headers']
248
273
 
249
274
  # Add request body
250
275
  if req['body'] != nil and not req['body'].empty?
251
276
  req_body = try_format_json(req['body']).gsub(/"/, '\\"')
252
- cmd.append '-d', '"' + req_body + '"'
277
+ cmd.append('-d', '"' + req_body + '"')
253
278
  elsif ['POST', 'PUT', 'PATCH'].include? req['method'].upcase
254
- cmd.append '-d', '"\n"'
279
+ cmd.append('-d', '"\n"')
255
280
  end
256
281
 
257
282
  # Add certificate path if one if given
258
283
  if req['cert']
259
284
  raise "Certificate '#{req['cert']}' does not exist" unless File.exists? req['cert']
260
- cmd.append '--cacert', req['cert']
285
+
286
+ cmd.append('--cacert', req['cert'])
261
287
  elsif req['use_ssl'] or uri.start_with? 'https'
262
- cmd.append '-k'
288
+ cmd.append('-k')
263
289
  end
264
290
 
265
- cmd.append '-i'
266
- cmd.append '-v'
291
+ cmd.append('-i')
292
+ cmd.append('-v')
267
293
 
268
- @@request = OpenStruct.new req
294
+ @@request = OpenStruct.new(req)
269
295
 
270
- sys_cmd = cmd.join ' '
296
+ sys_cmd = cmd.join(' ')
271
297
 
272
- @@logger.debug sys_cmd
298
+ @@logger.debug(sys_cmd)
273
299
 
274
300
  req_id = SecureRandom.uuid()[0..5]
275
301
 
276
302
  req_log = "[>] #{req_id} #{req['method']} #{uri}\n"
277
- req['headers'].each do |header|
278
- req_log += "#{header[0].to_s.ljust(30, '.')}: #{header[1].to_s}\n"
279
- end if req['headers']
280
- req_log += req['body'] if req['body'] != nil and not req['body'].empty?
303
+ req_log += header_to_s(req['headers'])
304
+ req_log += try_format_json(req['body'], pretty: true)
281
305
 
282
- @@logger.info req_log
306
+ @@logger.info(req_log)
283
307
 
284
308
  start_time = Time.now
285
309
 
@@ -295,9 +319,9 @@ module Spectre::Curl
295
319
 
296
320
  # debug_log.lines.each { |x| @@logger.debug x unless x.empty? }
297
321
 
298
- raise "Unable to request #{uri}. Please check if this service is reachable." unless output
322
+ raise "Unable to request #{uri}. Please check if this URL is correctly configured and reachable." unless output
299
323
 
300
- @@logger.debug "[<] #{req_id} stdout:\n#{output}"
324
+ @@logger.debug("[<] #{req_id} stdout:\n#{output}")
301
325
 
302
326
  header, body = output.split /\r?\n\r?\n/
303
327
 
@@ -323,7 +347,7 @@ module Spectre::Curl
323
347
  code: match[:code].to_i,
324
348
  message: match[:message],
325
349
  headers: Hash[res_headers],
326
- body: body
350
+ body: body,
327
351
  }
328
352
 
329
353
  # Call all registered modules
@@ -342,7 +366,7 @@ module Spectre::Curl
342
366
 
343
367
  @@logger.info res_log
344
368
 
345
- @@response = SpectreHttpResponse.new res
369
+ @@response = SpectreHttpResponse.new(res)
346
370
 
347
371
  raise "Response did not indicate success: #{@@response.code} #{@@response.message}" if req['ensure_success'] and not @@response.success?
348
372
 
@@ -351,7 +375,12 @@ module Spectre::Curl
351
375
  end
352
376
 
353
377
  Spectre.register do |config|
354
- @@logger = ::Logger.new config['log_file'], progname: 'spectre/curl'
378
+ @@debug = config['debug']
379
+
380
+ @@logger = ::Logger.new(config['log_file'], progname: 'spectre/curl')
381
+ @@logger.level = @@debug ? Logger::DEBUG : Logger::INFO
382
+
383
+ @@secure_keys = config['secure_keys'] || []
355
384
 
356
385
  @@curl_path = config['curl_path'] || 'curl'
357
386
 
@@ -365,4 +394,4 @@ module Spectre::Curl
365
394
  end
366
395
 
367
396
  Spectre.delegate :curl, :curl_response, :curl_request, to: self
368
- end
397
+ end
@@ -1,3 +1,5 @@
1
+ require_relative '../spectre'
2
+
1
3
  module Spectre
2
4
  module Diagnostic
3
5
  module Stopwatch
@@ -21,9 +23,17 @@ module Spectre
21
23
  def duration
22
24
  @@end_time - @@start_time
23
25
  end
26
+
27
+ def started_at
28
+ @@start_time
29
+ end
30
+
31
+ def finished_at
32
+ @@end_time
33
+ end
24
34
  end
25
35
 
26
- Spectre.delegate :start_watch, :stop_watch, :duration, :measure, to: Stopwatch
36
+ Spectre.delegate :start_watch, :stop_watch, :duration, :measure, to: self
27
37
  end
28
38
  end
29
- end
39
+ end
@@ -1,9 +1,13 @@
1
+ require_relative '../spectre'
2
+
1
3
  require 'ostruct'
2
4
 
3
5
  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)
6
+ OpenStruct.new(
7
+ hash.each_with_object({}) do |(key, val), memo|
8
+ memo[key] = val.is_a?(Hash) ? to_recursive_ostruct(val) : val
9
+ end
10
+ )
7
11
  end
8
12
 
9
13
  module Spectre
@@ -21,6 +25,6 @@ module Spectre
21
25
  @@environment.freeze
22
26
  end
23
27
 
24
- Spectre.delegate :env, to: Environment
28
+ Spectre.delegate :env, to: self
25
29
  end
26
- end
30
+ end
@@ -1,7 +1,10 @@
1
+ require_relative '../spectre'
2
+
1
3
  require 'securerandom'
2
4
  require 'json'
3
5
  require 'date'
4
6
  require 'ostruct'
7
+ require 'jsonpath'
5
8
 
6
9
  class ::String
7
10
  def as_json
@@ -12,15 +15,8 @@ class ::String
12
15
  DateTime.parse(self)
13
16
  end
14
17
 
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
- file_content.with(with)
21
- else
22
- file_content
23
- end
18
+ def as_timestamp
19
+ DateTime.parse(self).to_time.to_i
24
20
  end
25
21
 
26
22
  def with mapping
@@ -29,46 +25,109 @@ class ::String
29
25
  new_string = self
30
26
 
31
27
  mapping.each do |key, value|
32
- new_string = new_string.gsub '#{' + key.to_s + '}', value.to_s
28
+ new_string = new_string.gsub('#{' + key.to_s + '}', value.to_s)
33
29
  end
34
30
 
35
31
  new_string
36
32
  end
37
33
 
38
- def exists?
39
- File.exists? self
34
+ def trim size = 50
35
+ if (self.length + 3) > size
36
+ return self[0..size-4] + '...'
37
+ end
38
+
39
+ self
40
40
  end
41
41
 
42
- def remove!
43
- fail "'#{self}' is not a file path, or the file does not exist." if !File.exists? self
42
+ def pick path
43
+ raise ArgumentError.new("`path' must not be nil or empty") if path.nil? or path.empty?
44
44
 
45
- File.delete self
45
+ begin
46
+ JsonPath.on(self, path)
47
+ rescue MultiJson::ParseError
48
+ # do nothing and return nil
49
+ end
46
50
  end
47
51
 
48
- def trim count = 50
49
- if (self.length + 3) > count
50
- return self[0..count-4] + '...'
52
+ # File helpers
53
+
54
+ def content with: nil
55
+ fail "'#{self}' is not a file path, or the file does not exist." unless File.exists? self
56
+
57
+ file_content = File.read(self)
58
+
59
+ if with
60
+ file_content.with(with)
61
+ else
62
+ file_content
51
63
  end
64
+ end
52
65
 
53
- self
66
+ def file_size
67
+ fail "'#{self}' is not a file path, or the file does not exist." unless File.exists? self
68
+
69
+ File.size(self)
54
70
  end
55
- end
56
71
 
72
+ def exists?
73
+ File.exists? self
74
+ end
75
+
76
+ def remove!
77
+ fail "'#{self}' is not a file path, or the file does not exist." unless File.exists? self
78
+
79
+ File.delete self
80
+ end
81
+ end
57
82
 
58
83
  class ::OpenStruct
59
84
  def to_json *args, **kwargs
60
85
  self.to_h.inject({}) { |memo, (k,v)| memo[k] = v.is_a?(OpenStruct) ? v.to_h : v; memo }.to_json(*args, **kwargs)
61
86
  end
62
- end
63
87
 
88
+ def pick path
89
+ raise ArgumentError.new("`path' must not be nil or empty") if path.nil? or path.empty?
90
+
91
+ JsonPath.on(self, path)
92
+ end
93
+
94
+ def default_to! defaults
95
+ defaults.each_key do |key|
96
+ unless self[key] != nil
97
+ self[key] = defaults[key]
98
+ end
99
+ end
100
+ end
101
+
102
+ alias :defaults_to! :default_to!
103
+ end
64
104
 
65
105
  class ::Hash
66
106
  def symbolize_keys
67
107
  self.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
68
108
  end
109
+
110
+ def default_to! defaults
111
+ defaults.each_key do |key|
112
+ unless self[key] != nil
113
+ self[key] = defaults[key]
114
+ end
115
+ end
116
+ end
117
+
118
+ alias :defaults_to! :default_to!
69
119
  end
70
120
 
121
+ class ::Array
122
+ def last_element
123
+ self[-1]
124
+ end
125
+ end
71
126
 
72
127
  def uuid length = 5
73
128
  SecureRandom.uuid().gsub('-', '')[0..length-1]
74
- end
129
+ end
130
+
131
+ def now
132
+ Time.now
133
+ end
@@ -1,7 +1,9 @@
1
+ require_relative '../http'
2
+
1
3
  module Spectre::Http
2
4
  class SpectreHttpRequest
3
5
  def basic_auth username, password
4
- @__req['basic_auth'] = {} if not @__req.key? 'basic_auth'
6
+ @__req['basic_auth'] = {} unless @__req.key? 'basic_auth'
5
7
 
6
8
  @__req['basic_auth']['username'] = username
7
9
  @__req['basic_auth']['password'] = password
@@ -11,8 +13,9 @@ module Spectre::Http
11
13
  end
12
14
 
13
15
  module BasicAuth
14
- def self.on_req http, net_req, req
16
+ def self.on_req _http, net_req, req
15
17
  return unless req.key? 'basic_auth' and req['auth'] == 'basic_auth'
18
+
16
19
  basic_auth_cfg = req['basic_auth']
17
20
  net_req.basic_auth(basic_auth_cfg['username'], basic_auth_cfg['password'])
18
21
  end
@@ -1,98 +1,101 @@
1
- class HttpRequest
2
- def keystone url, username, password, project, domain, cert
3
- @__req['keystone'] = {} if not @__req.key? 'keystone'
4
-
5
- @__req['keystone']['url'] = url
6
- @__req['keystone']['username'] = username
7
- @__req['keystone']['password'] = password
8
- @__req['keystone']['project'] = project
9
- @__req['keystone']['domain'] = domain
10
- @__req['keystone']['cert'] = cert
11
-
12
- @__req.config['auth'] = 'keystone'
1
+ require_relative '../http'
2
+
3
+ module Spectre::Http
4
+ class SpectreHttpRequest
5
+ def keystone url, username, password, project, domain, cert=nil
6
+ @__req['keystone'] = {} unless @__req.key? 'keystone'
7
+
8
+ @__req['keystone']['url'] = url
9
+ @__req['keystone']['username'] = username
10
+ @__req['keystone']['password'] = password
11
+ @__req['keystone']['project'] = project
12
+ @__req['keystone']['domain'] = domain
13
+ @__req['keystone']['cert'] = cert
14
+
15
+ @__req['auth'] = 'keystone'
16
+ end
13
17
  end
14
- end
15
18
 
19
+ module Keystone
20
+ @@cache = {}
16
21
 
17
- module Spectre::Http::Keystone
18
- @@cache = {}
22
+ def self.on_req _http, net_req, req
23
+ return unless req.key? 'keystone' and req['auth'] == 'keystone'
19
24
 
20
- def self.on_req http, net_req, req
21
- return unless req.key? 'keystone' and req['auth'] == 'keystone'
25
+ keystone_cfg = req['keystone']
22
26
 
23
- keystone_cfg = req['keystone']
27
+ if @@cache.key? keystone_cfg
28
+ token = @@cache[keystone_cfg]
29
+ else
30
+ token, _ = authenticate(
31
+ keystone_cfg['url'],
32
+ keystone_cfg['username'],
33
+ keystone_cfg['password'],
34
+ keystone_cfg['project'],
35
+ keystone_cfg['domain'],
36
+ keystone_cfg['cert'],
37
+ )
24
38
 
25
- if @@cache.key? keystone_cfg
26
- token = @@cache[keystone_cfg]
27
- else
28
- token, _ = authenticate(
29
- keystone_cfg['url'],
30
- keystone_cfg['username'],
31
- keystone_cfg['password'],
32
- keystone_cfg['project'],
33
- keystone_cfg['domain'],
34
- keystone_cfg['cert'],
35
- )
39
+ @@cache[keystone_cfg] = token
40
+ end
36
41
 
37
- @@cache[keystone_cfg] = token
42
+ net_req['X-Auth-Token'] = token
38
43
  end
39
44
 
40
- net_req['X-Auth-Token'] = token
41
- end
42
-
43
- private
44
-
45
- def self.authenticate keystone_url, username, password, project, domain, cert
46
- auth_data = {
47
- auth: {
48
- identity: {
49
- methods: ['password'],
50
- password: {
51
- user: {
52
- name: username,
53
- password: password,
54
- domain: {
55
- name: domain,
45
+ private
46
+
47
+ def self.authenticate keystone_url, username, password, project, domain, cert
48
+ auth_data = {
49
+ auth: {
50
+ identity: {
51
+ methods: ['password'],
52
+ password: {
53
+ user: {
54
+ name: username,
55
+ password: password,
56
+ domain: {
57
+ name: domain,
58
+ },
56
59
  },
57
60
  },
58
61
  },
59
- },
60
- scope: {
61
- project: {
62
- name: project,
63
- domain: {
64
- name: domain,
62
+ scope: {
63
+ project: {
64
+ name: project,
65
+ domain: {
66
+ name: domain,
67
+ },
65
68
  },
66
69
  },
67
70
  },
68
- },
69
- }
71
+ }
70
72
 
71
- keystone_url = keystone_url + '/' if !keystone_url.end_with? '/'
73
+ keystone_url = keystone_url + '/' unless keystone_url.end_with? '/'
72
74
 
73
- base_uri = URI(keystone_url)
74
- uri = URI.join(base_uri, 'auth/tokens?nocatalog=true')
75
+ base_uri = URI(keystone_url)
76
+ uri = URI.join(base_uri, 'auth/tokens?nocatalog=true')
75
77
 
76
- http = Net::HTTP.new(base_uri.host, base_uri.port)
78
+ http = Net::HTTP.new(base_uri.host, base_uri.port)
77
79
 
78
- if cert
79
- http.use_ssl = true
80
- http.ca_file = cert
81
- end
80
+ if cert
81
+ http.use_ssl = true
82
+ http.ca_file = cert
83
+ end
82
84
 
83
- req = Net::HTTP::Post.new(uri)
84
- req.body = JSON.pretty_generate(auth_data)
85
- req.content_type = 'application/json'
85
+ req = Net::HTTP::Post.new(uri)
86
+ req.body = JSON.pretty_generate(auth_data)
87
+ req.content_type = 'application/json'
86
88
 
87
- res = http.request(req)
89
+ res = http.request(req)
88
90
 
89
- raise "error while authenticating: #{res.code} #{res.message}\n#{res.body}" if res.code != '201'
91
+ raise "error while authenticating: #{res.code} #{res.message}\n#{res.body}" if res.code != '201'
90
92
 
91
- [
92
- res['X-Subject-Token'],
93
- JSON.parse(res.body),
94
- ]
95
- end
93
+ [
94
+ res['X-Subject-Token'],
95
+ JSON.parse(res.body),
96
+ ]
97
+ end
96
98
 
97
- Spectre::Http.register(self)
99
+ Spectre::Http.register(self)
100
+ end
98
101
  end