inspec-core 2.2.102 → 2.2.112

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.
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