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.
@@ -0,0 +1,553 @@
1
+ require_relative '../spec_helper'
2
+ require_relative '../lib/ansible_spec_plus'
3
+ require_relative '../lib/ansiblespec_helper'
4
+
5
+ describe AnsibleSpecPlus do
6
+ include Helpers::Log
7
+
8
+ subject { described_class.new }
9
+
10
+ describe 'list_all_specs' do
11
+ it "lists specs for roles, hosts and playbook at once" do
12
+ roles = ['role1', 'role2']
13
+ hosts = ['host1']
14
+ playbooks = ['playbook1', 'playbook2']
15
+
16
+ host_file = { "host1" => {} }
17
+
18
+ allow(subject).to receive(:get_roles_with_specs).and_return(roles)
19
+ allow(subject).to receive(:get_hosts_with_specs).and_return(hosts)
20
+ allow(subject).to receive(:get_playbooks_with_host_and_or_role_specs).and_return(playbooks)
21
+ allow(subject).to receive(:get_hosts_from_vai_host_file).and_return(host_file)
22
+
23
+ # WHEN / THEN
24
+ expect do
25
+ subject.list_all_specs
26
+ end.to output("asp rolespec role1 # run role specs for role1\nasp rolespec role2 # run role specs for role2\nasp hostspec host1 # run host specs for host1\nasp playbookspec playbook1 # run playbook specs (host specs and role specs) for playbook1 playbook\nasp playbookspec playbook2 # run playbook specs (host specs and role specs) for playbook2 playbook\n").to_stdout
27
+ end
28
+ end
29
+
30
+ describe 'list_role_specs' do
31
+ it 'prints commands for running specs for roles' do
32
+ all_roles = ['test1', 'test2', 'test3']
33
+
34
+ allow(subject).to receive(:get_roles_with_specs).and_return(all_roles)
35
+
36
+ expect do
37
+ subject.list_role_specs
38
+ end.to output("asp rolespec test1 # run role specs for test1\nasp rolespec test2 # run role specs for test2\nasp rolespec test3 # run role specs for test3\n").to_stdout
39
+ end
40
+ end
41
+
42
+ # describe 'run_role_spec' do
43
+ # it "does somthing" do
44
+ # # GIVEN
45
+ # allow(RolesHelper).to receive(:check_role_directory_available).with('foo_role').and_return true
46
+ # allow(RolesHelper).to receive(:check_role_specs_available).with('foo_role').and_return true
47
+ #
48
+ # # WHEN
49
+ # res = subject
50
+ #
51
+ # # THEN
52
+ # expect(res).to eq true
53
+ # end
54
+ # end
55
+
56
+ describe 'get_all_roles' do
57
+ it 'returns an array of roles / directories' do
58
+ # GIVEN
59
+ file_list = ['/roles/test1', '/roles/test2', '/roles/test3']
60
+ allow(Dir).to receive(:glob).and_return(file_list)
61
+
62
+ # WHEN
63
+ res = subject.get_all_roles
64
+
65
+ # THEN
66
+ expect(res).to eq ["test1", "test2", "test3"]
67
+ end
68
+
69
+ it "returns an empty array if no roles / directories are present" do
70
+ # GIVEN
71
+ allow(Dir).to receive(:glob).and_return []
72
+
73
+ # WHEN
74
+ res = subject.get_all_roles
75
+
76
+ # THEN
77
+ expect(res).to eq []
78
+ end
79
+ end
80
+
81
+ describe 'get_roles_with_specs' do
82
+ it 'returns an array with roles that have specs' do
83
+ # GIVEN
84
+ file_list = ['test1', 'test2', 'test3']
85
+ allow(subject).to receive(:get_all_roles).and_return(file_list)
86
+
87
+ allow(Dir).to receive(:glob).with('roles/test1/spec/*_spec.rb').and_return ['roles/test1/spec/foo_spec.rb']
88
+ allow(Dir).to receive(:glob).with('roles/test2/spec/*_spec.rb').and_return []
89
+ allow(Dir).to receive(:glob).with('roles/test3/spec/*_spec.rb').and_return ['roles/test3/spec/bar_spec.rb']
90
+
91
+ allow(File).to receive(:size).with('roles/test1/spec/foo_spec.rb').and_return 123
92
+ allow(File).to receive(:size).with('').and_return 0
93
+ allow(File).to receive(:size).with('roles/test3/spec/bar_spec.rb').and_return 456
94
+
95
+ # WHEN
96
+ res = subject.get_roles_with_specs
97
+
98
+ # THEN
99
+ expect(res).to eq ['test1', 'test3']
100
+ end
101
+
102
+ it 'returns an array with roles that have specs with contents' do
103
+ # GIVEN
104
+ file_list = ['test1', 'test2']
105
+ allow(subject).to receive(:get_all_roles).and_return(file_list)
106
+
107
+ allow(Dir).to receive(:glob).with('roles/test1/spec/*_spec.rb').and_return ['roles/test1/spec/foo_spec.rb']
108
+ allow(Dir).to receive(:glob).with('roles/test2/spec/*_spec.rb').and_return ['roles/test2/spec/doo_spec.rb']
109
+
110
+ allow(File).to receive(:size).with('roles/test1/spec/foo_spec.rb').and_return 0
111
+ allow(File).to receive(:size).with('roles/test2/spec/doo_spec.rb').and_return 123
112
+
113
+ # WHEN
114
+ res = subject.get_roles_with_specs
115
+
116
+ # THEN
117
+ expect(res).to eq ['test2']
118
+ end
119
+ end
120
+
121
+ describe 'check_for_specs_in_file' do
122
+ it "returns true if file contains 'describe '" do
123
+ # GIVEN
124
+ test_spec_file = ["require 'spec_helper'\n", "\n", "describe command('docker network ls') do\n"]
125
+ allow(File).to receive(:readlines).with('test_spec.rb').and_return(test_spec_file)
126
+
127
+ # WHEN
128
+ res = subject.check_for_specs_in_file('test_spec.rb')
129
+
130
+ # THEN
131
+ expect(res).to eq true
132
+ end
133
+
134
+ it "does not return true if file contains no 'describe '" do
135
+ # GIVEN
136
+ test_spec_file = ["require 'spec_helper'\n", "\n"]
137
+ allow(File).to receive(:readlines).with('test_spec.rb').and_return(test_spec_file)
138
+
139
+ # WHEN
140
+ res = subject.check_for_specs_in_file('test_spec.rb')
141
+
142
+ # THEN
143
+ expect(res).not_to eq true
144
+ expect(res).to eq false
145
+ end
146
+ end
147
+
148
+ describe 'get_hosts_with_specs' do
149
+ it 'returns an array with hosts that have specs' do
150
+ # GIVEN
151
+ allow(subject).to receive(:get_vagrant_or_regular_ansible_hosts).and_return ['foo_host1', 'foo_host2', 'foo_host2']
152
+
153
+ allow(subject).to receive(:check_for_host_specs).with('foo_host1').and_return false
154
+ allow(subject).to receive(:check_for_host_specs).with('foo_host2').and_return true
155
+ allow(subject).to receive(:check_for_host_specs).with('foo_host3').and_return false
156
+
157
+ # WHEN
158
+ res = subject.get_hosts_with_specs
159
+
160
+ # THEN
161
+ expect(res).to eq ['foo_host2']
162
+ end
163
+ end
164
+
165
+ describe 'get_roles_without_specs' do
166
+ it 'returns an array with name of roles that don\'t have specs' do
167
+ # GIVEN
168
+ allow(subject).to receive(:get_all_roles).and_return ['test1', 'test2', 'test3']
169
+ allow(subject).to receive(:get_roles_with_specs).and_return ['test1', 'test3']
170
+
171
+ # WHEN
172
+ res = subject.get_roles_without_specs
173
+
174
+ # THEN
175
+ expect(res).to eq ['test2']
176
+ end
177
+ end
178
+
179
+ describe 'check_role_directory_available' do
180
+ it 'returns true if role directoy exists' do
181
+ # GIVEN
182
+ allow(Dir).to receive(:exists?).with('roles/foo').and_return true
183
+
184
+ # WHEN
185
+ res = subject.check_role_directory_available('foo')
186
+
187
+ # THEN
188
+ expect(res).to eq true
189
+ end
190
+
191
+ it "return false if directory does not exist" do
192
+ # GIVEN
193
+ allow(Dir).to receive(:exists?).with('roles/foo').and_return false
194
+ allow(log).to receive(:error)
195
+
196
+ # WHEN
197
+ res = subject.check_role_directory_available('foo')
198
+
199
+ # THEN
200
+ expect(res).to eq false
201
+ end
202
+
203
+ it "logs an error if directory does not exist" do
204
+ # GIVEN
205
+ allow(Dir).to receive(:exists?).with('roles/foo').and_return false
206
+
207
+ # THEN
208
+ expect(log).to receive(:error).with("Directory 'roles/foo' does not exist. That's strange, isn't it?")
209
+
210
+ # WHEN
211
+ subject.check_role_directory_available('foo')
212
+ end
213
+ end
214
+
215
+ describe 'check_role_specs_available' do
216
+ it "returns true if spec file is not empty" do
217
+ # GIVEN
218
+ allow(Dir).to receive(:glob).with('roles/foo/spec/*_spec.rb').and_return ['roles/foo/spec/foo_spec.rb']
219
+ allow(File).to receive(:size).with('roles/foo/spec/foo_spec.rb').and_return 123
220
+
221
+ # WHEN
222
+ res = subject.check_role_specs_available('foo')
223
+
224
+ # THEN
225
+ expect(res).to eq true
226
+ end
227
+
228
+ it "returns false if spec file is empty" do
229
+ # GIVEN
230
+ allow(Dir).to receive(:glob).with('roles/foo/spec/*_spec.rb').and_return ['roles/foo/spec/foo_spec.rb']
231
+ allow(File).to receive(:size).with('roles/foo/spec/foo_spec.rb').and_return 0
232
+ allow(log).to receive(:error)
233
+
234
+ # WHEN
235
+ res = subject.check_role_specs_available('foo')
236
+
237
+ # THEN
238
+ expect(res).to eq false
239
+ end
240
+
241
+ it "returns false if spec file has a newline" do
242
+ # GIVEN
243
+ allow(Dir).to receive(:glob).with('roles/foo/spec/*_spec.rb').and_return ['roles/foo/spec/foo_spec.rb']
244
+ allow(File).to receive(:size).with('roles/foo/spec/foo_spec.rb').and_return 1
245
+ allow(log).to receive(:error)
246
+
247
+ # WHEN
248
+ res = subject.check_role_specs_available('foo')
249
+
250
+ # THEN
251
+ expect(res).to eq false
252
+ end
253
+
254
+ it "returns false if there is an error" do
255
+ # GIVEN
256
+ allow(Dir).to receive(:glob).with('roles/foo/spec/*_spec.rb').and_return ['roles/foo/spec/foo_spec.rb']
257
+ allow(File).to receive(:size).with('roles/foo/spec/foo_spec.rb').and_return 1
258
+
259
+ # WHEN
260
+ res = subject.check_role_specs_available('foo')
261
+
262
+ # THEN
263
+ expect(res).to eq false
264
+
265
+ end
266
+ end
267
+
268
+ describe "get_hosts_where_role_is_used" do
269
+ it "returns array with one host if role is only used there" do
270
+ # GIVEN
271
+ playbooks = [
272
+ {"include"=>"foo.yml"},
273
+ {"include"=>"boo.yml"}
274
+ ]
275
+ allow(YAML).to receive(:load_file).and_return(playbooks)
276
+
277
+ foo_playbook = [{
278
+ "name"=>"foo",
279
+ "hosts"=>"foo-hosts",
280
+ "roles"=>
281
+ ["role1",
282
+ "role2",
283
+ "role3",
284
+ "role4",
285
+ "role5"]
286
+ }]
287
+ allow(YAML).to receive(:load_file).and_return(foo_playbook)
288
+
289
+ boo_playbook = [{
290
+ "name"=>"boo",
291
+ "hosts"=>"boo-hosts",
292
+ "roles"=>
293
+ ["role1",
294
+ "role6"]
295
+ }]
296
+ allow(YAML).to receive(:load_file).and_return(boo_playbook)
297
+
298
+ # WHEN
299
+ res = subject.get_hosts_where_role_is_used('role6')
300
+
301
+ # THEN
302
+ expect(res).to eq ['boo']
303
+ end
304
+
305
+ it "returns array with all hosts where role is used" do
306
+ # GIVEN
307
+ playbooks = [
308
+ {"include"=>"foo.yml"},
309
+ {"include"=>"boo.yml"}
310
+ ]
311
+ allow(YAML).to receive(:load_file).with('site.yml').and_return(playbooks)
312
+
313
+ foo_playbook = [{
314
+ "name"=>"foo",
315
+ "hosts"=>"foo-hosts",
316
+ "roles"=>
317
+ ["role1",
318
+ "role2",
319
+ "role3",
320
+ "role4",
321
+ "role5"]
322
+ }]
323
+ allow(YAML).to receive(:load_file).with('foo.yml').and_return(foo_playbook)
324
+
325
+ boo_playbook = [{
326
+ "name"=>"boo",
327
+ "hosts"=>"boo-hosts",
328
+ "roles"=>
329
+ ["role1",
330
+ "role6"]
331
+ }]
332
+ allow(YAML).to receive(:load_file).with('boo.yml').and_return(boo_playbook)
333
+
334
+ # WHEN
335
+ res = subject.get_hosts_where_role_is_used('role1')
336
+
337
+ # THEN
338
+ expect(res).to eq ['foo','boo']
339
+ end
340
+
341
+ it "returns array with one host if role of typ hash is used" do
342
+ # GIVEN
343
+ playbooks = [
344
+ {"include"=>"foo.yml"},
345
+ {"include"=>"boo.yml"}
346
+ ]
347
+ allow(YAML).to receive(:load_file).with('site.yml').and_return(playbooks)
348
+
349
+ foo_playbook = [{
350
+ "name"=>"foo",
351
+ "hosts"=>"foo-hosts",
352
+ "roles"=>
353
+ ["role1",
354
+ "role2",
355
+ "role3",
356
+ {"role"=>"role4"},
357
+ "role5"]
358
+ }]
359
+ allow(YAML).to receive(:load_file).with('foo.yml').and_return(foo_playbook)
360
+
361
+ boo_playbook = [{
362
+ "name"=>"boo",
363
+ "hosts"=>"boo-hosts",
364
+ "roles"=>
365
+ ["role1",
366
+ "role6"]
367
+ }]
368
+ allow(YAML).to receive(:load_file).with('boo.yml').and_return(boo_playbook)
369
+
370
+ # WHEN
371
+ res = subject.get_hosts_where_role_is_used('role4')
372
+
373
+ # THEN
374
+ expect(res).to eq ['foo']
375
+ end
376
+ end
377
+
378
+ describe 'load_role_resources' do
379
+ it 'returns resources for a given role' do
380
+ # GIVEN
381
+ allow(Dir).to receive(:glob).with('roles/test_role/tasks/*.yml').and_return ['roles/test_role/tasks/test_role.yml']
382
+
383
+ resources = [
384
+ {
385
+ "name"=>"create deploy user",
386
+ "user"=> {"name"=>"deploy", "comment"=>"User for deployments", "append"=>true},
387
+ "roles"=>[]
388
+ },
389
+ {
390
+ "name"=>"sudo right for deploy user is present",
391
+ "template"=> "src=sudo/deploy dest=/etc/sudoers.d/deploy owner=root group=root mode=0440",
392
+ "roles"=>[]
393
+ }
394
+ ]
395
+ allow(AnsibleSpec).to receive(:load_playbook).with('roles/test_role/tasks/test_role.yml').and_return(resources)
396
+
397
+ # WHEN
398
+ res = subject.load_role_resources('test_role')
399
+
400
+ # THEN
401
+ expect(res).to eq [{"name"=>"create deploy user", "user"=> {"name"=>"deploy", "comment"=>"User for deployments", "append"=>true}, "roles"=>[]},{"name"=>"sudo right for deploy user is present", "template"=> "src=sudo/deploy dest=/etc/sudoers.d/deploy owner=root group=root mode=0440", "roles"=>[]}]
402
+ end
403
+ end
404
+
405
+ describe 'load_host_resources' do
406
+ it 'returns resources for a given host' do
407
+ # GIVEN
408
+ playbook = [{
409
+ "hosts"=>"foo-hosts",
410
+ "name"=>"foo",
411
+ "remote_user"=>"vagrant",
412
+ "serial"=>1,
413
+ "vars_files"=>["roles/cme-infrastructure/vars/secrets.yml"],
414
+ "roles"=>
415
+ ["common",
416
+ "docker",
417
+ "docker-compose",
418
+ "docker-flow",
419
+ "java",
420
+ "maven",
421
+ "cme-infrastructure",
422
+ "cme-demo"],
423
+ "tasks"=>
424
+ [{"name"=>"Debian python-pymongo is present",
425
+ "pip"=>{"name"=>"pymongo", "state"=>"latest"}},
426
+ {"name"=>"create mongodb user and database 'hello-world-java'",
427
+ "mongodb_user"=>
428
+ {"login_user"=>"root",
429
+ "login_password"=>"secret",
430
+ "login_database"=>"admin",
431
+ "database"=>"hello-world-java",
432
+ "name"=>"hello-world-java",
433
+ "password"=>"hello-world-java",
434
+ "roles"=>"readWrite,dbAdmin",
435
+ "state"=>"present"}}]
436
+ }]
437
+
438
+ allow(YAML).to receive(:load_file).with('./site.yml').and_return [{"include"=>"foo.yml"}]
439
+ allow(AnsibleSpec).to receive(:load_playbook).with('foo.yml').and_return(playbook)
440
+
441
+ # WHEN
442
+ res = subject.load_host_resources('foo')
443
+
444
+ # THEN
445
+ expect(res).to eq [{"name"=>"Debian python-pymongo is present", "pip"=>{"name"=>"pymongo", "state"=>"latest"}}, {"name"=>"create mongodb user and database 'hello-world-java'", "mongodb_user"=> {"login_user"=>"root", "login_password"=>"secret", "login_database"=>"admin", "database"=>"hello-world-java", "name"=>"hello-world-java", "password"=>"hello-world-java", "roles"=>"readWrite,dbAdmin", "state"=>"present"}}]
446
+ end
447
+
448
+ it 'returns no resources for a given host if there are none' do
449
+ # GIVEN
450
+ playbook = [{
451
+ "hosts"=>"foo-hosts",
452
+ "name"=>"foo",
453
+ "remote_user"=>"vagrant",
454
+ "serial"=>1,
455
+ "vars_files"=>["roles/cme-infrastructure/vars/secrets.yml"],
456
+ "roles"=>
457
+ ["common",
458
+ "docker",
459
+ "docker-compose",
460
+ "docker-flow",
461
+ "java",
462
+ "maven",
463
+ "cme-infrastructure",
464
+ "cme-demo"]
465
+ }]
466
+
467
+ allow(YAML).to receive(:load_file).with('./site.yml').and_return [{"include"=>"foo.yml"}]
468
+ allow(AnsibleSpec).to receive(:load_playbook).with('foo.yml').and_return(playbook)
469
+
470
+ # WHEN
471
+ res = subject.load_host_resources('foo')
472
+
473
+ # THEN
474
+ expect(res).to eq []
475
+ end
476
+ end
477
+
478
+ describe 'analyze_resources' do
479
+ it 'prints a log message for unknown resource "apt"' do
480
+ # GIVEN
481
+ stub_const('AnsibleSpecPlus::KNOWN_RESOURCES', ['file','template','docker_container','docker_image','service'])
482
+ file_resource = [{"name"=>"ensure apt cache is up to date","apt"=>"update_cache=yes cache_valid_time=3600"}]
483
+
484
+ # THEN
485
+ expect(log).to receive(:warn).with('Unknown resource: {"name"=>"ensure apt cache is up to date", "apt"=>"update_cache=yes cache_valid_time=3600"}')
486
+
487
+ # WHEN
488
+ subject.analyze_resources(file_resource)
489
+ end
490
+
491
+ it 'returns a result for a string file resource' do
492
+ # GIVEN
493
+ stub_const('AnsibleSpecPlus::KNOWN_RESOURCES', ['file','template','docker_container','docker_image','service'])
494
+ file_resource = [{"name"=>"test one","file"=>"path=/git/.ssh state=directory owner=git group=git mode=0755"}]
495
+
496
+ # WHEN
497
+ res = subject.analyze_resources(file_resource)
498
+
499
+ # THEN
500
+ expect(res).to eq ["File \"/git/.ssh\""]
501
+ end
502
+
503
+ it 'returns a empty result for a string file resource if path contains double {{ }}' do
504
+ # GIVEN
505
+ stub_const('AnsibleSpecPlus::KNOWN_RESOURCES', ['file','template','docker_container','docker_image','service'])
506
+ file_resource = [{"name"=>"test one","file"=>"path=/opt/{{item.0}}/{{item.1}} state=directory owner=git group=git mode=0755"}]
507
+ allow(log).to receive(:warn)
508
+
509
+ # WHEN
510
+ res = subject.analyze_resources(file_resource)
511
+
512
+ # THEN
513
+ expect(res).to eq []
514
+ end
515
+
516
+ it 'returns a result for a hash file resource' do
517
+ # GIVEN
518
+ stub_const('AnsibleSpecPlus::KNOWN_RESOURCES', ['file','template','docker_container','docker_image','service'])
519
+ file_resource = [{"name"=>"Ensure webroot exists", "file"=>{"path"=>"/some/path","state"=>"directory","follow"=>true},"become"=>true,"roles"=>[]}]
520
+
521
+ # WHEN
522
+ res = subject.analyze_resources(file_resource)
523
+
524
+ # THEN
525
+ expect(res).to eq ["File \"/some/path\""]
526
+ end
527
+
528
+ it 'returns a empty result for a hash file resource if path contains double {{ }}' do
529
+ # GIVEN
530
+ stub_const('AnsibleSpecPlus::KNOWN_RESOURCES', ['file','template','docker_container','docker_image','service'])
531
+ file_resource = [{"name"=>"Ensure webroot exists", "file"=>{"path"=>"{{ letsencrypt_webroot_path }}","state"=>"directory","follow"=>true},"become"=>true,"roles"=>[]}]
532
+ allow(log).to receive(:warn)
533
+
534
+ # WHEN
535
+ res = subject.analyze_resources(file_resource)
536
+
537
+ # THEN
538
+ expect(res).to eq []
539
+ end
540
+
541
+ it 'returns a result for a string template resource' do
542
+ # GIVEN
543
+ stub_const('AnsibleSpecPlus::KNOWN_RESOURCES', ['file','template','docker_container','docker_image','service'])
544
+ file_resource = [{"name"=>"fix 'stdin is not a tty' warning","template"=>"src=profile dest=/root/.profile owner=root group=root mode=0644","roles"=>[]}]
545
+
546
+ # WHEN
547
+ res = subject.analyze_resources(file_resource)
548
+
549
+ # THEN
550
+ expect(res).to eq ["File \"/root/.profile\""]
551
+ end
552
+ end
553
+ end