kitchen-inspec 0.26.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
-
4
- require "bundler/setup"
5
- require "kitchen/inspec"
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require 'pry'
12
- # Pry.start
13
-
14
- require "irb"
15
- IRB.start
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/bin/bash
2
-
3
- set -euo pipefail
4
- IFS=$'\n\t'
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -1,24 +0,0 @@
1
- # encoding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "kitchen/verifier/inspec_version"
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "kitchen-inspec"
8
- spec.version = Kitchen::Verifier::INSPEC_VERSION
9
- spec.license = "Apache-2.0"
10
- spec.authors = ["Chef Software, Inc."]
11
- spec.email = ["info@chef.io"]
12
-
13
- spec.summary = "A Test Kitchen Verifier for InSpec"
14
- spec.description = spec.summary
15
- spec.homepage = "https://github.com/inspec/kitchen-inspec"
16
-
17
- spec.files = `git ls-files -z`.split("\x0")
18
- .grep(/LICENSE|^lib|/)
19
- spec.require_paths = ["lib"]
20
- spec.required_ruby_version = ">= 2.1.0"
21
- spec.add_dependency "inspec", ">=0.34.0", "<4.0.0"
22
- spec.add_dependency "test-kitchen", "~> 1.6"
23
- spec.add_dependency "hashie", "~> 3.4"
24
- end
@@ -1,607 +0,0 @@
1
- # encoding: utf-8
2
- #
3
- # Author:: Fletcher Nichol (<fnichol@chef.io>)
4
- # Author:: Christoph Hartmann (<chartmann@chef.io>)
5
- #
6
- # Copyright (C) 2015, Chef Software Inc.
7
- #
8
- # Licensed under the Apache License, Version 2.0 (the "License");
9
- # you may not use this file except in compliance with the License.
10
- # You may obtain a copy of the License at
11
- #
12
- # http://www.apache.org/licenses/LICENSE-2.0
13
- #
14
- # Unless required by applicable law or agreed to in writing, software
15
- # distributed under the License is distributed on an "AS IS" BASIS,
16
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
- # See the License for the specific language governing permissions and
18
- # limitations under the License.
19
-
20
- require_relative "../../spec_helper"
21
-
22
- require "logger"
23
-
24
- require "kitchen/verifier/inspec"
25
- require "kitchen/transport/exec"
26
- require "kitchen/transport/ssh"
27
- require "kitchen/transport/winrm"
28
-
29
- describe Kitchen::Verifier::Inspec do
30
-
31
- let(:logged_output) { StringIO.new }
32
- let(:logger) { Logger.new(logged_output) }
33
- let(:config) do
34
- {
35
- kitchen_root: kitchen_root,
36
- test_base_path: File.join(kitchen_root, "test", "integration"),
37
- backend_cache: true,
38
- reporter: [
39
- "cli",
40
- "junit:path/to/results/%{platform}_%{suite}_inspec.xml",
41
- ],
42
- }
43
- end
44
- let(:transport_config) { {} }
45
- let(:kitchen_root) { Dir.mktmpdir }
46
-
47
- let(:platform) do
48
- instance_double("Kitchen::Platform", os_type: nil, shell_type: nil, name: "default")
49
- end
50
-
51
- let(:suite) do
52
- instance_double("Kitchen::Suite", name: "germany")
53
- end
54
-
55
- let(:transport) do
56
- instance_double(
57
- "Kitchen::Transport::Dummy",
58
- name: "wickedsauce",
59
- diagnose: transport_config
60
- )
61
- end
62
-
63
- let(:instance) do
64
- instance_double(
65
- "Kitchen::Instance",
66
- name: "coolbeans",
67
- logger: logger,
68
- platform: platform,
69
- suite: suite,
70
- transport: transport,
71
- to_str: "instance"
72
- )
73
- end
74
-
75
- before do
76
- allow(transport).to receive(:instance).and_return(instance)
77
- end
78
-
79
- after do
80
- FileUtils.remove_entry(kitchen_root)
81
- end
82
-
83
- let(:verifier) do
84
- Kitchen::Verifier::Inspec.new(config).finalize_config!(instance)
85
- end
86
-
87
- it "verifier api_version is 1" do
88
- expect(verifier.diagnose_plugin[:api_version]).to eq(1)
89
- end
90
-
91
- it "plugin_version is set to Kitchen::Verifier::INSPEC_VERSION" do
92
- expect(verifier.diagnose_plugin[:version])
93
- .to eq(Kitchen::Verifier::INSPEC_VERSION)
94
- end
95
-
96
- describe "configuration" do
97
- let(:transport) do
98
- Kitchen::Transport::Ssh.new({})
99
- end
100
-
101
- it "supports reporter config platform and suite replacements" do
102
- config = verifier.send(:runner_options, transport, {}, "osx", "internal")
103
- expected_value = [
104
- "cli",
105
- "junit:path/to/results/osx_internal_inspec.xml",
106
- ]
107
-
108
- expect(config.to_hash).to include("reporter" => expected_value)
109
- end
110
-
111
- it "backend_cache option sets to true" do
112
- config = verifier.send(:runner_options, transport)
113
- expect(config.to_hash).to include(backend_cache: true)
114
- end
115
-
116
- it "backend_cache option defaults to false" do
117
- config[:backend_cache] = nil
118
- config = verifier.send(:runner_options, transport)
119
- expect(config.to_hash).to include(backend_cache: false)
120
- end
121
-
122
- it "inspec version warn for backend_cache" do
123
- config[:backend_cache] = true
124
- stub_const("Inspec::VERSION", "1.46.0")
125
- expect_any_instance_of(Logger).to receive(:warn).
126
- with("backend_cache requires InSpec version >= 1.47.0").
127
- and_return("captured")
128
- config = verifier.send(:runner_options, transport)
129
- expect(config.to_hash).to include(backend_cache: true)
130
- end
131
- end
132
-
133
- describe "#finalize_config!" do
134
- let(:kitchen_inspec_tests) { File.join(kitchen_root, "test", "recipes") }
135
- context "when a test/recipes folder exists" do
136
- before do
137
- FileUtils.mkdir_p(kitchen_inspec_tests)
138
- end
139
-
140
- it "should read the tests from there" do
141
- expect(verifier[:test_base_path]).to eq(kitchen_inspec_tests)
142
- end
143
- end
144
-
145
- context "when a test/recipes folder does not exist" do
146
- it "should read the tests from the default location" do
147
- expect(verifier[:test_base_path]).to eq(File.join(kitchen_root, "test", "integration"))
148
- end
149
- end
150
- end
151
-
152
- describe "#resolve_config_inspec_tests" do
153
- context "when the entry is a string" do
154
- context "when the path does not exist" do
155
- it "returns the original string" do
156
- config[:inspec_tests] = ["test/integration/foo"]
157
- expect(File).to receive(:exist?).with("test/integration/foo").and_return(false)
158
- allow(File).to receive(:exist?).and_call_original
159
- expect(verifier.send(:resolve_config_inspec_tests)).to eq(["test/integration/foo"])
160
- end
161
- end
162
-
163
- context "when the path exists" do
164
- it "expands to an absolute path and returns a hash" do
165
- config[:inspec_tests] = ["test/integration/foo"]
166
- expect(File).to receive(:exist?).with("test/integration/foo").and_return(true)
167
- allow(File).to receive(:exist?).and_call_original
168
- expect(File).to receive(:expand_path).with("test/integration/foo").and_return("/absolute/path/to/foo")
169
- expect(verifier.send(:resolve_config_inspec_tests)).to eq([{ path: "/absolute/path/to/foo" }])
170
- end
171
- end
172
- end
173
-
174
- context "when the entry is a hash" do
175
- context "when the entry is a path" do
176
- it "expands the path to an absolute path and removes unnecessary keys" do
177
- config[:inspec_tests] = [{ name: "foo_profile", path: "test/integration/foo" }]
178
- expect(File).to receive(:expand_path).with("test/integration/foo").and_return("/absolute/path/to/foo")
179
- expect(verifier.send(:resolve_config_inspec_tests)).to eq([{ path: "/absolute/path/to/foo" }])
180
- end
181
- end
182
-
183
- context "when the entry is a url item" do
184
- it "returns a hash with unnecessary keys removed" do
185
- config[:inspec_tests] = [{ name: "foo_profile", url: "http://some.domain/profile" }]
186
- expect(verifier.send(:resolve_config_inspec_tests)).to eq([{ url: "http://some.domain/profile" }])
187
- end
188
- end
189
-
190
- context "when the entry is a git item" do
191
- it "returns a hash with unnecessary keys removed" do
192
- config[:inspec_tests] = [{ name: "foo_profile", git: "http://some.domain/profile" }]
193
- expect(verifier.send(:resolve_config_inspec_tests)).to eq([{ git: "http://some.domain/profile" }])
194
- end
195
- end
196
-
197
- context "when the entry is a compliance item" do
198
- it "returns a hash with unnecessary keys removed" do
199
- config[:inspec_tests] = [{ name: "foo_profile", compliance: "me/foo" }]
200
- expect(verifier.send(:resolve_config_inspec_tests)).to eq([{ compliance: "me/foo" }])
201
- end
202
- end
203
-
204
- context "when the entry only contains a name" do
205
- it "returns it as-is to be resolved on Supermarket" do
206
- config[:inspec_tests] = [{ name: "me/foo" }]
207
- expect(verifier.send(:resolve_config_inspec_tests)).to eq([{ name: "me/foo" }])
208
- end
209
- end
210
-
211
- context "when the entry contains no acceptable keys" do
212
- it "returns nil" do
213
- config[:inspec_tests] = [{ key1: "value1", key2: "value2" }]
214
- expect(verifier.send(:resolve_config_inspec_tests)).to eq([nil])
215
- end
216
- end
217
- end
218
-
219
- it "returns an array of properly formatted entries when multiple entries are supplied" do
220
- config[:inspec_tests] = [
221
- { name: "profile1", git: "me/profile1" },
222
- { name: "profile2", random_key: "random_value", compliance: "me/profile2" },
223
- { name: "profile3", url: "someurl", random_key: "what is this for?", another_random_key: 123 },
224
- { name: "profile4" },
225
- ]
226
-
227
- expect(verifier.send(:resolve_config_inspec_tests)).to eq([
228
- { git: "me/profile1" },
229
- { compliance: "me/profile2" },
230
- { url: "someurl" },
231
- { name: "profile4" },
232
- ])
233
- end
234
- end
235
-
236
- context "with an ssh transport" do
237
-
238
- let(:transport_config) do
239
- {
240
- hostname: "boogie",
241
- port: "I shouldn't be used",
242
- username: "dance",
243
- ssh_key: "/backstage/pass",
244
- keepalive: "keepalive",
245
- keepalive_interval: "forever",
246
- connection_timeout: "nope",
247
- connection_retries: "thousand",
248
- connection_retry_sleep: "sleepy",
249
- max_wait_until_ready: 42,
250
- compression: "maxyo",
251
- compression_level: "pico",
252
- }
253
- end
254
-
255
- let(:transport) do
256
- Kitchen::Transport::Ssh.new(transport_config)
257
- end
258
-
259
- let(:runner) do
260
- instance_double("Inspec::Runner")
261
- end
262
-
263
- before do
264
- allow(runner).to receive(:add_target)
265
- allow(runner).to receive(:run).and_return 0
266
- end
267
-
268
- it "constructs a Inspec::Runner using transport config data and state" do
269
- config[:sudo] = "jellybeans"
270
- config[:sudo_command] = "allyourbase"
271
- config[:proxy_command] = "gateway"
272
-
273
- expect(Inspec::Runner).to receive(:new)
274
- .with(
275
- hash_including(
276
- "backend" => "ssh",
277
- "logger" => logger,
278
- "sudo" => "jellybeans",
279
- "sudo_command" => "allyourbase",
280
- "host" => "boogie",
281
- "port" => 123,
282
- "user" => "dance",
283
- "keepalive" => "keepalive",
284
- "keepalive_interval" => "forever",
285
- "connection_timeout" => "nope",
286
- "connection_retries" => "thousand",
287
- "connection_retry_sleep" => "sleepy",
288
- "max_wait_until_ready" => 42,
289
- "compression" => "maxyo",
290
- "compression_level" => "pico",
291
- "key_files" => ["/backstage/pass"],
292
- "proxy_command" => "gateway"
293
- )
294
- )
295
- .and_return(runner)
296
-
297
- verifier.call(port: 123)
298
- end
299
-
300
- it "constructs a Inspec::Runner using transport config data(host and port)" do
301
- config[:host] = "192.168.33.40"
302
- config[:port] = 222
303
-
304
- expect(Inspec::Runner).to receive(:new)
305
- .with(
306
- hash_including(
307
- "backend" => "ssh",
308
- "host" => "192.168.33.40",
309
- "port" => 222
310
- )
311
- )
312
- .and_return(runner)
313
-
314
- verifier.call(port: 123)
315
- end
316
-
317
- it "constructs an Inspec::Runner with a specified inspec output format" do
318
- config[:format] = "documentation"
319
-
320
- expect(Inspec::Runner).to receive(:new)
321
- .with(
322
- hash_including(
323
- "format" => "documentation"
324
- )
325
- )
326
- .and_return(runner)
327
-
328
- verifier.call(port: 123)
329
- end
330
-
331
- it "constructs an Inspec::Runner with a controls filter" do
332
- config[:controls] = %w{a control}
333
-
334
- expect(Inspec::Runner).to receive(:new)
335
- .with(
336
- hash_including(
337
- controls: %w{a control}
338
- )
339
- )
340
- .and_return(runner)
341
-
342
- verifier.call(port: 123)
343
- end
344
-
345
- it "does not send keys_only=true to InSpec (which breaks SSH Agent usage)" do
346
- expect(Inspec::Runner).to receive(:new)
347
- .with(
348
- hash_not_including(
349
- "keys_only" => true
350
- )
351
- )
352
- .and_return(runner)
353
-
354
- verifier.call(port: 123)
355
- end
356
-
357
- it "provide platform and test suite to build output path" do
358
- allow(Inspec::Runner).to receive(:new).and_return(runner)
359
-
360
- expect(verifier).to receive(:runner_options).with(
361
- transport,
362
- {},
363
- "default",
364
- "germany"
365
- ).and_return({})
366
- verifier.call({})
367
- end
368
-
369
- it "custom inspec output path" do
370
- ensure_suite_directory("germany")
371
- config[:output] = "/tmp/inspec_results.xml"
372
-
373
- allow(Inspec::Runner).to receive(:new).and_return(runner)
374
-
375
- expect(runner).to receive(:add_target).with({ :path =>
376
- File.join(
377
- config[:test_base_path],
378
- "germany"
379
- ) }, hash_including(
380
- "output" => "/tmp/inspec_results.xml"
381
- ))
382
-
383
- verifier.call({})
384
- end
385
-
386
- it "resolve template format for inspec output path" do
387
- ensure_suite_directory("germany")
388
- config[:output] = "/tmp/%{platform}_%{suite}.xml"
389
-
390
- allow(Inspec::Runner).to receive(:new).and_return(runner)
391
-
392
- expect(runner).to receive(:add_target).with({ :path =>
393
- File.join(
394
- config[:test_base_path],
395
- "germany"
396
- ) }, hash_including(
397
- "output" => "/tmp/default_germany.xml"
398
- ))
399
-
400
- verifier.call({})
401
- end
402
-
403
- it "find test directory for runner" do
404
- ensure_suite_directory("germany")
405
- allow(Inspec::Runner).to receive(:new).and_return(runner)
406
- expect(runner).to receive(:add_target).with({ :path =>
407
- File.join(
408
- config[:test_base_path],
409
- "germany"
410
- ) }, anything)
411
-
412
- verifier.call({})
413
- end
414
-
415
- it "find test directory for runner if legacy" do
416
- create_legacy_test_directories
417
- allow(Inspec::Runner).to receive(:new).and_return(runner)
418
- expect(runner).to receive(:add_target).with({ :path =>
419
- File.join(
420
- config[:test_base_path],
421
- "germany", "inspec"
422
- ) }, anything)
423
-
424
- verifier.call({})
425
- end
426
-
427
- it "non-existent test directory for runner" do
428
- allow(Inspec::Runner).to receive(:new).and_return(runner)
429
- expect(runner).to_not receive(:add_target).with(
430
- File.join(
431
- config[:test_base_path],
432
- "nobody"
433
- ), anything)
434
-
435
- verifier.call({})
436
- end
437
-
438
- it "calls #run on the runner" do
439
- allow(Inspec::Runner).to receive(:new).and_return(runner)
440
- expect(runner).to receive(:run)
441
-
442
- verifier.call({})
443
- end
444
- end
445
-
446
- context "with an remote profile" do
447
-
448
- let(:transport) do
449
- Kitchen::Transport::Ssh.new({})
450
- end
451
-
452
- let(:runner) do
453
- instance_double("Inspec::Runner")
454
- end
455
-
456
- let(:suite) do
457
- instance_double("Kitchen::Suite", { name: "local" })
458
- end
459
-
460
- let(:instance) do
461
- instance_double(
462
- "Kitchen::Instance",
463
- name: "coolbeans",
464
- logger: logger,
465
- platform: platform,
466
- suite: suite,
467
- transport: transport,
468
- to_str: "instance"
469
- )
470
- end
471
-
472
- let(:config) do
473
- {
474
- inspec_tests: [{ :url => "https://github.com/nathenharvey/tmp_compliance_profile" }],
475
- kitchen_root: kitchen_root,
476
- test_base_path: File.join(kitchen_root, "test", "integration"),
477
- }
478
- end
479
-
480
- before do
481
- allow(runner).to receive(:add_target)
482
- allow(runner).to receive(:run).and_return 0
483
- end
484
-
485
- it "find test directory and remote profile" do
486
- ensure_suite_directory("local")
487
- allow(Inspec::Runner).to receive(:new).and_return(runner)
488
- expect(runner).to receive(:add_target).with({ :path =>
489
- File.join(config[:test_base_path], "local") }, anything)
490
- expect(runner).to receive(:add_target).with(
491
- { :url => "https://github.com/nathenharvey/tmp_compliance_profile" }, anything)
492
- verifier.call({})
493
- end
494
- end
495
-
496
- context "with an winrm transport" do
497
-
498
- let(:transport_config) do
499
- {
500
- username: "dance",
501
- password: "party",
502
- connection_retries: "thousand",
503
- connection_retry_sleep: "sleepy",
504
- max_wait_until_ready: 42,
505
- }
506
- end
507
-
508
- let(:transport) do
509
- Kitchen::Transport::Winrm.new(transport_config)
510
- end
511
-
512
- let(:runner) do
513
- instance_double("Inspec::Runner")
514
- end
515
-
516
- before do
517
- allow(runner).to receive(:add_target)
518
- allow(runner).to receive(:run).and_return 0
519
- end
520
-
521
- it "constructs a Inspec::Runner using transport config data and state" do
522
- expect(Inspec::Runner).to receive(:new)
523
- .with(
524
- hash_including(
525
- "backend" => "winrm",
526
- "logger" => logger,
527
- "host" => "win.dows",
528
- "port" => 123,
529
- "user" => "dance",
530
- "password" => "party",
531
- "connection_retries" => "thousand",
532
- "connection_retry_sleep" => "sleepy",
533
- "max_wait_until_ready" => 42,
534
- "color" => true
535
- )
536
- )
537
- .and_return(runner)
538
-
539
- verifier.call(hostname: "win.dows", port: 123)
540
- end
541
-
542
- it "constructs a Inspec::Runner using transport config data(host and port)" do
543
- config[:host] = "192.168.56.40"
544
- config[:port] = 22
545
-
546
- expect(Inspec::Runner).to receive(:new)
547
- .with(
548
- hash_including(
549
- "backend" => "winrm",
550
- "host" => "192.168.56.40",
551
- "port" => 22
552
- )
553
- )
554
- .and_return(runner)
555
-
556
- verifier.call(hostname: "win.dows", port: 123)
557
- end
558
- end
559
-
560
- context "with an exec transport" do
561
-
562
- let(:transport) do
563
- Kitchen::Transport::Exec.new
564
- end
565
-
566
- let(:runner) do
567
- instance_double("Inspec::Runner")
568
- end
569
-
570
- before do
571
- allow(runner).to receive(:add_target)
572
- allow(runner).to receive(:run).and_return 0
573
- end
574
-
575
- it "constructs a Inspec::Runner using transport config data and state" do
576
- expect(Inspec::Runner).to receive(:new)
577
- .with(
578
- hash_including(
579
- "backend" => "local",
580
- "logger" => logger,
581
- "color" => true
582
- )
583
- )
584
- .and_return(runner)
585
-
586
- verifier.call({})
587
- end
588
- end
589
-
590
- context "with an unsupported transport" do
591
-
592
- it "#call raises a UserError" do
593
- expect { verifier.call({}) }.to raise_error(Kitchen::UserError)
594
- end
595
- end
596
-
597
- def create_legacy_test_directories
598
- base = File.join(config[:test_base_path], "germany")
599
- FileUtils.mkdir_p(File.join(base, "inspec"))
600
- FileUtils.mkdir_p(File.join(base, "serverspec"))
601
- end
602
-
603
- def ensure_suite_directory(suitename)
604
- suite = File.join(config[:test_base_path], suitename)
605
- FileUtils.mkdir_p(suite)
606
- end
607
- end