inspec 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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/CHANGELOG.md +25 -7
  4. data/Rakefile +8 -2
  5. data/docs/profiles.md +9 -0
  6. data/docs/resources/aws_security_group.md.erb +19 -2
  7. data/docs/resources/gem.md.erb +24 -5
  8. data/docs/resources/mssql_session.md.erb +8 -0
  9. data/lib/inspec/plugin/v2/loader.rb +33 -7
  10. data/lib/inspec/reporters/json_automate.rb +1 -1
  11. data/lib/inspec/version.rb +1 -1
  12. data/lib/plugins/README.md +16 -0
  13. data/lib/plugins/inspec-artifact/lib/inspec-artifact.rb +12 -0
  14. data/lib/plugins/inspec-artifact/lib/inspec-artifact/base.rb +162 -0
  15. data/lib/plugins/inspec-artifact/lib/inspec-artifact/cli.rb +114 -0
  16. data/lib/plugins/inspec-artifact/test/functional/inspec_artifact_test.rb +46 -0
  17. data/lib/plugins/inspec-habitat/lib/inspec-habitat.rb +11 -0
  18. data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +39 -0
  19. data/lib/plugins/inspec-habitat/lib/inspec-habitat/profile.rb +394 -0
  20. data/lib/plugins/inspec-habitat/test/unit/profile_test.rb +184 -0
  21. data/lib/{bundles → plugins}/inspec-init/README.md +0 -0
  22. data/lib/plugins/inspec-init/lib/inspec-init.rb +12 -0
  23. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +28 -0
  24. data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +81 -0
  25. data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/README.md +0 -0
  26. data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/controls/example.rb +0 -0
  27. data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/inspec.yml +0 -0
  28. data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/libraries/.gitkeep +0 -0
  29. data/lib/plugins/inspec-init/test/functional/inspec_init_test.rb +30 -0
  30. data/lib/plugins/shared/core_plugin_test_helper.rb +50 -0
  31. data/lib/resources/aws/aws_security_group.rb +38 -6
  32. data/lib/resources/gem.rb +7 -1
  33. data/lib/resources/mssql_session.rb +4 -2
  34. metadata +21 -17
  35. data/lib/bundles/inspec-artifact.rb +0 -7
  36. data/lib/bundles/inspec-artifact/README.md +0 -1
  37. data/lib/bundles/inspec-artifact/cli.rb +0 -278
  38. data/lib/bundles/inspec-habitat.rb +0 -12
  39. data/lib/bundles/inspec-habitat/cli.rb +0 -37
  40. data/lib/bundles/inspec-habitat/log.rb +0 -10
  41. data/lib/bundles/inspec-habitat/profile.rb +0 -391
  42. data/lib/bundles/inspec-init.rb +0 -12
  43. data/lib/bundles/inspec-init/cli.rb +0 -39
  44. data/lib/bundles/inspec-init/renderer.rb +0 -79
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+ require_relative 'base'
3
+
4
+ #
5
+ # Notes:
6
+ #
7
+ # Generate keys
8
+ # The initial implementation uses 2048 bit RSA key pairs (public + private).
9
+ # Public keys must be available for a customer to install and verify an artifact.
10
+ # Private keys should be stored in a secure location and NOT be distributed.
11
+ # (They're only for creating artifacts).
12
+ #
13
+ #
14
+ # .IAF file format
15
+ # .iaf = "Inspec Artifact File", easy to rename if you'd like something more appropriate.
16
+ # The iaf file wraps a binary artifact with some metadata. The first implementation
17
+ # looks like this:
18
+ #
19
+ # INSPEC-PROFILE-1
20
+ # name_of_signing_key
21
+ # algorithm
22
+ # signature
23
+ # <empty line>
24
+ # binary-blob
25
+ # <eof>
26
+ #
27
+ # Let's look at each line:
28
+ # INSPEC-PROFILE-1:
29
+ # This is the artifact version descriptor. It should't change unless the
30
+ # format of the archive changes.
31
+ #
32
+ # name_of_signing_key
33
+ # The name of the public key that can be used to verify an artifact
34
+ #
35
+ # algorithm
36
+ # The digest used to sign, I picked SHA512 to start with.
37
+ # If we support multiple digests, we'll need to have the verify() method
38
+ # support each digest.
39
+ #
40
+ # signature
41
+ # The result of passing the binary artifact through the digest algorithm above.
42
+ # Result is base64 encoded.
43
+ #
44
+ # <empty line>
45
+ # We use an empty line to separate artifact header from artifact body (binary blob).
46
+ # The artifact body can be anything you like.
47
+ #
48
+ # binary-blob
49
+ # A binary blob, most likely a .tar.gz or tar.xz file. We'll need to pick one and
50
+ # stick with it as part of the "INSPEC-PROFILE-1" artifact version. If we change block
51
+ # format, the artifact version descriptor must be incremented, and the sign()
52
+ # and verify() methods must be updated to support a newer version.
53
+ #
54
+ #
55
+ # Key revocation
56
+ # This implementation doesn't support key revocation. However, a customer
57
+ # can remove the public cert file before installation, and artifacts will then
58
+ # fail verification.
59
+ #
60
+ # Key locations
61
+ # This implementation uses the current working directory to find public and
62
+ # private keys. We should establish a common key directory (similar to /hab/cache/keys
63
+ # or ~/.hab/cache/keys in Habitat).
64
+ #
65
+ # Extracting artifacts outside of Inspec
66
+ # As in Habitat, the artifact format for Inspec allows the use of common
67
+ # Unix tools to read the header and body of an artifact.
68
+ # To extract the header from a .iaf:
69
+ # sed '/^$/q' foo.iaf
70
+ # To extract the raw content from a .iaf:
71
+ # sed '1,/^$/d' foo.iaf
72
+
73
+ module InspecPlugins
74
+ module Artifact
75
+ class CLI < Inspec.plugin(2, :cli_command)
76
+ subcommand_desc 'artifact SUBCOMMAND', 'Manage InSpec Artifacts'
77
+
78
+ desc 'generate', 'Generate a RSA key pair for signing and verification'
79
+ option :keyname, type: :string, required: true,
80
+ desc: 'Desriptive name of key'
81
+ option :keydir, type: :string, default: './',
82
+ desc: 'Directory to search for keys'
83
+ def generate_keys
84
+ puts 'Generating keys'
85
+ InspecPlugins::Artifact::Base.keygen(options)
86
+ end
87
+
88
+ desc 'sign-profile', 'Create a signed .iaf artifact'
89
+ option :profile, type: :string, required: true,
90
+ desc: 'Path to profile directory'
91
+ option :keyname, type: :string, required: true,
92
+ desc: 'Desriptive name of key'
93
+ def sign_profile
94
+ InspecPlugins::Artifact::Base.profile_sign(options)
95
+ end
96
+
97
+ desc 'verify-profile', 'Verify a signed .iaf artifact'
98
+ option :infile, type: :string, required: true,
99
+ desc: '.iaf file to verify'
100
+ def verify_profile
101
+ InspecPlugins::Artifact::Base.profile_verify(options)
102
+ end
103
+
104
+ desc 'install-profile', 'Verify and install a signed .iaf artifact'
105
+ option :infile, type: :string, required: true,
106
+ desc: '.iaf file to install'
107
+ option :destdir, type: :string, required: true,
108
+ desc: 'Installation directory'
109
+ def install_profile
110
+ InspecPlugins::Artifact::Base.profile_install(options)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative '../../../shared/core_plugin_test_helper.rb'
4
+ require 'fileutils'
5
+ require 'securerandom'
6
+
7
+ class ArtifactCli < MiniTest::Test
8
+ include CorePluginFunctionalHelper
9
+
10
+ def test_generating_archive_keys
11
+ Dir.mktmpdir do |dir|
12
+ unique_key_name = SecureRandom.uuid()
13
+ out = run_inspec_process("artifact generate --keyname #{unique_key_name}", prefix: "cd #{dir} &&")
14
+ assert_equal 0, out.exit_status
15
+
16
+ stdout = out.stdout.force_encoding(Encoding::UTF_8)
17
+ assert_includes stdout, 'Generating private key'
18
+ assert_includes stdout, 'Generating public key'
19
+ end
20
+ end
21
+
22
+ def test_verify_and_install_signed_profile
23
+ Dir.mktmpdir do |dir|
24
+ unique_key_name = SecureRandom.uuid()
25
+ install_dir = File.join(dir, SecureRandom.uuid())
26
+ profile = File.join(dir, 'profile')
27
+ FileUtils.mkdir(install_dir)
28
+
29
+ # create profile
30
+ profile = File.join(dir, 'artifact-profile')
31
+ run_inspec_process("init profile artifact-profile", prefix: "cd #{dir} &&")
32
+
33
+ out = run_inspec_process("artifact generate --keyname #{unique_key_name}", prefix: "cd #{dir} &&")
34
+ assert_equal 0, out.exit_status
35
+
36
+ out = run_inspec_process("artifact sign-profile --profile #{profile} --keyname #{unique_key_name}", prefix: "cd #{dir} &&")
37
+ assert_equal 0, out.exit_status
38
+
39
+ out = run_inspec_process("artifact install-profile --infile artifact-profile-0.1.0.iaf --destdir #{install_dir}", prefix: "cd #{dir} &&")
40
+ assert_equal 0, out.exit_status
41
+
42
+ assert_includes out.stdout.force_encoding(Encoding::UTF_8), "Installing to #{install_dir}"
43
+ assert_includes Dir.entries(install_dir).join, 'inspec.yml'
44
+ end
45
+ end
46
+ 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,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