ansible_spec_plus 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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