ansible_spec_plus 1.0.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/.gitignore +3 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +73 -0
- data/Guardfile +14 -0
- data/Jenkinsfile +71 -0
- data/README.md +291 -0
- data/ansible_spec_plus.gemspec +26 -0
- data/bin/asp +72 -0
- data/bin/asp-init +78 -0
- data/lib/ansible_spec_plus.rb +653 -0
- data/lib/ansiblespec_helper.rb +34 -0
- data/lib/helpers/buffered_logger.rb +110 -0
- data/lib/helpers/color_formatter.rb +18 -0
- data/lib/helpers/colorize.rb +52 -0
- data/lib/helpers/log.rb +80 -0
- data/lib/helpers/ring_buffer.rb +65 -0
- data/lib/src/.ansiblespec +5 -0
- data/lib/src/.rspec +3 -0
- data/lib/src/spec/spec_helper.rb +92 -0
- data/spec/ansible_spec_plus_spec.rb +553 -0
- data/spec_helper.rb +16 -0
- metadata +145 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# lib = File.expand_path('../lib', __FILE__)
|
3
|
+
# $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
# require 'ansible_spec/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "ansible_spec_plus"
|
8
|
+
gem.date = Time.now.strftime("%Y-%m-%d")
|
9
|
+
gem.version = "1.0.0"
|
10
|
+
gem.authors = ["Meik Minks"]
|
11
|
+
gem.email = ["mminks@inoxio.de"]
|
12
|
+
gem.description = %q{Ansible Config Parser for Serverspec to test roles, hosts and playbooks. Providing test coverage.}
|
13
|
+
gem.summary = %q{Ansible Config Parser for Serverspec to test roles, hosts and playbooks. Providing test coverage.}
|
14
|
+
gem.homepage = "https://github.com/consort-it/ansible_spec_plus"
|
15
|
+
gem.license = "MIT"
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_development_dependency "bundler", "~> 1.3"
|
21
|
+
gem.add_development_dependency 'rake', '~> 0'
|
22
|
+
gem.add_development_dependency 'diff-lcs', '~> 0'
|
23
|
+
gem.add_development_dependency 'simplecov', '~> 0'
|
24
|
+
|
25
|
+
gem.add_runtime_dependency 'ansible_spec', '~> 0.2', '>= 0.2.19'
|
26
|
+
end
|
data/bin/asp
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
# TODO comment in again when ready
|
5
|
+
# require 'ansible_spec_plus'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
require_relative '../lib/ansible_spec_plus'
|
9
|
+
|
10
|
+
ARGV.push('-h') if ARGV.empty?
|
11
|
+
|
12
|
+
KNOWN_RESOURCES = [
|
13
|
+
'file',
|
14
|
+
'template',
|
15
|
+
'docker_container',
|
16
|
+
'docker_image',
|
17
|
+
'service',
|
18
|
+
'apt',
|
19
|
+
'pip',
|
20
|
+
'gem'
|
21
|
+
]
|
22
|
+
|
23
|
+
options = {}
|
24
|
+
|
25
|
+
opt_parser = OptionParser.new do |opt|
|
26
|
+
opt.banner = "Ansible Spec Plus is an addon to 'ansible_spec' which enables you to run
|
27
|
+
specs for Ansible roles, hosts and/or playbooks separately. Furthermore
|
28
|
+
it provides you with a simple resource coverage summary.
|
29
|
+
|
30
|
+
Usage: asp COMMAND [OPTIONS]"
|
31
|
+
opt.separator ""
|
32
|
+
opt.separator "Commands:"
|
33
|
+
opt.separator " list # list all available specs"
|
34
|
+
opt.separator " [rolespec|hostspec|playbookspec] list # list all available role/host/playbook specs"
|
35
|
+
opt.separator ""
|
36
|
+
opt.separator "Options:"
|
37
|
+
|
38
|
+
opt.on("-l", "--local", "running specs on local machine") do
|
39
|
+
ENV['BACKEND'] = 'exec'
|
40
|
+
end
|
41
|
+
|
42
|
+
opt.on("-h","--help","help") do
|
43
|
+
puts opt_parser
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
opt_parser.parse!
|
48
|
+
|
49
|
+
if ARGV[0] == 'list'
|
50
|
+
options[:list] = true
|
51
|
+
elsif ARGV[0] == 'rolespec'
|
52
|
+
if ARGV[1] == 'list'
|
53
|
+
options[:list_rolespec] = true
|
54
|
+
else
|
55
|
+
options[:run_rolespec] = ARGV[1]
|
56
|
+
end
|
57
|
+
elsif ARGV[0] == 'hostspec'
|
58
|
+
if ARGV[1] == 'list'
|
59
|
+
options[:list_hostspec] = true
|
60
|
+
else
|
61
|
+
options[:run_hostspec] = ARGV[1]
|
62
|
+
end
|
63
|
+
elsif ARGV[0] == 'playbookspec'
|
64
|
+
if ARGV[1] == 'list'
|
65
|
+
options[:list_playbookspec] = true
|
66
|
+
else
|
67
|
+
options[:run_playbookspec] = ARGV[1]
|
68
|
+
end
|
69
|
+
else
|
70
|
+
end
|
71
|
+
|
72
|
+
AnsibleSpecPlus.new.run(options)
|
data/bin/asp-init
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
def safe_create_spec_helper
|
7
|
+
content = File.open(File.dirname(__FILE__) + "/../lib/src/spec/spec_helper.rb").read
|
8
|
+
safe_mkdir("spec")
|
9
|
+
safe_touch("spec/spec_helper.rb")
|
10
|
+
File.open("spec/spec_helper.rb", 'w') do |f|
|
11
|
+
f.puts content
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def safe_create_ansiblespec
|
17
|
+
content = File.open(File.dirname(__FILE__) + "/../lib/src/.ansiblespec").read
|
18
|
+
safe_touch(".ansiblespec")
|
19
|
+
File.open(".ansiblespec", 'w') do |f|
|
20
|
+
f.puts content
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def safe_create_rspec
|
25
|
+
content = File.open(File.dirname(__FILE__) + "/../lib/src/.rspec").read
|
26
|
+
safe_touch(".rspec")
|
27
|
+
File.open(".rspec", 'w') do |f|
|
28
|
+
f.puts content
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def safe_mkdir(dir)
|
33
|
+
unless FileTest.exist?("#{dir}")
|
34
|
+
FileUtils.mkdir_p("#{dir}")
|
35
|
+
TermColor.green
|
36
|
+
puts "\t\tcreate\t#{dir}"
|
37
|
+
TermColor.reset
|
38
|
+
else
|
39
|
+
TermColor.red
|
40
|
+
puts "\t\texists\t#{dir}"
|
41
|
+
TermColor.reset
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def safe_touch(file)
|
46
|
+
unless File.exists? "#{file}"
|
47
|
+
File.open("#{file}", 'w') do |f|
|
48
|
+
#f.puts content
|
49
|
+
end
|
50
|
+
TermColor.green
|
51
|
+
puts "\t\tcreate\t#{file}"
|
52
|
+
TermColor.reset
|
53
|
+
else
|
54
|
+
TermColor.red
|
55
|
+
puts "\t\texists\t#{file}"
|
56
|
+
TermColor.reset
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class TermColor
|
61
|
+
class << self
|
62
|
+
def reset ; c 0 ; end
|
63
|
+
|
64
|
+
def red ; c 31; end
|
65
|
+
|
66
|
+
def green ; c 32; end
|
67
|
+
|
68
|
+
def c(num)
|
69
|
+
print "\e[#{num.to_s}m"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
safe_create_spec_helper
|
75
|
+
safe_create_ansiblespec
|
76
|
+
safe_create_rspec
|
77
|
+
|
78
|
+
exit 0
|
@@ -0,0 +1,653 @@
|
|
1
|
+
require_relative '../lib/helpers/log'
|
2
|
+
require_relative '../lib/ansiblespec_helper'
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
require 'yaml'
|
7
|
+
require 'ansible_spec'
|
8
|
+
require 'pp'
|
9
|
+
require 'json'
|
10
|
+
|
11
|
+
class AnsibleSpecPlus
|
12
|
+
include Helpers::Log
|
13
|
+
|
14
|
+
if ENV['BASE_DIR']
|
15
|
+
BASE_DIR = ENV['BASE_DIR']
|
16
|
+
else
|
17
|
+
BASE_DIR = './'
|
18
|
+
end
|
19
|
+
|
20
|
+
##################
|
21
|
+
# COMMON METHODS #
|
22
|
+
##################
|
23
|
+
|
24
|
+
def run(options)
|
25
|
+
list_all_specs if options[:list] == true
|
26
|
+
|
27
|
+
list_role_specs if options[:list_rolespec] == true
|
28
|
+
list_host_specs if options[:list_hostspec] == true
|
29
|
+
list_playbook_specs if options[:list_playbookspec] == true
|
30
|
+
|
31
|
+
run_role_spec(options[:run_rolespec]) if options[:run_rolespec]
|
32
|
+
run_host_spec(options[:run_hostspec]) if options[:run_hostspec]
|
33
|
+
run_playbook_spec(options[:run_playbookspec]) if options[:run_playbookspec]
|
34
|
+
end
|
35
|
+
|
36
|
+
def list_all_specs
|
37
|
+
list_role_specs
|
38
|
+
list_host_specs
|
39
|
+
list_playbook_specs
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_for_specs_in_file(file)
|
43
|
+
File.readlines(file).grep(/describe\s{1}/).any?
|
44
|
+
end
|
45
|
+
|
46
|
+
def analyze_resources(resources)
|
47
|
+
analyzed_resources = []
|
48
|
+
|
49
|
+
resources.each do |resource|
|
50
|
+
resource_type = nil
|
51
|
+
|
52
|
+
resource.keys.each do |key|
|
53
|
+
resource_type = key if KNOWN_RESOURCES.include?(key)
|
54
|
+
end
|
55
|
+
|
56
|
+
if resource_type.nil?
|
57
|
+
log.warn "Unknown resource: #{resource}"
|
58
|
+
next
|
59
|
+
end
|
60
|
+
|
61
|
+
skip = false
|
62
|
+
|
63
|
+
if resource.values[1].respond_to?(:each)
|
64
|
+
resource.values[1].each do |key, value|
|
65
|
+
skip = true if value =~ /\{\{/
|
66
|
+
end
|
67
|
+
else
|
68
|
+
skip = true if resource.values[1] =~ /\{\{/
|
69
|
+
end
|
70
|
+
|
71
|
+
if skip == true
|
72
|
+
log.warn "Don't know how to deal with '{{ }}' syntax: #{resource}"
|
73
|
+
next
|
74
|
+
end
|
75
|
+
|
76
|
+
if resource_type == 'file' || resource_type == 'template'
|
77
|
+
resource_type = 'File'
|
78
|
+
if resource.values[1].respond_to?(:each)
|
79
|
+
target = resource.values[1].select { |i| i =~ /path|dest/ }
|
80
|
+
resource_name = target.values[0]
|
81
|
+
else
|
82
|
+
target = resource.values[1].split(" ").select { |i| i =~ /path|dest/ }
|
83
|
+
resource_name = target.first.gsub(/.*=/,'')
|
84
|
+
end
|
85
|
+
elsif resource_type == 'docker_container'
|
86
|
+
resource_type = 'Docker container'
|
87
|
+
resource_name = resource.values[1]['name']
|
88
|
+
elsif resource_type == 'docker_image'
|
89
|
+
resource_type = 'Docker image'
|
90
|
+
resource_name = resource.values[1]['name']
|
91
|
+
elsif resource_type == 'service'
|
92
|
+
resource_type = 'Service'
|
93
|
+
if resource.values[1].respond_to?(:each)
|
94
|
+
resource_name = resource.values[1]['name']
|
95
|
+
else
|
96
|
+
resource_name = resource.values[1].split(" ")[0].gsub(/.*=/,'')
|
97
|
+
end
|
98
|
+
elsif resource_type =~ /apt|pip|gem/
|
99
|
+
if (resource_type == 'apt') && (! resource['apt'].include?('name'))
|
100
|
+
log.warn "Unknown resource: #{resource}"
|
101
|
+
next
|
102
|
+
end
|
103
|
+
|
104
|
+
resource_type = 'Package'
|
105
|
+
resource.each do |item|
|
106
|
+
next unless item[0] =~ /apt|pip|gem/
|
107
|
+
|
108
|
+
resource_name = item[1]['name']
|
109
|
+
end
|
110
|
+
else
|
111
|
+
next
|
112
|
+
end
|
113
|
+
|
114
|
+
analyzed_resources << "#{resource_type} \"#{resource_name}\""
|
115
|
+
end
|
116
|
+
|
117
|
+
return analyzed_resources
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_hosts_from_vai_host_file
|
121
|
+
inventory_hosts = []
|
122
|
+
hosts_with_vagrant_vars = {}
|
123
|
+
|
124
|
+
Dir.chdir(BASE_DIR) do
|
125
|
+
begin
|
126
|
+
File.open('hosts', 'r') do |file|
|
127
|
+
file.each_line do |line|
|
128
|
+
pattern = /^#.*|^$\n|^\[.*|^\r\n?/
|
129
|
+
next if line =~ pattern
|
130
|
+
inventory_hosts << line
|
131
|
+
end
|
132
|
+
end
|
133
|
+
rescue => e
|
134
|
+
log.error "hosts file doesn't exist: #{e}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
inventory_hosts.each do |line|
|
139
|
+
hostname = line.split(' ').first
|
140
|
+
|
141
|
+
parts = line.match(/\s.*/).to_s.split(' ')
|
142
|
+
|
143
|
+
vars = {}
|
144
|
+
vars['name'] = hostname
|
145
|
+
parts.each do |part|
|
146
|
+
key = part.gsub(/=.*/,'')
|
147
|
+
value = part.gsub(/.*=/,'').gsub(/'|"/,'')
|
148
|
+
vars["#{key}"] = value
|
149
|
+
end
|
150
|
+
|
151
|
+
hosts_with_vagrant_vars[hostname] = [vars]
|
152
|
+
end
|
153
|
+
|
154
|
+
return hosts_with_vagrant_vars
|
155
|
+
end
|
156
|
+
|
157
|
+
def print_rounded_role_coverage(all_resources, differences)
|
158
|
+
if all_resources.count == 0
|
159
|
+
return "0%"
|
160
|
+
else
|
161
|
+
"#{((all_resources - differences).count.to_f / all_resources.count.to_f * 100.0).round}%"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def calculate_coverage(type, name)
|
166
|
+
Dir.chdir(BASE_DIR) do
|
167
|
+
json_report = File.read('report.json')
|
168
|
+
parsed_report = JSON.parse(json_report)
|
169
|
+
|
170
|
+
case type
|
171
|
+
when 'role'
|
172
|
+
all_known_resources = analyze_resources(load_role_resources(name))
|
173
|
+
when 'host'
|
174
|
+
all_known_resources = analyze_resources(load_host_resources(name))
|
175
|
+
when 'playbook'
|
176
|
+
all_known_resources = analyze_resources(load_playbook_resources(name))
|
177
|
+
else
|
178
|
+
raise "Unknow type '#{type}'. Should be either role, host or playbook."
|
179
|
+
end
|
180
|
+
|
181
|
+
tested_resources = []
|
182
|
+
|
183
|
+
parsed_report['examples'].each do |example|
|
184
|
+
tested_resources << example['full_description'].gsub(/"\s{1}.*/,'"')
|
185
|
+
end
|
186
|
+
|
187
|
+
tested_resources = tested_resources.uniq
|
188
|
+
|
189
|
+
differences = []
|
190
|
+
|
191
|
+
all_known_resources.each do |resource|
|
192
|
+
differences << resource if ! tested_resources.include?(resource)
|
193
|
+
end
|
194
|
+
|
195
|
+
puts "Total resources: #{all_known_resources.count}"
|
196
|
+
puts "Touched resources: #{(all_known_resources - differences).count}"
|
197
|
+
puts "Resource coverage: #{print_rounded_role_coverage(all_known_resources, differences)}"
|
198
|
+
|
199
|
+
if differences.count > 0
|
200
|
+
puts "\nUncovered resources:"
|
201
|
+
differences.each do |item|
|
202
|
+
puts "- #{item}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
################
|
209
|
+
# ROLE METHODS #
|
210
|
+
################
|
211
|
+
|
212
|
+
def list_role_specs
|
213
|
+
get_roles_with_specs.each do |role|
|
214
|
+
command = "asp rolespec #{role}"
|
215
|
+
description = "# run role specs for #{role}"
|
216
|
+
|
217
|
+
puts "#{command} #{description.rjust(40)}"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def run_role_spec(role)
|
222
|
+
if check_role_directory_available(role) && check_role_specs_available(role)
|
223
|
+
create_role_rake_task(role)
|
224
|
+
|
225
|
+
Dir.chdir(BASE_DIR) do
|
226
|
+
Rake.application["#{role}"].invoke()
|
227
|
+
end
|
228
|
+
|
229
|
+
calculate_coverage('role', role)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def get_all_roles
|
234
|
+
all_roles = []
|
235
|
+
|
236
|
+
Dir.chdir(BASE_DIR) do
|
237
|
+
Dir.glob("roles/*").each do |role|
|
238
|
+
all_roles << File.basename(role)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
return all_roles
|
243
|
+
end
|
244
|
+
|
245
|
+
def get_roles_with_specs
|
246
|
+
roles_with_specs = []
|
247
|
+
|
248
|
+
Dir.chdir(BASE_DIR) do
|
249
|
+
get_all_roles.each do |role|
|
250
|
+
successes = 0
|
251
|
+
|
252
|
+
Dir.glob("roles/#{role}/spec/*_spec.rb").each do |file|
|
253
|
+
successes =+ 1 if File.size(file) > 1
|
254
|
+
end
|
255
|
+
|
256
|
+
roles_with_specs << File.basename(role) if successes > 0
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
return roles_with_specs
|
261
|
+
end
|
262
|
+
|
263
|
+
def get_roles_without_specs
|
264
|
+
get_all_roles - get_roles_with_specs
|
265
|
+
end
|
266
|
+
|
267
|
+
def check_role_directory_available(role)
|
268
|
+
Dir.chdir(BASE_DIR) do
|
269
|
+
if ! Dir.exists?("roles/#{role}")
|
270
|
+
log.error "Directory 'roles/#{role}' does not exist. That's strange, isn't it?"
|
271
|
+
return false
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
return true
|
276
|
+
end
|
277
|
+
|
278
|
+
def check_role_specs_available(role)
|
279
|
+
Dir.chdir(BASE_DIR) do
|
280
|
+
successes = 0
|
281
|
+
|
282
|
+
Dir.glob("roles/#{role}/spec/*_spec.rb").each do |file|
|
283
|
+
successes =+ 1 if File.size(file) > 1
|
284
|
+
end
|
285
|
+
|
286
|
+
if successes == 0
|
287
|
+
# log.error "'#{role}' does not have specs but you requested me to run specs. Huu?"
|
288
|
+
return false
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
return true
|
293
|
+
end
|
294
|
+
|
295
|
+
def get_hosts_where_role_is_used(role)
|
296
|
+
role_used_in_hosts = []
|
297
|
+
|
298
|
+
Dir.chdir(BASE_DIR) do
|
299
|
+
YAML.load_file("site.yml").each do |playbook|
|
300
|
+
YAML.load_file(playbook['include'].to_s).each do |site|
|
301
|
+
site['roles'].each do |playbook_role|
|
302
|
+
if playbook_role.respond_to?(:each)
|
303
|
+
role_used_in_hosts << site['name'].to_s if playbook_role['role'].include?(role)
|
304
|
+
else
|
305
|
+
role_used_in_hosts << site['name'].to_s if playbook_role.include?(role)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
return role_used_in_hosts
|
313
|
+
end
|
314
|
+
|
315
|
+
def load_role_resources(name)
|
316
|
+
resources = []
|
317
|
+
|
318
|
+
Dir.chdir(BASE_DIR) do
|
319
|
+
Dir.glob("roles/#{name}/tasks/*.yml").each do |file|
|
320
|
+
AnsibleSpec.load_playbook(file).each do |resource|
|
321
|
+
resources << resource
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
return resources
|
327
|
+
end
|
328
|
+
|
329
|
+
def create_role_rake_task(role)
|
330
|
+
Dir.chdir(BASE_DIR) do
|
331
|
+
properties = AnsibleSpecHelper.get_properties
|
332
|
+
cfg = AnsibleSpec::AnsibleCfg.new
|
333
|
+
get_hosts_where_role_is_used = get_hosts_where_role_is_used(role)
|
334
|
+
|
335
|
+
properties.each do |property|
|
336
|
+
next unless property['name'] == get_hosts_where_role_is_used.first
|
337
|
+
|
338
|
+
if property['hosts'].empty?
|
339
|
+
if ! get_hosts_from_vai_host_file.keys.include?(get_hosts_where_role_is_used.first)
|
340
|
+
raise "Uuups. I cannot find '#{get_hosts_where_role_is_used.first}' in your hosts file. 'vagrant up #{get_hosts_where_role_is_used.first}' may help."
|
341
|
+
end
|
342
|
+
|
343
|
+
get_hosts_from_vai_host_file.each do |host, values|
|
344
|
+
values.each do |property|
|
345
|
+
next unless property['name'] == get_hosts_where_role_is_used.first
|
346
|
+
|
347
|
+
RSpec::Core::RakeTask.new(role.to_sym) do |t|
|
348
|
+
log.info "Run role tests for #{role} on #{get_hosts_where_role_is_used.first} (#{property["ansible_ssh_host"]}:#{property["ansible_ssh_port"]})"
|
349
|
+
|
350
|
+
ENV['TARGET_HOST'] = property["ansible_ssh_host"]
|
351
|
+
ENV['TARGET_PORT'] = property["ansible_ssh_port"].to_s
|
352
|
+
ENV['TARGET_PRIVATE_KEY'] = property["ansible_ssh_private_key_file"]
|
353
|
+
ENV['TARGET_USER'] = property["ansible_ssh_user"]
|
354
|
+
|
355
|
+
t.pattern = '{' + cfg.roles_path.join(',') + '}/{' + role + '}/spec/*_spec.rb'
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
else
|
360
|
+
property['hosts'].each do |host|
|
361
|
+
RSpec::Core::RakeTask.new(role.to_sym) do |t|
|
362
|
+
log.info "Run role tests for #{role} on #{get_hosts_where_role_is_used.first} (#{host['uri']})"
|
363
|
+
|
364
|
+
ENV['TARGET_HOST'] = host['uri']
|
365
|
+
|
366
|
+
t.pattern = '{' + cfg.roles_path.join(',') + '}/{' + role + '}/spec/*_spec.rb'
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
################
|
375
|
+
# HOST METHODS #
|
376
|
+
################
|
377
|
+
|
378
|
+
def list_host_specs
|
379
|
+
get_hosts_with_specs.each do |host|
|
380
|
+
command = "asp hostspec #{host}"
|
381
|
+
description = "# run host specs for #{host}"
|
382
|
+
|
383
|
+
puts "#{command} #{description.rjust(40)}"
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def create_host_rake_task(host)
|
388
|
+
Dir.chdir(BASE_DIR) do
|
389
|
+
properties = AnsibleSpecHelper.get_properties
|
390
|
+
cfg = AnsibleSpec::AnsibleCfg.new
|
391
|
+
|
392
|
+
properties.each do |property|
|
393
|
+
next unless property['name'] == host
|
394
|
+
|
395
|
+
if property['hosts'].empty?
|
396
|
+
get_hosts_from_vai_host_file.each do |host, values|
|
397
|
+
values.each do |property|
|
398
|
+
RSpec::Core::RakeTask.new(host.to_sym) do |t|
|
399
|
+
log.info "Run host tests for #{host}"
|
400
|
+
|
401
|
+
ENV['TARGET_HOST'] = property["ansible_ssh_host"]
|
402
|
+
ENV['TARGET_PORT'] = property["ansible_ssh_port"].to_s
|
403
|
+
ENV['TARGET_PRIVATE_KEY'] = property["ansible_ssh_private_key_file"]
|
404
|
+
ENV['TARGET_USER'] = property["ansible_ssh_user"]
|
405
|
+
|
406
|
+
t.pattern = "spec/#{host}/*_spec.rb"
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
else
|
411
|
+
property['hosts'].each do |host|
|
412
|
+
RSpec::Core::RakeTask.new(property["name"].to_sym) do |t|
|
413
|
+
log.info "Run host tests for #{property["name"]}"
|
414
|
+
|
415
|
+
ENV['TARGET_HOST'] = host['uri']
|
416
|
+
|
417
|
+
t.pattern = "spec/#{property["name"]}/*_spec.rb"
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
def run_host_spec(host)
|
426
|
+
create_host_rake_task(host)
|
427
|
+
|
428
|
+
Dir.chdir(BASE_DIR) do
|
429
|
+
Rake.application.invoke_task("#{host}")
|
430
|
+
end
|
431
|
+
|
432
|
+
calculate_coverage('host', host)
|
433
|
+
end
|
434
|
+
|
435
|
+
def load_host_resources(name)
|
436
|
+
resources = []
|
437
|
+
|
438
|
+
Dir.chdir(BASE_DIR) do
|
439
|
+
YAML.load_file('./site.yml').each do |site|
|
440
|
+
next unless site.values[0] =~ /#{name}\.(yml|yaml)/
|
441
|
+
|
442
|
+
playbook_path = site.values[0]
|
443
|
+
|
444
|
+
AnsibleSpec.load_playbook(playbook_path).each do |playbook|
|
445
|
+
next if playbook['tasks'].nil?
|
446
|
+
|
447
|
+
playbook['tasks'].map { |task| resources << task }
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
return resources.flatten
|
453
|
+
end
|
454
|
+
|
455
|
+
def get_hosts_with_specs
|
456
|
+
hosts_with_specs = []
|
457
|
+
|
458
|
+
get_vagrant_or_regular_ansible_hosts.each do |host|
|
459
|
+
hosts_with_specs << host if check_for_host_specs(host)
|
460
|
+
end
|
461
|
+
|
462
|
+
return hosts_with_specs.uniq
|
463
|
+
end
|
464
|
+
|
465
|
+
def get_vagrant_or_regular_ansible_hosts
|
466
|
+
hosts = []
|
467
|
+
|
468
|
+
Dir.chdir(BASE_DIR) do
|
469
|
+
properties = AnsibleSpecHelper.get_properties
|
470
|
+
cfg = AnsibleSpec::AnsibleCfg.new
|
471
|
+
|
472
|
+
properties.each do |property|
|
473
|
+
if property['hosts'].empty?
|
474
|
+
get_hosts_from_vai_host_file.each do |host, values|
|
475
|
+
values.each do |property|
|
476
|
+
hosts << property['name'] if check_for_existing_playbook(property['name'])
|
477
|
+
end
|
478
|
+
end
|
479
|
+
else
|
480
|
+
property['hosts'].each do |host|
|
481
|
+
hosts << property['name'] if check_for_existing_playbook(property['name'])
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
return hosts
|
488
|
+
end
|
489
|
+
|
490
|
+
def check_for_host_specs(host)
|
491
|
+
Dir.chdir(BASE_DIR) do
|
492
|
+
if File.directory?("./spec/#{host}")
|
493
|
+
Dir.glob("./spec/#{host}/*_spec.rb").each do |file|
|
494
|
+
if check_for_specs_in_file(file)
|
495
|
+
return true
|
496
|
+
else
|
497
|
+
return false
|
498
|
+
end
|
499
|
+
end
|
500
|
+
else
|
501
|
+
return false
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def get_roles_of_host(host)
|
507
|
+
roles_with_specs = []
|
508
|
+
|
509
|
+
Dir.chdir(BASE_DIR) do
|
510
|
+
playbook_path = ''
|
511
|
+
|
512
|
+
YAML.load_file('./site.yml').each do |site|
|
513
|
+
next unless site.values[0] =~ /#{host}\.(yml|yaml)/
|
514
|
+
|
515
|
+
playbook_path = site.values[0]
|
516
|
+
end
|
517
|
+
|
518
|
+
YAML.load_file(playbook_path).each do |playbook|
|
519
|
+
playbook['roles'].each do |role|
|
520
|
+
roles_with_specs << role if check_role_specs_available(role)
|
521
|
+
end
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
return roles_with_specs.uniq
|
526
|
+
end
|
527
|
+
|
528
|
+
####################
|
529
|
+
# PLAYBOOK METHODS #
|
530
|
+
####################
|
531
|
+
|
532
|
+
def list_playbook_specs
|
533
|
+
get_playbooks_with_host_and_or_role_specs.each do |playbook|
|
534
|
+
command = "asp playbookspec #{playbook}"
|
535
|
+
description = "# run playbook specs (host specs and role specs) for #{playbook} playbook"
|
536
|
+
|
537
|
+
puts "#{command} #{description.rjust(77)}"
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
def run_playbook_spec(playbook)
|
542
|
+
create_playbook_rake_task(playbook)
|
543
|
+
|
544
|
+
Dir.chdir(BASE_DIR) do
|
545
|
+
Rake.application.invoke_task("#{playbook}")
|
546
|
+
end
|
547
|
+
|
548
|
+
calculate_coverage('playbook', playbook)
|
549
|
+
end
|
550
|
+
|
551
|
+
def create_playbook_rake_task(playbook)
|
552
|
+
ansiblespec_roles = []
|
553
|
+
|
554
|
+
hostname = playbook.gsub(/\.yml|\.yaml/,'')
|
555
|
+
|
556
|
+
get_roles_of_host(hostname).each do |role|
|
557
|
+
ansiblespec_roles << role if check_role_specs_available(role)
|
558
|
+
end
|
559
|
+
|
560
|
+
Dir.chdir(BASE_DIR) do
|
561
|
+
properties = AnsibleSpecHelper.get_properties
|
562
|
+
cfg = AnsibleSpec::AnsibleCfg.new
|
563
|
+
|
564
|
+
if properties.select { |item| item['name'] == hostname}[0]['hosts'].empty?
|
565
|
+
get_hosts_from_vai_host_file.select { |name,_| name == hostname }.each do |item|
|
566
|
+
ENV['TARGET_HOST'] = item.flatten.last['ansible_ssh_host']
|
567
|
+
ENV['TARGET_PORT'] = item.flatten.last['ansible_ssh_port']
|
568
|
+
ENV['TARGET_PRIVATE_KEY'] = item.flatten.last['ansible_ssh_private_key_file']
|
569
|
+
ENV['TARGET_USER'] = item.flatten.last['ansible_ssh_user']
|
570
|
+
end
|
571
|
+
else
|
572
|
+
ENV['TARGET_HOST'] = properties.select { |item| item['name'] == hostname}[0]['hosts'][0]['uri']
|
573
|
+
end
|
574
|
+
|
575
|
+
RSpec::Core::RakeTask.new("#{hostname}".to_sym) do |t|
|
576
|
+
log.info "Run playbook tests for #{hostname}"
|
577
|
+
|
578
|
+
roles_pattern = ansiblespec_roles.nil? ? '' : ",{#{cfg.roles_path.join(',')}}/{#{ansiblespec_roles.uniq.join(',')}}/spec/*_spec.rb"
|
579
|
+
host_pattern = check_for_host_specs(hostname) ? ",spec/#{hostname}/*_spec.rb" : ''
|
580
|
+
|
581
|
+
t.pattern = roles_pattern + host_pattern
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
def load_playbook_resources(name)
|
587
|
+
resources = []
|
588
|
+
|
589
|
+
# collect role resources
|
590
|
+
Dir.chdir(BASE_DIR) do
|
591
|
+
YAML.load_file('./site.yml').each do |site|
|
592
|
+
next unless site.values[0] =~ /#{name}\.(yml|yaml)/
|
593
|
+
|
594
|
+
playbook_path = site.values[0]
|
595
|
+
|
596
|
+
AnsibleSpec.load_playbook(playbook_path).each do |playbook|
|
597
|
+
playbook['roles'].map { |role| resources << load_role_resources(role) }
|
598
|
+
end
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
# collect host resources
|
603
|
+
resources = resources.flatten + load_host_resources(name)
|
604
|
+
|
605
|
+
return resources
|
606
|
+
end
|
607
|
+
|
608
|
+
def check_for_existing_playbook(host)
|
609
|
+
Dir.chdir(BASE_DIR) do
|
610
|
+
if File.exists?("#{host}.yml")
|
611
|
+
return true
|
612
|
+
else
|
613
|
+
return false
|
614
|
+
end
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
def get_playbooks_host_spec_summary
|
619
|
+
playbooks = []
|
620
|
+
|
621
|
+
Dir.chdir(BASE_DIR) do
|
622
|
+
YAML.load_file('./site.yml').each do |site|
|
623
|
+
Dir.glob(site.values[0]).each do |playbook|
|
624
|
+
playbook = File.basename(playbook)
|
625
|
+
host = playbook.gsub(/\.yml|\.yaml/,'')
|
626
|
+
|
627
|
+
playbooks << { playbook => check_for_host_specs(host) }
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
return playbooks.uniq
|
633
|
+
end
|
634
|
+
|
635
|
+
def get_playbooks_with_host_and_or_role_specs
|
636
|
+
playbooks = []
|
637
|
+
|
638
|
+
get_playbooks_host_spec_summary.each do |entry|
|
639
|
+
host = entry.keys[0].gsub(/\.yml|\.yaml/,'')
|
640
|
+
has_specs = entry.values[0]
|
641
|
+
|
642
|
+
if has_specs
|
643
|
+
playbooks << host
|
644
|
+
else
|
645
|
+
get_roles_of_host(host).each do |role|
|
646
|
+
playbooks << host if check_role_specs_available(role)
|
647
|
+
end
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
return playbooks.uniq
|
652
|
+
end
|
653
|
+
end
|