inspec 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 (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