inspec-core 2.2.102 → 2.2.112

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -7
  3. data/docs/profiles.md +9 -0
  4. data/docs/resources/gem.md.erb +24 -5
  5. data/docs/resources/mssql_session.md.erb +8 -0
  6. data/lib/inspec/plugin/v2/loader.rb +33 -7
  7. data/lib/inspec/reporters/json_automate.rb +1 -1
  8. data/lib/inspec/version.rb +1 -1
  9. data/lib/plugins/README.md +16 -0
  10. data/lib/plugins/inspec-artifact/lib/inspec-artifact/base.rb +162 -0
  11. data/lib/plugins/inspec-artifact/lib/inspec-artifact/cli.rb +114 -0
  12. data/lib/plugins/inspec-artifact/lib/inspec-artifact.rb +12 -0
  13. data/lib/plugins/inspec-artifact/test/functional/inspec_artifact_test.rb +46 -0
  14. data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +39 -0
  15. data/lib/plugins/inspec-habitat/lib/inspec-habitat/profile.rb +394 -0
  16. data/lib/plugins/inspec-habitat/lib/inspec-habitat.rb +11 -0
  17. data/lib/plugins/inspec-habitat/test/unit/profile_test.rb +184 -0
  18. data/lib/{bundles → plugins}/inspec-init/README.md +0 -0
  19. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +28 -0
  20. data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +81 -0
  21. data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/README.md +0 -0
  22. data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/controls/example.rb +0 -0
  23. data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/inspec.yml +0 -0
  24. data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/libraries/.gitkeep +0 -0
  25. data/lib/plugins/inspec-init/lib/inspec-init.rb +12 -0
  26. data/lib/plugins/inspec-init/test/functional/inspec_init_test.rb +30 -0
  27. data/lib/plugins/shared/core_plugin_test_helper.rb +50 -0
  28. data/lib/resources/gem.rb +7 -1
  29. data/lib/resources/mssql_session.rb +4 -2
  30. metadata +21 -17
  31. data/lib/bundles/inspec-artifact/README.md +0 -1
  32. data/lib/bundles/inspec-artifact/cli.rb +0 -278
  33. data/lib/bundles/inspec-artifact.rb +0 -7
  34. data/lib/bundles/inspec-habitat/cli.rb +0 -37
  35. data/lib/bundles/inspec-habitat/log.rb +0 -10
  36. data/lib/bundles/inspec-habitat/profile.rb +0 -391
  37. data/lib/bundles/inspec-habitat.rb +0 -12
  38. data/lib/bundles/inspec-init/cli.rb +0 -39
  39. data/lib/bundles/inspec-init/renderer.rb +0 -79
  40. data/lib/bundles/inspec-init.rb +0 -12
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ require_relative 'profile'
3
+
4
+ module InspecPlugins
5
+ module Habitat
6
+ class ProfileCLI < Inspec.plugin(2, :cli_command)
7
+ # Override banner method to correct missing subcommand.
8
+ # @see https://github.com/erikhuda/thor/issues/261
9
+ def self.banner(command, _namespace = nil, _subcommand = false)
10
+ "#{basename} habitat profile #{command.usage}"
11
+ end
12
+
13
+ desc 'create PATH', 'Create a one-time Habitat artifact for the profile found at PATH'
14
+ option :output_dir, type: :string, required: false,
15
+ desc: 'Directory in which to save the generated Habitat artifact. Default: current directory'
16
+ def create(path)
17
+ InspecPlugins::Habitat::Profile.create(path, options)
18
+ end
19
+
20
+ desc 'setup PATH', 'Configure the profile at PATH for Habitat, including a plan and hooks'
21
+ def setup(path)
22
+ InspecPlugins::Habitat::Profile.setup(path)
23
+ end
24
+
25
+ desc 'upload PATH', 'Create a one-time Habitat artifact for the profile found at PATH, and upload it to a Habitat Depot'
26
+ def upload(path)
27
+ InspecPlugins::Habitat::Profile.upload(path, options)
28
+ end
29
+ end
30
+
31
+ class CLI < Inspec.plugin(2, :cli_command)
32
+ subcommand_desc 'habitat SUBCOMMAND', 'Manage Habitat with InSpec'
33
+ namespace 'habitat'
34
+
35
+ desc 'profile', 'Manage InSpec profiles as Habitat artifacts'
36
+ subcommand 'profile', ProfileCLI
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,394 @@
1
+ # encoding: utf-8
2
+ # author: Adam Leff
3
+
4
+ require 'inspec/profile_vendor'
5
+ require 'mixlib/shellout'
6
+ require 'tomlrb'
7
+
8
+ module InspecPlugins
9
+ module Habitat
10
+ class Profile
11
+ attr_reader :options, :path, :profile
12
+
13
+ def self.create(path, options = {})
14
+ creator = new(path, options)
15
+ hart_file = creator.create
16
+ creator.copy(hart_file)
17
+ ensure
18
+ creator.delete_work_dir
19
+ end
20
+
21
+ def self.setup(path)
22
+ new(path).setup
23
+ end
24
+
25
+ def self.upload(path, options = {})
26
+ uploader = new(path, options)
27
+ uploader.upload
28
+ ensure
29
+ uploader.delete_work_dir
30
+ end
31
+
32
+ def initialize(path, options = {})
33
+ @path = path
34
+ @options = options
35
+ @cli_config = nil
36
+
37
+ log_level = options.fetch('log_level', 'info')
38
+ @log = Inspec::Log
39
+ @log.level(log_level.to_sym)
40
+ end
41
+
42
+ def create
43
+ @log.info("Creating a Habitat artifact for profile: #{path}")
44
+
45
+ validate_habitat_installed
46
+ validate_habitat_origin
47
+ create_profile_object
48
+ verify_profile
49
+ vendor_profile_dependencies
50
+ copy_profile_to_work_dir
51
+ create_habitat_directories(work_dir)
52
+ create_plan(work_dir)
53
+ create_run_hook(work_dir)
54
+ create_settings_file(work_dir)
55
+ create_default_config(work_dir)
56
+
57
+ # returns the path to the .hart file in the work directory
58
+ build_hart
59
+ rescue => e
60
+ @log.debug(e.backtrace.join("\n"))
61
+ exit_with_error(
62
+ 'Unable to generate Habitat artifact.',
63
+ "#{e.class} -- #{e.message}",
64
+ )
65
+ end
66
+
67
+ def copy(hart_file)
68
+ validate_output_dir
69
+
70
+ @log.info("Copying artifact to #{output_dir}...")
71
+ copy_hart(hart_file)
72
+ end
73
+
74
+ def upload
75
+ validate_habitat_auth_token
76
+ hart_file = create
77
+ upload_hart(hart_file)
78
+ rescue => e
79
+ @log.debug(e.backtrace.join("\n"))
80
+ exit_with_error(
81
+ 'Unable to upload Habitat artifact.',
82
+ "#{e.class} -- #{e.message}",
83
+ )
84
+ end
85
+
86
+ def delete_work_dir
87
+ @log.debug("Deleting work directory #{work_dir}")
88
+ FileUtils.rm_rf(work_dir) if Dir.exist?(work_dir)
89
+ end
90
+
91
+ def setup
92
+ @log.info("Setting up profile at #{path} for Habitat...")
93
+ create_profile_object
94
+ verify_profile
95
+ vendor_profile_dependencies
96
+ create_habitat_directories(path)
97
+ create_plan(path)
98
+ create_run_hook(path)
99
+ create_settings_file(path)
100
+ create_default_config(path)
101
+ end
102
+
103
+ private
104
+
105
+ def create_profile_object
106
+ @profile = Inspec::Profile.for_target(
107
+ path,
108
+ backend: Inspec::Backend.create(target: 'mock://'),
109
+ )
110
+ end
111
+
112
+ def verify_profile
113
+ @log.info('Checking to see if the profile is valid...')
114
+
115
+ unless profile.check[:summary][:valid]
116
+ exit_with_error('Profile check failed. Please fix the profile before creating a Habitat artifact.')
117
+ end
118
+
119
+ @log.info('Profile is valid.')
120
+ end
121
+
122
+ def vendor_profile_dependencies
123
+ profile_vendor = Inspec::ProfileVendor.new(path)
124
+ if profile_vendor.lockfile.exist? && profile_vendor.cache_path.exist?
125
+ @log.info("Profile's dependencies are already vendored, skipping vendor process.")
126
+ else
127
+ @log.info("Vendoring the profile's dependencies...")
128
+ profile_vendor.vendor!
129
+
130
+ @log.info('Ensuring all vendored content has read permissions...')
131
+ profile_vendor.make_readable
132
+
133
+ # refresh the profile object since the profile now has new files
134
+ create_profile_object
135
+ end
136
+ end
137
+
138
+ def validate_habitat_installed
139
+ @log.info('Checking to see if Habitat is installed...')
140
+ cmd = Mixlib::ShellOut.new('hab --version')
141
+ cmd.run_command
142
+ exit_with_error('Unable to run Habitat commands.', cmd.stderr) if cmd.error?
143
+ end
144
+
145
+ def validate_habitat_origin
146
+ exit_with_error(
147
+ 'Unable to determine Habitat origin name.',
148
+ 'Run `hab setup` or set the HAB_ORIGIN environment variable.',
149
+ ) if habitat_origin.nil?
150
+ end
151
+
152
+ def validate_habitat_auth_token
153
+ exit_with_error(
154
+ 'Unable to determine Habitat auth token for publishing.',
155
+ 'Run `hab setup` or set the HAB_AUTH_TOKEN environment variable.',
156
+ ) if habitat_auth_token.nil?
157
+ end
158
+
159
+ def validate_output_dir
160
+ exit_with_error("Output directory #{output_dir} is not a directory or does not exist.") unless
161
+ File.directory?(output_dir)
162
+ end
163
+
164
+ def work_dir
165
+ return @work_dir if @work_dir
166
+
167
+ @work_dir ||= Dir.mktmpdir('inspec-habitat-exporter')
168
+ @log.debug("Generated work directory #{@work_dir}")
169
+
170
+ @work_dir
171
+ end
172
+
173
+ def create_habitat_directories(parent_directory)
174
+ [
175
+ File.join(parent_directory, 'habitat'),
176
+ File.join(parent_directory, 'habitat', 'config'),
177
+ File.join(parent_directory, 'habitat', 'hooks'),
178
+ ].each do |dir|
179
+ Dir.mkdir(dir) unless Dir.exist?(dir)
180
+ end
181
+ end
182
+
183
+ def copy_profile_to_work_dir
184
+ @log.info('Copying profile contents to the work directory...')
185
+ profile.files.each do |f|
186
+ src = File.join(profile.root_path, f)
187
+ dst = File.join(work_dir, f)
188
+ if File.directory?(f)
189
+ @log.debug("Creating directory #{dst}")
190
+ FileUtils.mkdir_p(dst)
191
+ else
192
+ @log.debug("Copying file #{src} to #{dst}")
193
+ FileUtils.cp_r(src, dst)
194
+ end
195
+ end
196
+ end
197
+
198
+ def create_plan(directory)
199
+ plan_file = File.join(directory, 'habitat', 'plan.sh')
200
+ @log.info("Generating Habitat plan at #{plan_file}...")
201
+ File.write(plan_file, plan_contents)
202
+ end
203
+
204
+ def create_run_hook(directory)
205
+ run_hook_file = File.join(directory, 'habitat', 'hooks', 'run')
206
+ @log.info("Generating a Habitat run hook at #{run_hook_file}...")
207
+ File.write(run_hook_file, run_hook_contents)
208
+ end
209
+
210
+ def create_settings_file(directory)
211
+ settings_file = File.join(directory, 'habitat', 'config', 'settings.sh')
212
+ @log.info("Generating a settings file at #{settings_file}...")
213
+ File.write(settings_file, "SLEEP_TIME={{cfg.sleep_time}}\n")
214
+ end
215
+
216
+ def create_default_config(directory)
217
+ default_toml = File.join(directory, 'habitat', 'default.toml')
218
+ @log.info("Generating Habitat's default.toml configuration...")
219
+ File.write(default_toml, 'sleep_time = 300')
220
+ end
221
+
222
+ def build_hart
223
+ @log.info('Building our Habitat artifact...')
224
+
225
+ env = {
226
+ 'TERM' => 'vt100',
227
+ 'HAB_ORIGIN' => habitat_origin,
228
+ 'HAB_NONINTERACTIVE' => 'true',
229
+ }
230
+
231
+ env['RUST_LOG'] = 'debug' if @log.level == :debug
232
+
233
+ # TODO: Would love to use Mixlib::ShellOut here, but it doesn't
234
+ # seem to preserve the STDIN tty, and docker gets angry.
235
+ Dir.chdir(work_dir) do
236
+ unless system(env, 'hab pkg build .')
237
+ exit_with_error('Unable to build the Habitat artifact.')
238
+ end
239
+ end
240
+
241
+ hart_files = Dir.glob(File.join(work_dir, 'results', '*.hart'))
242
+
243
+ if hart_files.length > 1
244
+ exit_with_error('More than one Habitat artifact was created which was not expected.')
245
+ elsif hart_files.empty?
246
+ exit_with_error('No Habitat artifact was created.')
247
+ end
248
+
249
+ hart_files.first
250
+ end
251
+
252
+ def copy_hart(working_dir_hart)
253
+ hart_basename = File.basename(working_dir_hart)
254
+ dst = File.join(output_dir, hart_basename)
255
+ FileUtils.cp(working_dir_hart, dst)
256
+
257
+ dst
258
+ end
259
+
260
+ def upload_hart(hart_file)
261
+ @log.info('Uploading the Habitat artifact to our Depot...')
262
+
263
+ env = {
264
+ 'TERM' => 'vt100',
265
+ 'HAB_AUTH_TOKEN' => habitat_auth_token,
266
+ 'HAB_NONINTERACTIVE' => 'true',
267
+ }
268
+
269
+ env['HAB_DEPOT_URL'] = ENV['HAB_DEPOT_URL'] if ENV['HAB_DEPOT_URL']
270
+
271
+ cmd = Mixlib::ShellOut.new("hab pkg upload #{hart_file}", env: env)
272
+ cmd.run_command
273
+ if cmd.error?
274
+ exit_with_error(
275
+ 'Unable to upload Habitat artifact to the Depot.',
276
+ cmd.stdout,
277
+ cmd.stderr,
278
+ )
279
+ end
280
+
281
+ @log.info('Upload complete!')
282
+ end
283
+
284
+ def habitat_origin
285
+ ENV['HAB_ORIGIN'] || habitat_cli_config['origin']
286
+ end
287
+
288
+ def habitat_auth_token
289
+ ENV['HAB_AUTH_TOKEN'] || habitat_cli_config['auth_token']
290
+ end
291
+
292
+ def habitat_cli_config
293
+ return @cli_config if @cli_config
294
+
295
+ config_file = File.join(ENV['HOME'], '.hab', 'etc', 'cli.toml')
296
+ return {} unless File.exist?(config_file)
297
+
298
+ @cli_config = Tomlrb.load_file(config_file)
299
+ end
300
+
301
+ def output_dir
302
+ options[:output_dir] || Dir.pwd
303
+ end
304
+
305
+ def exit_with_error(*errors)
306
+ errors.each do |error_msg|
307
+ @log.error(error_msg)
308
+ end
309
+
310
+ exit 1
311
+ end
312
+
313
+ def package_name
314
+ "inspec-profile-#{profile.name}"
315
+ end
316
+
317
+ def plan_contents
318
+ plan = <<~EOL
319
+ pkg_name=#{package_name}
320
+ pkg_version=#{profile.version}
321
+ pkg_origin=#{habitat_origin}
322
+ pkg_deps=(chef/inspec core/ruby core/hab)
323
+ pkg_svc_user=root
324
+ EOL
325
+
326
+ plan += "pkg_license='#{profile.metadata.params[:license]}'\n\n" if profile.metadata.params[:license]
327
+
328
+ plan += <<~EOL
329
+
330
+ do_build() {
331
+ cp -vr $PLAN_CONTEXT/../* $HAB_CACHE_SRC_PATH/$pkg_dirname
332
+ }
333
+
334
+ do_install() {
335
+ local profile_contents
336
+ local excludes
337
+ profile_contents=($(ls))
338
+ excludes=(habitat results *.hart)
339
+
340
+ for item in ${excludes[@]}; do
341
+ profile_contents=(${profile_contents[@]/$item/})
342
+ done
343
+
344
+ mkdir ${pkg_prefix}/dist
345
+ cp -r ${profile_contents[@]} ${pkg_prefix}/dist/
346
+ }
347
+ EOL
348
+
349
+ plan
350
+ end
351
+
352
+ def run_hook_contents
353
+ <<~EOL
354
+ #!/bin/sh
355
+
356
+ # redirect stderr to stdout
357
+ # ultimately, we'd like to log this somewhere useful, but due to
358
+ # https://github.com/habitat-sh/habitat/issues/2395, we need to
359
+ # avoid doing that for now.
360
+ exec 2>&1
361
+
362
+ # InSpec will try to create a .cache directory in the user's home directory
363
+ # so this needs to be someplace writeable by the hab user
364
+ export HOME={{pkg.svc_var_path}}
365
+
366
+ PROFILE_IDENT="{{pkg.origin}}/{{pkg.name}}"
367
+ RESULTS_DIR="{{pkg.svc_var_path}}/inspec_results"
368
+ RESULTS_FILE="${RESULTS_DIR}/{{pkg.name}}.json"
369
+
370
+ # Create a directory for inspec formatter output
371
+ mkdir -p {{pkg.svc_var_path}}/inspec_results
372
+
373
+ while true; do
374
+ echo "Executing InSpec for ${PROFILE_IDENT}"
375
+ inspec exec {{pkg.path}}/dist --format=json > ${RESULTS_FILE}
376
+
377
+ if [ $? -eq 0 ]; then
378
+ echo "InSpec run completed successfully."
379
+ else
380
+ echo "InSpec run did not complete successfully. If you do not see any errors above,"
381
+ echo "control failures were detected. Check the InSpec results here for details:"
382
+ echo ${RESULTS_FILE}
383
+ echo "Otherwise, troubleshoot any errors shown above."
384
+ fi
385
+
386
+ source {{pkg.svc_config_path}}/settings.sh
387
+ echo "sleeping for ${SLEEP_TIME} seconds"
388
+ sleep ${SLEEP_TIME}
389
+ done
390
+ EOL
391
+ end
392
+ end
393
+ end
394
+ end
@@ -0,0 +1,11 @@
1
+ module InspecPlugins
2
+ module Habitat
3
+ class Plugin < Inspec.plugin(2)
4
+ plugin_name :'inspec-habitat'
5
+ cli_command :habitat do
6
+ require_relative 'inspec-habitat/cli'
7
+ InspecPlugins::Habitat::CLI
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,184 @@
1
+ require 'mixlib/log'
2
+ require 'ostruct'
3
+ require 'minitest/autorun'
4
+ require 'mocha/setup'
5
+ require_relative '../../lib/inspec-habitat/profile.rb'
6
+
7
+ describe InspecPlugins::Habitat::Profile do
8
+ let(:profile) do
9
+ OpenStruct.new(
10
+ name: 'my_profile',
11
+ version: '1.2.3',
12
+ files: %w(file1 file2)
13
+ )
14
+ end
15
+
16
+ let(:subject) { InspecPlugins::Habitat::Profile.new('/path/to/profile', { 'log_level' => 'fatal' }) }
17
+
18
+ before do
19
+ Inspec::Log.level(:fatal)
20
+ end
21
+
22
+ describe '#verify_profile' do
23
+ it 'exits if the profile is not valid' do
24
+ profile = mock
25
+ profile.stubs(:check).returns(summary: { valid: false })
26
+ subject.expects(:profile).returns(profile)
27
+ proc { subject.send(:verify_profile) }.must_raise SystemExit
28
+ end
29
+
30
+ it 'does not exist if the profile is valid' do
31
+ profile = mock
32
+ profile.stubs(:check).returns(summary: { valid: true })
33
+ subject.expects(:profile).returns(profile)
34
+ subject.send(:verify_profile)
35
+ end
36
+ end
37
+
38
+ describe '#vendor_profile_dependencies' do
39
+ let(:profile_vendor) do
40
+ profile_vendor = mock
41
+ profile_vendor.stubs(:lockfile).returns(lockfile)
42
+ profile_vendor.stubs(:cache_path).returns(cache_path)
43
+ profile_vendor
44
+ end
45
+ let(:lockfile) { mock }
46
+ let(:cache_path) { mock }
47
+
48
+ before do
49
+ Inspec::ProfileVendor.expects(:new).returns(profile_vendor)
50
+ end
51
+
52
+ describe 'when lockfile exists and cache dir exists' do
53
+ it 'does not vendor the dependencies' do
54
+ lockfile.expects(:exist?).returns(true)
55
+ cache_path.expects(:exist?).returns(true)
56
+ profile_vendor.expects(:vendor!).never
57
+ profile_vendor.expects(:make_readable).never
58
+ subject.send(:vendor_profile_dependencies)
59
+ end
60
+ end
61
+
62
+ describe 'when the lockfile exists but the cache dir does not' do
63
+ it 'vendors the dependencies and refreshes the profile object' do
64
+ lockfile.expects(:exist?).returns(true)
65
+ cache_path.expects(:exist?).returns(false)
66
+ profile_vendor.expects(:vendor!)
67
+ profile_vendor.expects(:make_readable)
68
+ subject.expects(:create_profile_object)
69
+
70
+ subject.send(:vendor_profile_dependencies)
71
+ end
72
+ end
73
+
74
+ describe 'when the lockfile does not exist' do
75
+ it 'vendors the dependencies and refreshes the profile object' do
76
+ lockfile.expects(:exist?).returns(false)
77
+ profile_vendor.expects(:vendor!)
78
+ profile_vendor.expects(:make_readable)
79
+ subject.expects(:create_profile_object)
80
+
81
+ subject.send(:vendor_profile_dependencies)
82
+ end
83
+ end
84
+ end
85
+
86
+ describe '#validate_habitat_installed' do
87
+ it 'exits if hab --version fails' do
88
+ cmd = mock
89
+ cmd.stubs(:error?).returns(true)
90
+ cmd.stubs(:run_command)
91
+ cmd.stubs(:stdout)
92
+ cmd.stubs(:stderr)
93
+ Mixlib::ShellOut.expects(:new).with('hab --version').returns(cmd)
94
+ proc { subject.send(:validate_habitat_installed) }.must_raise SystemExit
95
+ end
96
+ end
97
+
98
+ describe '#validate_habitat_origin' do
99
+ it 'does not exit if the origin key exists' do
100
+ subject.expects(:habitat_origin).returns('12345')
101
+ subject.send(:validate_habitat_origin)
102
+ end
103
+
104
+ it 'exits if no origin key exists' do
105
+ subject.expects(:habitat_origin).returns(nil)
106
+ proc { subject.send(:validate_habitat_origin) }.must_raise SystemExit
107
+ end
108
+ end
109
+
110
+ describe '#validate_habitat_auth_token' do
111
+ it 'does not exit if the auth_token exists' do
112
+ subject.expects(:habitat_auth_token).returns('12345')
113
+ subject.send(:validate_habitat_auth_token)
114
+ end
115
+
116
+ it 'exits if no auth_token exists' do
117
+ subject.expects(:habitat_auth_token).returns(nil)
118
+ proc { subject.send(:validate_habitat_auth_token) }.must_raise SystemExit
119
+ end
120
+ end
121
+
122
+ describe '#build_hart' do
123
+ before do
124
+ subject.expects(:work_dir).at_least_once.returns(Dir.tmpdir)
125
+ end
126
+
127
+ it 'exits if the build fails' do
128
+ subject.expects(:system).returns(false)
129
+ proc { subject.send(:build_hart) }.must_raise SystemExit
130
+ end
131
+
132
+ it 'exits if more than one hart is created' do
133
+ subject.expects(:system).returns(true)
134
+ Dir.expects(:glob).returns(%w(hart1 hart2))
135
+ proc { subject.send(:build_hart) }.must_raise SystemExit
136
+ end
137
+
138
+ it 'exits if more than no hart is created' do
139
+ subject.expects(:system).returns(true)
140
+ Dir.expects(:glob).returns([])
141
+ proc { subject.send(:build_hart) }.must_raise SystemExit
142
+ end
143
+
144
+ it 'returns the hart filename' do
145
+ subject.expects(:system).returns(true)
146
+ Dir.expects(:glob).returns(%w(hart1))
147
+ subject.send(:build_hart).must_equal('hart1')
148
+ end
149
+ end
150
+
151
+ describe '#upload_hart' do
152
+ it 'exits if the upload failed' do
153
+ env = {
154
+ 'TERM' => 'vt100',
155
+ 'HAB_AUTH_TOKEN' => 'my_token',
156
+ 'HAB_NONINTERACTIVE' => 'true',
157
+ }
158
+
159
+ cmd = mock
160
+ cmd.stubs(:run_command)
161
+ cmd.stubs(:error?).returns(true)
162
+ cmd.stubs(:stdout)
163
+ cmd.stubs(:stderr)
164
+
165
+ subject.expects(:habitat_auth_token).returns('my_token')
166
+ Mixlib::ShellOut.expects(:new).with("hab pkg upload my_hart", env: env).returns(cmd)
167
+ proc { subject.send(:upload_hart, 'my_hart') }.must_raise SystemExit
168
+ end
169
+ end
170
+
171
+ describe '#habitat_cli_config' do
172
+ it 'returns an empty hash if the CLI config does not exist' do
173
+ File.expects(:exist?).with(File.join(ENV['HOME'], '.hab', 'etc', 'cli.toml')).returns(false)
174
+ subject.send(:habitat_cli_config).must_equal({})
175
+ end
176
+
177
+ it 'returns parsed TOML from the hab config file' do
178
+ config_file = File.join(ENV['HOME'], '.hab', 'etc', 'cli.toml')
179
+ File.expects(:exist?).with(config_file).returns(true)
180
+ Tomlrb.expects(:load_file).with(config_file).returns(foo: 1)
181
+ subject.send(:habitat_cli_config).must_equal(foo: 1)
182
+ end
183
+ end
184
+ end
File without changes
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ require 'pathname'
4
+ require_relative 'renderer'
5
+
6
+ module InspecPlugins
7
+ module Init
8
+ class CLI < Inspec.plugin(2, :cli_command)
9
+ subcommand_desc 'init SUBCOMMAND', 'Initialize InSpec objects'
10
+
11
+ # Look in the 'template' directory, and register a subcommand
12
+ # for each template directory found there.
13
+ template_dir = File.join(File.dirname(__FILE__), 'templates')
14
+ Dir.glob(File.join(template_dir, '*')) do |template|
15
+ template_name = Pathname.new(template).relative_path_from(Pathname.new(template_dir)).to_s
16
+
17
+ # register command for the template
18
+ desc "#{template_name} NAME", "Create a new #{template_name}"
19
+ option :overwrite, type: :boolean, default: false,
20
+ desc: 'Overwrites existing directory'
21
+ define_method template_name.to_sym do |name_for_new_structure|
22
+ renderer = InspecPlugins::Init::Renderer.new(self, options)
23
+ renderer.render_with_values(template_name, name: name_for_new_structure)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end