spectre-core 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,22 @@
|
|
1
|
+
module Spectre::Http
|
2
|
+
class SpectreHttpRequest
|
3
|
+
def basic_auth username, password
|
4
|
+
@__req['basic_auth'] = {} if not @__req.has_key? 'basic_auth'
|
5
|
+
|
6
|
+
@__req['basic_auth']['username'] = username
|
7
|
+
@__req['basic_auth']['password'] = password
|
8
|
+
|
9
|
+
@__req['auth'] = 'basic_auth'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module BasicAuth
|
14
|
+
def self.on_req http, net_req, req
|
15
|
+
return unless req.has_key? 'basic_auth' and req['auth'] == 'basic_auth'
|
16
|
+
basic_auth_cfg = req['basic_auth']
|
17
|
+
net_req.basic_auth(basic_auth_cfg['username'], basic_auth_cfg['password'])
|
18
|
+
end
|
19
|
+
|
20
|
+
Spectre::Http.register(self)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
class HttpRequest
|
2
|
+
def keystone url, username, password, project, domain, cert
|
3
|
+
@__req['keystone'] = {} if not @__req.has_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'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
module Spectre::Http::Keystone
|
18
|
+
@@cache = {}
|
19
|
+
|
20
|
+
def self.on_req http, net_req, req
|
21
|
+
return unless req.has_key? 'keystone' and req['auth'] == 'keystone'
|
22
|
+
|
23
|
+
keystone_cfg = req['keystone']
|
24
|
+
|
25
|
+
if @@cache.has_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
|
+
)
|
36
|
+
|
37
|
+
@@cache[keystone_cfg] = token
|
38
|
+
end
|
39
|
+
|
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,
|
56
|
+
},
|
57
|
+
},
|
58
|
+
},
|
59
|
+
},
|
60
|
+
scope: {
|
61
|
+
project: {
|
62
|
+
name: project,
|
63
|
+
domain: {
|
64
|
+
name: domain,
|
65
|
+
},
|
66
|
+
},
|
67
|
+
},
|
68
|
+
},
|
69
|
+
}
|
70
|
+
|
71
|
+
keystone_url = keystone_url + '/' if !keystone_url.end_with? '/'
|
72
|
+
|
73
|
+
base_uri = URI(keystone_url)
|
74
|
+
uri = URI.join(base_uri, 'auth/tokens?nocatalog=true')
|
75
|
+
|
76
|
+
http = Net::HTTP.new(base_uri.host, base_uri.port)
|
77
|
+
|
78
|
+
if cert
|
79
|
+
http.use_ssl = true
|
80
|
+
http.ca_file = cert
|
81
|
+
end
|
82
|
+
|
83
|
+
req = Net::HTTP::Post.new(uri)
|
84
|
+
req.body = JSON.pretty_generate(auth_data)
|
85
|
+
req.content_type = 'application/json'
|
86
|
+
|
87
|
+
res = http.request(req)
|
88
|
+
|
89
|
+
raise "error while authenticating: #{res.code} #{res.message}\n#{res.body}" if res.code != '201'
|
90
|
+
|
91
|
+
[
|
92
|
+
res['X-Subject-Token'],
|
93
|
+
JSON.parse(res.body),
|
94
|
+
]
|
95
|
+
end
|
96
|
+
|
97
|
+
Spectre::Http.register(self)
|
98
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Spectre
|
4
|
+
module Logger
|
5
|
+
module Status
|
6
|
+
OK = '[ok]'
|
7
|
+
FAILED = '[failed]'
|
8
|
+
ERROR = '[error]'
|
9
|
+
INFO = '[info]'
|
10
|
+
SKIPPED = '[skipped]'
|
11
|
+
DEBUG = '[debug]'
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
@@debug = false
|
16
|
+
@@logger = []
|
17
|
+
|
18
|
+
def debug!
|
19
|
+
@@debug = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def debug?
|
23
|
+
@@debug
|
24
|
+
end
|
25
|
+
|
26
|
+
def add logger
|
27
|
+
@@logger.append logger
|
28
|
+
end
|
29
|
+
|
30
|
+
def start_subject subject
|
31
|
+
delegate(:start_subject, subject)
|
32
|
+
end
|
33
|
+
|
34
|
+
def end_subject subject
|
35
|
+
delegate(:end_subject, subject)
|
36
|
+
end
|
37
|
+
|
38
|
+
def start_context context
|
39
|
+
delegate(:start_context, context)
|
40
|
+
end
|
41
|
+
|
42
|
+
def end_context context
|
43
|
+
delegate(:end_context, context)
|
44
|
+
end
|
45
|
+
|
46
|
+
def start_spec spec, data=nil
|
47
|
+
delegate(:start_spec, spec, data)
|
48
|
+
end
|
49
|
+
|
50
|
+
def end_spec spec, data=nil
|
51
|
+
delegate(:end_spec, spec, data)
|
52
|
+
end
|
53
|
+
|
54
|
+
def log_subject subject
|
55
|
+
begin
|
56
|
+
start_subject(subject)
|
57
|
+
yield
|
58
|
+
ensure
|
59
|
+
end_subject(subject)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def log_context context
|
64
|
+
begin
|
65
|
+
start_context(context)
|
66
|
+
yield
|
67
|
+
ensure
|
68
|
+
end_context(context)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def log_spec spec, data=nil
|
73
|
+
start_spec(spec, data)
|
74
|
+
yield
|
75
|
+
end_spec(spec, data)
|
76
|
+
end
|
77
|
+
|
78
|
+
def log_separator desc
|
79
|
+
delegate(:log_separator, desc)
|
80
|
+
end
|
81
|
+
|
82
|
+
def start_group desc
|
83
|
+
delegate(:start_group, desc)
|
84
|
+
end
|
85
|
+
|
86
|
+
def end_group desc
|
87
|
+
delegate(:end_group, desc)
|
88
|
+
end
|
89
|
+
|
90
|
+
def log_process desc
|
91
|
+
delegate(:log_process, desc)
|
92
|
+
end
|
93
|
+
|
94
|
+
def log_info message
|
95
|
+
add_log(message)
|
96
|
+
delegate(:log_info, message)
|
97
|
+
end
|
98
|
+
|
99
|
+
def log_debug message
|
100
|
+
return unless @@debug
|
101
|
+
add_log(message)
|
102
|
+
delegate(:log_debug, message)
|
103
|
+
end
|
104
|
+
|
105
|
+
def log_error spec, exception
|
106
|
+
add_log(exception)
|
107
|
+
delegate(:log_error, spec, exception)
|
108
|
+
end
|
109
|
+
|
110
|
+
def log_skipped spec
|
111
|
+
delegate(:log_skipped, spec)
|
112
|
+
end
|
113
|
+
|
114
|
+
def log_status desc, status, annotation=nil
|
115
|
+
delegate(:log_status, desc, status, annotation)
|
116
|
+
end
|
117
|
+
|
118
|
+
def group desc
|
119
|
+
Logger.start_group desc
|
120
|
+
yield
|
121
|
+
Logger.end_group desc
|
122
|
+
end
|
123
|
+
|
124
|
+
alias_method :info, :log_info
|
125
|
+
alias_method :log, :log_info
|
126
|
+
alias_method :debug, :log_debug
|
127
|
+
alias_method :separate, :log_separator
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def delegate method, *args
|
132
|
+
@@logger.each do |logger|
|
133
|
+
logger.send(method, *args) if logger.respond_to? method
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def add_log message
|
138
|
+
Spectre::Runner.current.log.append([DateTime.now, message])
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
Spectre.delegate :log, :info, :debug, :group, :separate, to: self
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'ectoplasm'
|
2
|
+
|
3
|
+
module Spectre
|
4
|
+
module Logger
|
5
|
+
class Console
|
6
|
+
def initialize config
|
7
|
+
raise 'No log format section in config for console logger' unless config.has_key? 'log_format' and config['log_format'].has_key? 'console'
|
8
|
+
|
9
|
+
@config = config['log_format']['console']
|
10
|
+
@indent = @config['indent'] || 2
|
11
|
+
@width = @config['width'] || 80
|
12
|
+
@fmt_end_context = @config['end_context']
|
13
|
+
@fmt_sep = @config['separator']
|
14
|
+
@fmt_start_group = @config['start_group']
|
15
|
+
@fmt_end_group = @config['end_group']
|
16
|
+
|
17
|
+
@process = nil
|
18
|
+
@level = 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def start_subject subject
|
22
|
+
puts subject.desc.blue
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_context context
|
26
|
+
return unless context.__desc
|
27
|
+
puts (' ' * indent) + context.__desc.magenta
|
28
|
+
@level += 1
|
29
|
+
end
|
30
|
+
|
31
|
+
def end_context context
|
32
|
+
return unless context.__desc
|
33
|
+
@level -= 1
|
34
|
+
puts (' ' * indent) + @fmt_end_context.gsub('<desc>', context.__desc).magenta if @fmt_end_context
|
35
|
+
end
|
36
|
+
|
37
|
+
def start_spec spec, data=nil
|
38
|
+
text = spec.desc
|
39
|
+
text += " with #{data}" if data
|
40
|
+
puts (' ' * indent) + text.cyan
|
41
|
+
|
42
|
+
@level += 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def end_spec spec, data
|
46
|
+
@level -= 1
|
47
|
+
end
|
48
|
+
|
49
|
+
def log_separator desc
|
50
|
+
if desc
|
51
|
+
desc = @fmt_sep.gsub('<indent>', ' ' * indent).gsub('<desc>', desc) if @fmt_sep
|
52
|
+
puts desc.blue
|
53
|
+
else
|
54
|
+
puts
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def start_group desc
|
59
|
+
desc = @fmt_start_group.gsub('<desc>', desc) if @fmt_start_group
|
60
|
+
puts (' ' * indent) + desc.blue
|
61
|
+
@level += 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def end_group desc
|
65
|
+
if desc and @fmt_start_group
|
66
|
+
desc = @fmt_start_group.gsub('<desc>', desc) if @fmt_start_group
|
67
|
+
puts (' ' * indent) + desc.blue
|
68
|
+
end
|
69
|
+
|
70
|
+
@level -= 1
|
71
|
+
end
|
72
|
+
|
73
|
+
def log_process desc
|
74
|
+
print_line(desc)
|
75
|
+
@process = desc
|
76
|
+
@level += 1
|
77
|
+
end
|
78
|
+
|
79
|
+
def log_status desc, status, annotation=nil
|
80
|
+
status = status.green if status == Status::OK
|
81
|
+
status = status.blue if status == Status::INFO
|
82
|
+
status = status.grey if status == Status::DEBUG
|
83
|
+
status = status.red if status == Status::FAILED
|
84
|
+
status = status.red if status == Status::ERROR
|
85
|
+
status = status.grey if status == Status::SKIPPED
|
86
|
+
|
87
|
+
txt = status
|
88
|
+
txt += ' ' + annotation if annotation
|
89
|
+
|
90
|
+
@level -= 1
|
91
|
+
|
92
|
+
if @process
|
93
|
+
puts txt
|
94
|
+
else
|
95
|
+
print_line('', status)
|
96
|
+
end
|
97
|
+
|
98
|
+
@process = nil
|
99
|
+
end
|
100
|
+
|
101
|
+
def log_info message
|
102
|
+
print_line(message, Status::INFO.blue)
|
103
|
+
end
|
104
|
+
|
105
|
+
def log_debug message
|
106
|
+
print_line(message, Status::DEBUG.grey)
|
107
|
+
end
|
108
|
+
|
109
|
+
def log_error spec, exception
|
110
|
+
txt = (Status::ERROR + ' - ' + exception.class.name).red
|
111
|
+
print_line('', txt)
|
112
|
+
end
|
113
|
+
|
114
|
+
def log_skipped spec
|
115
|
+
print_line('', Status::SKIPPED.grey)
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def indent
|
121
|
+
(@level+1) * @indent
|
122
|
+
end
|
123
|
+
|
124
|
+
def print_line text='', status=nil
|
125
|
+
puts if @process
|
126
|
+
|
127
|
+
ind = indent
|
128
|
+
line = (' ' * indent) + text
|
129
|
+
remaining = @width - text.length - ind
|
130
|
+
line += '.' * (@width - text.length - ind) if remaining > 0
|
131
|
+
|
132
|
+
print line
|
133
|
+
|
134
|
+
if status
|
135
|
+
puts status
|
136
|
+
@process = nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Spectre
|
2
|
+
module Logger
|
3
|
+
class File
|
4
|
+
def initialize config
|
5
|
+
raise 'No log format section in config for console logger' unless config.has_key? 'log_format' and config['log_format'].has_key? 'file'
|
6
|
+
|
7
|
+
@config = config['log_format']['file']
|
8
|
+
@fmt_start_group = @config['start_group']
|
9
|
+
@fmt_end_group = @config['end_group']
|
10
|
+
@fmt_sep = @config['separator']
|
11
|
+
|
12
|
+
@file_log = ::Logger.new config['log_file'], progname: 'spectre'
|
13
|
+
@file_log.level = config['debug'] ? 'DEBUG' : 'INFO'
|
14
|
+
end
|
15
|
+
|
16
|
+
def start_subject subject
|
17
|
+
@file_log.debug "start running subject '#{subject.desc}'"
|
18
|
+
end
|
19
|
+
|
20
|
+
def end_subject subject
|
21
|
+
@file_log.debug "subject '#{subject.desc}' finished"
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_context context
|
25
|
+
if context and context.__desc
|
26
|
+
@file_log.debug "start running context '#{context.__desc}'"
|
27
|
+
else
|
28
|
+
@file_log.debug "start running main context of #{context.__subject.desc}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def end_context context
|
33
|
+
if context and context.__desc
|
34
|
+
@file_log.debug "context '#{context.__desc}' finished"
|
35
|
+
else
|
36
|
+
@file_log.debug "main context finished of #{context.__subject.desc}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def start_spec spec, data=nil
|
41
|
+
log_msg = "start running spec [#{spec.name}] '#{spec.desc}'"
|
42
|
+
log_msg += " with data #{data}" if data
|
43
|
+
@file_log.debug log_msg
|
44
|
+
end
|
45
|
+
|
46
|
+
def end_spec spec, data=nil
|
47
|
+
log_msg = "running spec [#{spec.name}] '#{spec.desc}'"
|
48
|
+
log_msg += " with data #{data}" if data
|
49
|
+
log_msg += " finished"
|
50
|
+
@file_log.debug log_msg
|
51
|
+
end
|
52
|
+
|
53
|
+
def log_separator desc
|
54
|
+
desc = @fmt_sep.gsub('<desc>', desc) if @fmt_sep
|
55
|
+
@file_log.info desc
|
56
|
+
end
|
57
|
+
|
58
|
+
def start_group desc
|
59
|
+
desc = @fmt_start_group.gsub('<desc>', desc) if @fmt_start_group
|
60
|
+
@file_log.info desc
|
61
|
+
end
|
62
|
+
|
63
|
+
def end_group desc
|
64
|
+
desc = @fmt_end_group.gsub('<desc>', desc) if @fmt_end_group
|
65
|
+
@file_log.info desc
|
66
|
+
end
|
67
|
+
|
68
|
+
def log_process desc
|
69
|
+
@file_log.debug desc
|
70
|
+
end
|
71
|
+
|
72
|
+
def log_info message
|
73
|
+
@file_log.info "#{Status::INFO} #{message}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def log_debug message
|
77
|
+
@file_log.debug "#{Status::DEBUG} #{message}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def log_error spec, exception
|
81
|
+
file, line = exception.backtrace[0].match(/(.*\.rb):(\d+)/).captures
|
82
|
+
@file_log.error "An unexpected error occured at '#{file}:#{line}' while running spec '#{spec.name}': [#{exception.class}] #{exception.message}\n#{exception.backtrace.join "\n"}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def log_skipped spec
|
86
|
+
@file_log.warn "spec '#{spec.desc}' canceled by user"
|
87
|
+
end
|
88
|
+
|
89
|
+
def log_status desc, status, annotation=nil
|
90
|
+
msg = "expected #{desc}...#{status.upcase}"
|
91
|
+
msg += " - #{annotation}" if annotation
|
92
|
+
@file_log.debug msg
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|