inspec 3.7.1 → 3.7.11

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 (178) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -2
  3. data/lib/inspec/config.rb +12 -0
  4. data/lib/inspec/shell.rb +2 -15
  5. data/lib/inspec/version.rb +1 -1
  6. data/lib/plugins/inspec-habitat/Berksfile +5 -0
  7. data/lib/plugins/inspec-habitat/README.md +150 -0
  8. data/lib/plugins/inspec-habitat/kitchen.yml +28 -0
  9. data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +9 -9
  10. data/lib/plugins/inspec-habitat/lib/inspec-habitat/profile.rb +164 -280
  11. data/lib/plugins/inspec-habitat/templates/habitat/config/inspec_exec_config.json.erb +25 -0
  12. data/lib/plugins/inspec-habitat/templates/habitat/default.toml.erb +9 -0
  13. data/lib/plugins/inspec-habitat/templates/habitat/hooks/run.erb +32 -0
  14. data/lib/plugins/inspec-habitat/templates/habitat/plan.sh.erb +85 -0
  15. data/lib/plugins/inspec-habitat/test/cookbooks/inspec_habitat_fixture/Berksfile +2 -0
  16. data/lib/plugins/inspec-habitat/test/cookbooks/inspec_habitat_fixture/README.md +3 -0
  17. data/lib/plugins/inspec-habitat/test/cookbooks/inspec_habitat_fixture/files/hab_setup.exp +28 -0
  18. data/lib/plugins/inspec-habitat/test/cookbooks/inspec_habitat_fixture/metadata.rb +9 -0
  19. data/lib/plugins/inspec-habitat/test/cookbooks/inspec_habitat_fixture/recipes/default.rb +61 -0
  20. data/lib/plugins/inspec-habitat/test/functional/inspec_habitat_test.rb +38 -0
  21. data/lib/plugins/inspec-habitat/test/integration/default/inspec_habitat/README.md +3 -0
  22. data/lib/plugins/inspec-habitat/test/integration/default/inspec_habitat/controls/inspec_habitat.rb +40 -0
  23. data/lib/plugins/inspec-habitat/test/integration/default/inspec_habitat/inspec.yml +10 -0
  24. data/lib/plugins/inspec-habitat/test/support/example_profile/README.md +3 -0
  25. data/lib/plugins/inspec-habitat/test/support/example_profile/controls/example.rb +7 -0
  26. data/lib/plugins/inspec-habitat/test/support/example_profile/inspec.yml +10 -0
  27. data/lib/plugins/inspec-habitat/test/unit/profile_test.rb +188 -132
  28. data/lib/plugins/inspec-init/templates/profiles/azure/README.md +56 -0
  29. data/lib/plugins/inspec-init/templates/profiles/azure/controls/example.rb +15 -0
  30. data/lib/plugins/inspec-init/templates/profiles/azure/inspec.yml +14 -0
  31. data/lib/plugins/inspec-init/templates/profiles/azure/libraries/.gitkeep +0 -0
  32. data/lib/plugins/inspec-init/test/functional/inspec_init_profile_test.rb +12 -0
  33. data/lib/resources/aide_conf.rb +2 -2
  34. data/lib/resources/apache.rb +2 -2
  35. data/lib/resources/apache_conf.rb +2 -2
  36. data/lib/resources/apt.rb +2 -2
  37. data/lib/resources/audit_policy.rb +2 -2
  38. data/lib/resources/auditd.rb +2 -2
  39. data/lib/resources/auditd_conf.rb +2 -2
  40. data/lib/resources/aws/aws_billing_report.rb +3 -2
  41. data/lib/resources/aws/aws_billing_reports.rb +11 -10
  42. data/lib/resources/aws/aws_cloudtrail_trail.rb +2 -2
  43. data/lib/resources/aws/aws_cloudtrail_trails.rb +2 -2
  44. data/lib/resources/aws/aws_cloudwatch_alarm.rb +9 -9
  45. data/lib/resources/aws/aws_cloudwatch_log_metric_filter.rb +18 -18
  46. data/lib/resources/aws/aws_config_delivery_channel.rb +2 -2
  47. data/lib/resources/aws/aws_config_recorder.rb +2 -2
  48. data/lib/resources/aws/aws_ebs_volume.rb +2 -2
  49. data/lib/resources/aws/aws_ebs_volumes.rb +2 -2
  50. data/lib/resources/aws/aws_ec2_instance.rb +2 -2
  51. data/lib/resources/aws/aws_ec2_instances.rb +2 -2
  52. data/lib/resources/aws/aws_ecs_cluster.rb +2 -2
  53. data/lib/resources/aws/aws_eks_cluster.rb +2 -2
  54. data/lib/resources/aws/aws_elb.rb +2 -2
  55. data/lib/resources/aws/aws_elbs.rb +2 -2
  56. data/lib/resources/aws/aws_flow_log.rb +2 -2
  57. data/lib/resources/aws/aws_iam_access_key.rb +2 -2
  58. data/lib/resources/aws/aws_iam_access_keys.rb +2 -2
  59. data/lib/resources/aws/aws_iam_group.rb +2 -2
  60. data/lib/resources/aws/aws_iam_groups.rb +2 -2
  61. data/lib/resources/aws/aws_iam_password_policy.rb +2 -2
  62. data/lib/resources/aws/aws_iam_policies.rb +2 -2
  63. data/lib/resources/aws/aws_iam_policy.rb +2 -2
  64. data/lib/resources/aws/aws_iam_role.rb +2 -2
  65. data/lib/resources/aws/aws_iam_root_user.rb +2 -2
  66. data/lib/resources/aws/aws_iam_user.rb +2 -2
  67. data/lib/resources/aws/aws_iam_users.rb +2 -2
  68. data/lib/resources/aws/aws_kms_key.rb +2 -2
  69. data/lib/resources/aws/aws_kms_keys.rb +2 -2
  70. data/lib/resources/aws/aws_rds_instance.rb +2 -2
  71. data/lib/resources/aws/aws_route_table.rb +2 -2
  72. data/lib/resources/aws/aws_route_tables.rb +2 -2
  73. data/lib/resources/aws/aws_s3_bucket.rb +2 -2
  74. data/lib/resources/aws/aws_s3_bucket_object.rb +2 -2
  75. data/lib/resources/aws/aws_s3_buckets.rb +2 -2
  76. data/lib/resources/aws/aws_security_group.rb +5 -5
  77. data/lib/resources/aws/aws_security_groups.rb +2 -2
  78. data/lib/resources/aws/aws_sns_subscription.rb +2 -2
  79. data/lib/resources/aws/aws_sns_topic.rb +2 -2
  80. data/lib/resources/aws/aws_sns_topics.rb +2 -2
  81. data/lib/resources/aws/aws_sqs_queue.rb +2 -2
  82. data/lib/resources/aws/aws_subnet.rb +2 -2
  83. data/lib/resources/aws/aws_subnets.rb +2 -2
  84. data/lib/resources/aws/aws_vpc.rb +2 -2
  85. data/lib/resources/aws/aws_vpcs.rb +2 -2
  86. data/lib/resources/bash.rb +2 -2
  87. data/lib/resources/bond.rb +2 -2
  88. data/lib/resources/bridge.rb +2 -2
  89. data/lib/resources/chocolatey_package.rb +2 -2
  90. data/lib/resources/command.rb +2 -2
  91. data/lib/resources/cpan.rb +2 -2
  92. data/lib/resources/cran.rb +2 -2
  93. data/lib/resources/crontab.rb +2 -2
  94. data/lib/resources/csv.rb +2 -2
  95. data/lib/resources/dh_params.rb +2 -2
  96. data/lib/resources/directory.rb +2 -2
  97. data/lib/resources/docker.rb +2 -2
  98. data/lib/resources/docker_container.rb +2 -2
  99. data/lib/resources/docker_image.rb +2 -2
  100. data/lib/resources/docker_plugin.rb +2 -2
  101. data/lib/resources/docker_service.rb +2 -2
  102. data/lib/resources/elasticsearch.rb +2 -2
  103. data/lib/resources/etc_fstab.rb +2 -2
  104. data/lib/resources/etc_group.rb +2 -2
  105. data/lib/resources/etc_hosts.rb +2 -2
  106. data/lib/resources/etc_hosts_allow_deny.rb +4 -4
  107. data/lib/resources/file.rb +2 -2
  108. data/lib/resources/filesystem.rb +2 -2
  109. data/lib/resources/firewalld.rb +2 -2
  110. data/lib/resources/gem.rb +2 -2
  111. data/lib/resources/groups.rb +4 -4
  112. data/lib/resources/grub_conf.rb +2 -2
  113. data/lib/resources/host.rb +2 -2
  114. data/lib/resources/http.rb +25 -5
  115. data/lib/resources/iis_app.rb +2 -2
  116. data/lib/resources/iis_app_pool.rb +6 -3
  117. data/lib/resources/iis_site.rb +4 -4
  118. data/lib/resources/inetd_conf.rb +2 -2
  119. data/lib/resources/ini.rb +2 -2
  120. data/lib/resources/interface.rb +2 -2
  121. data/lib/resources/iptables.rb +2 -2
  122. data/lib/resources/json.rb +2 -3
  123. data/lib/resources/kernel_module.rb +17 -18
  124. data/lib/resources/kernel_parameter.rb +2 -2
  125. data/lib/resources/key_rsa.rb +2 -2
  126. data/lib/resources/ksh.rb +2 -2
  127. data/lib/resources/limits_conf.rb +2 -2
  128. data/lib/resources/login_def.rb +2 -2
  129. data/lib/resources/mount.rb +2 -2
  130. data/lib/resources/mssql_session.rb +2 -2
  131. data/lib/resources/mysql_conf.rb +2 -2
  132. data/lib/resources/mysql_session.rb +2 -2
  133. data/lib/resources/nginx.rb +2 -2
  134. data/lib/resources/nginx_conf.rb +2 -2
  135. data/lib/resources/npm.rb +2 -2
  136. data/lib/resources/ntp_conf.rb +2 -2
  137. data/lib/resources/oneget.rb +2 -2
  138. data/lib/resources/oracledb_session.rb +2 -2
  139. data/lib/resources/os.rb +2 -2
  140. data/lib/resources/os_env.rb +2 -2
  141. data/lib/resources/package.rb +2 -2
  142. data/lib/resources/packages.rb +2 -2
  143. data/lib/resources/parse_config.rb +4 -4
  144. data/lib/resources/passwd.rb +2 -2
  145. data/lib/resources/pip.rb +2 -2
  146. data/lib/resources/platform.rb +2 -2
  147. data/lib/resources/port.rb +2 -2
  148. data/lib/resources/postgres_conf.rb +2 -2
  149. data/lib/resources/postgres_hba_conf.rb +2 -2
  150. data/lib/resources/postgres_ident_conf.rb +2 -2
  151. data/lib/resources/postgres_session.rb +2 -2
  152. data/lib/resources/powershell.rb +2 -2
  153. data/lib/resources/processes.rb +2 -2
  154. data/lib/resources/rabbitmq_conf.rb +2 -2
  155. data/lib/resources/registry_key.rb +2 -2
  156. data/lib/resources/security_identifier.rb +2 -2
  157. data/lib/resources/security_policy.rb +2 -2
  158. data/lib/resources/service.rb +14 -14
  159. data/lib/resources/shadow.rb +2 -2
  160. data/lib/resources/ssh_conf.rb +4 -4
  161. data/lib/resources/ssl.rb +2 -2
  162. data/lib/resources/sys_info.rb +2 -2
  163. data/lib/resources/toml.rb +2 -2
  164. data/lib/resources/users.rb +4 -4
  165. data/lib/resources/vbscript.rb +2 -2
  166. data/lib/resources/virtualization.rb +2 -2
  167. data/lib/resources/windows_feature.rb +2 -2
  168. data/lib/resources/windows_hotfix.rb +2 -2
  169. data/lib/resources/windows_task.rb +2 -2
  170. data/lib/resources/wmi.rb +2 -2
  171. data/lib/resources/x509_certificate.rb +2 -2
  172. data/lib/resources/xinetd.rb +2 -2
  173. data/lib/resources/xml.rb +2 -2
  174. data/lib/resources/yaml.rb +2 -2
  175. data/lib/resources/yum.rb +2 -2
  176. data/lib/resources/zfs_dataset.rb +2 -2
  177. data/lib/resources/zfs_pool.rb +2 -2
  178. metadata +26 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5fac8050f287e7724b43ec98cc5d315849aeabe1ba832068c30eab5d1c2e990f
4
- data.tar.gz: 5779e58ee9fb65084d06c3a118fff3f9f99083436f747a60230c34a857a6e71f
3
+ metadata.gz: 0cac247e4c431779ae6dde11e5d4a8ad35a0a06dcd98a267ccb6e8dad37bda98
4
+ data.tar.gz: 33924d693f865466f4ad6c97b8601a8b87f1e81a16877a6454c2a5749cac26be
5
5
  SHA512:
6
- metadata.gz: f13bf3144f139198a8443ac4db6e8e0bb1c3ac38cc3c2aaec9169544d90133991888d25f98c74ff3634440fd269cf9ddfb3231b69129c7d6aedf69ef934ff335
7
- data.tar.gz: 5f6587a9d8716b90b79d024f3d486a76ae8f77ec85ab72b4e0f1232261b7c0a88cf399d35c5e59ee52559d8ef946e2a6a4c9dd4ac23dd95dfb195a5c7865d419
6
+ metadata.gz: ed07328de84c51b24bcb45dd551dd3853baa7ac6b5de061782062e06bd41e3badc5262259e2d8ce8e887c6f26562712e1c65d8d1d2102d34f04cc189f7239add
7
+ data.tar.gz: 3270590d5506f73aca604f9bd9fb4618a4e478708925cac9cd558a400aeb745d511e7cd07e57b644c1c3d9dbb3876520755cd84886c3e4de873dd39b457f2fc7
data/Gemfile CHANGED
@@ -1,11 +1,17 @@
1
1
  # encoding: utf-8
2
2
  source 'https://rubygems.org'
3
3
 
4
- gemspec name: 'inspec'
4
+ gem 'inspec', path: '.'
5
5
 
6
6
  gem 'ffi', '>= 1.9.14'
7
7
  gem 'aws-sdk', '~> 2'
8
8
 
9
+ group :omnibus do
10
+ gem 'rb-readline'
11
+ gem 'appbundler'
12
+ gem 'unf_ext', git: 'https://github.com/chef/ruby-unf_ext.git', ref: '55d872fb15f3a26e4ee8f8dbe7ba859c80d40383'
13
+ end
14
+
9
15
  group :test do
10
16
  gem 'minitest', '~> 5.5'
11
17
  gem 'rake', '>= 10'
@@ -36,7 +42,6 @@ end
36
42
 
37
43
  group :tools do
38
44
  gem 'pry', '~> 0.10'
39
- gem 'rb-readline'
40
45
  gem 'license_finder'
41
46
  gem 'git', '~> 1.4'
42
47
  end
@@ -30,6 +30,17 @@ module Inspec
30
30
  Inspec::Config.new({ backend: :mock }.merge(opts), StringIO.new('{}'))
31
31
  end
32
32
 
33
+ # Use this to get a cached version of the config. This prevents you from
34
+ # being required to pass it around everywhere.
35
+ def self.cached
36
+ @cached_config
37
+ end
38
+
39
+ def self.cached=(cfg)
40
+ @cached_config ||= cfg
41
+ end
42
+
43
+ # This gets called when the first config is created.
33
44
  def initialize(cli_opts = {}, cfg_io = nil, command_name = nil)
34
45
  @command_name = command_name || (ARGV.empty? ? nil : ARGV[0].to_sym)
35
46
  @defaults = Defaults.for_command(@command_name)
@@ -40,6 +51,7 @@ module Inspec
40
51
 
41
52
  @merged_options = merge_options
42
53
  @final_options = finalize_options
54
+ self.class.cached = self
43
55
  end
44
56
 
45
57
  def diagnose
@@ -80,19 +80,6 @@ module Inspec
80
80
  "\e[1m\e[39m#{x}\e[0m"
81
81
  end
82
82
 
83
- def print_example(example)
84
- # determine min whitespace that can be removed
85
- min = nil
86
- example.lines.each do |line|
87
- if !line.strip.empty? # ignore empty lines
88
- line_whitespace = line.length - line.lstrip.length
89
- min = line_whitespace if min.nil? || line_whitespace < min
90
- end
91
- end
92
- # remove whitespace from each line
93
- example.gsub(/\n\s{#{min}}/, "\n")
94
- end
95
-
96
83
  def intro
97
84
  puts 'Welcome to the interactive InSpec Shell'
98
85
  puts "To find out how to use it, type: #{mark 'help'}"
@@ -142,8 +129,8 @@ module Inspec
142
129
  end
143
130
 
144
131
  unless topic_info.example.nil?
145
- info += "#{mark 'Example:'}\n"
146
- info += "#{print_example(topic_info.example)}\n\n"
132
+ info += "#{mark 'Example:'}\n\n"
133
+ info += "#{topic_info.example}\n\n"
147
134
  end
148
135
 
149
136
  info += "#{mark 'Web Reference:'}\n\n"
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '3.7.1'
7
+ VERSION = '3.7.11'
8
8
  end
@@ -0,0 +1,5 @@
1
+ source 'https://supermarket.chef.io'
2
+
3
+ group :integration do
4
+ cookbook 'inspec_habitat_fixture', path: 'test/cookbooks/inspec_habitat_fixture/'
5
+ end
@@ -0,0 +1,150 @@
1
+ # InSpec Habitat Plugin
2
+
3
+ ## Summary
4
+
5
+ This plugin allows you to do the following:
6
+ 1. Add Habitat configuration to a profile
7
+ 2. Create/Upload a Habitat package from an InSpec profile
8
+
9
+ Creating a [Habitat](https://www.habitat.sh/) package from an InSpec profile
10
+ allows you to execute that profile as a service (via a Habitat Supervisor) on
11
+ any Linux based platform.
12
+
13
+ When running as a service, an InSpec report will be created in JSON format (by
14
+ default at `/hab/svc/YOUR_SERVICE/logs/inspec_last_run.json`). Additionally, a
15
+ log of the last run will be located at
16
+ `/hab/svc/YOUR_SERVICE/logs/inspec_log.txt` and CLI output is viewable in
17
+ the Supervisor logs by default. You can also configure this service to report
18
+ to [Chef Automate](https://www.chef.io/automate/).
19
+
20
+ See below for usage instructions.
21
+
22
+ ## Plugin Usage
23
+
24
+ ### Adding Habitat Configuration to an InSpec Profile
25
+
26
+ Run the following command:
27
+
28
+ ```
29
+ inspec habitat profile setup PATH
30
+ ```
31
+
32
+ This will create the following files:
33
+ - habitat/plan.sh (Provides build time instructions to Habitat)
34
+ - habitat/default.toml (Used to configure the running Habitat service)
35
+ - habitat/hooks/run (Shell script to execute this profile as a service)
36
+ - habitat/config/inspec_exec_config.json (JSON for `inspec exec` CLI options)
37
+
38
+ ### Creating a Habitat Package
39
+
40
+ > This command requires Habitat to be installed and configured. For instructions
41
+ on how to do that see [here](https://www.habitat.sh/docs/install-habitat/).
42
+
43
+ Run the following command:
44
+
45
+ ```
46
+ inspec habitat profile create PATH
47
+ ```
48
+
49
+ This command will:
50
+ - Create a Habitat artifact (`.hart` file).
51
+
52
+ > NOTE: If you are fetching packages from Chef Automate see
53
+ [below](#Integrating-with-Chef-Automate).
54
+
55
+ ### Uploading a Habitat Package
56
+
57
+ > This command requires Habitat to be installed and configured. For instructions
58
+ on how to do that see [here](https://www.habitat.sh/docs/install-habitat/).
59
+
60
+ Run the following command:
61
+
62
+ ```
63
+ inspec habitat profile upload PATH
64
+ ```
65
+
66
+ This command will:
67
+ - Create a Habitat artifact (`.hart` file).
68
+ - Upload the Habitat artifact to [bldr.habitat.sh](bldr.habitat.sh).
69
+
70
+ > NOTE: If you are fetching packages from Chef Automate see
71
+ [below](#Integrating-with-Chef-Automate).
72
+
73
+ ## Habitat Package Usage
74
+
75
+ > This command requires Habitat to be installed and configured. For instructions
76
+ on how to do that see [here](https://www.habitat.sh/docs/install-habitat/).
77
+
78
+ General usage instructions for using Habitat packages can be found
79
+ [here](https://www.habitat.sh/docs/using-habitat/#Using-Habitat-Packages).
80
+
81
+ Installing the package from a HART file:
82
+
83
+ ```
84
+ # See Habitat docs for more info. The below is for testing only.
85
+ hab pkg install PATH_TO_CREATED_HART_FILE
86
+ hab sup run YOUR_ORIGIN/inspec-profile-YOUR_PROFILE_NAME
87
+ ```
88
+
89
+ Installing the package from the Public Builder Depot:
90
+
91
+ ```
92
+ # See Habitat docs for more info. The below is for testing only.
93
+ hab pkg install YOUR_ORIGIN/inspec-profile-YOUR_PROFILE_NAME
94
+ hab sup run YOUR_ORIGIN/inspec-profile-YOUR_PROFILE_NAME
95
+ ```
96
+
97
+ ## Integrating with Chef Automate
98
+
99
+ ### Fetching Profiles from Chef Automate During Build
100
+
101
+ Fetching profiles from Chef Automate requires authentication.
102
+
103
+ Run the following commands prior to creating/uploading your Habitat package:
104
+
105
+ ```
106
+ # Remove -k if you are not using a self-signed certificate
107
+ inspec compliance login -k --user USER --token API_TOKEN https://AUTOMATE_FQDN
108
+ export HAB_STUDIO_SECRET_COMPLIANCE_CREDS=$(cat ~/.inspec/compliance/config.json)
109
+ ```
110
+
111
+ ### Sending InSpec Reports to Chef Automate
112
+
113
+ After running your Habitat package as a service you can configure it to report
114
+ to Chef Automate via a
115
+ [configuration update](https://www.habitat.sh/docs/using-habitat/#config-updates).
116
+
117
+ For example, create a TOML file (config.toml) that matches the below:
118
+
119
+ ```
120
+ [automate]
121
+ url = 'https://chef-automate.test'
122
+ token = 'TOKEN'
123
+ user = 'admin'
124
+ ```
125
+
126
+ Then apply it like so:
127
+
128
+ ```
129
+ # The '1' here is the config version (increment this with each change)
130
+ hab config apply inspec-profile-PROFILE_NAME.default 1 /path/to/config.toml
131
+ ```
132
+
133
+ This will apply the configuration to all services in the service group. For
134
+ more info on service groups see the
135
+ [Habitat docs](https://www.habitat.sh/docs/using-habitat/#service-groups)
136
+
137
+ ## Testing
138
+
139
+ Lint, unit, and functional tests are ran from the root of the InSpec source:
140
+
141
+ ```
142
+ bundle exec rake test
143
+ ```
144
+
145
+ To execute the integration tests (Test Kitchen + Vagrant + VirtualBox) run the
146
+ following from the directory containing this README.md:
147
+
148
+ ```
149
+ bundle exec kitchen test
150
+ ```
@@ -0,0 +1,28 @@
1
+ ---
2
+ driver:
3
+ name: vagrant
4
+
5
+ provisioner:
6
+ name: chef_solo
7
+ sudo: true
8
+
9
+ verifier:
10
+ name: inspec
11
+
12
+ platforms:
13
+ - name: ubuntu-18.04
14
+
15
+ lifecycle:
16
+ # Build the InSpec gem so it is available to install during `kitchen converge`
17
+ pre_create:
18
+ - cd ../../../ && gem build inspec.gemspec
19
+ - mv ../../../inspec-*.gem test/cookbooks/inspec_habitat_fixture/files/inspec-local.gem
20
+ post_converge:
21
+ - local: sleep 10 # Wait for Habitat to load/run hab service before `verify`
22
+
23
+ suites:
24
+ - name: default
25
+ run_list:
26
+ - recipe[inspec_habitat_fixture]
27
+ attributes:
28
+
@@ -10,21 +10,21 @@ module InspecPlugins
10
10
  "#{basename} habitat profile #{command.usage}"
11
11
  end
12
12
 
13
- desc 'create PATH', 'Create a one-time Habitat artifact for the profile found at PATH'
13
+ desc 'create PATH', 'Create a Habitat artifact for the profile found at PATH'
14
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)
15
+ desc: 'Output directory for the Habitat artifact. Default: current directory'
16
+ def create(path = '.')
17
+ InspecPlugins::Habitat::Profile.new(path, options).create
18
18
  end
19
19
 
20
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)
21
+ def setup(path = '.')
22
+ InspecPlugins::Habitat::Profile.new(path, options).setup
23
23
  end
24
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)
25
+ desc 'upload PATH', 'Create then upload a Habitat artifact for the profile found at PATH to the Habitat Builder Depot'
26
+ def upload(path = '.')
27
+ InspecPlugins::Habitat::Profile.new(path, options).upload
28
28
  end
29
29
  end
30
30
 
@@ -1,238 +1,226 @@
1
1
  # encoding: utf-8
2
- # author: Adam Leff
3
2
 
4
3
  require 'inspec/profile_vendor'
5
4
  require 'mixlib/shellout'
6
5
  require 'tomlrb'
6
+ require 'ostruct'
7
7
 
8
8
  module InspecPlugins
9
9
  module Habitat
10
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
-
11
+ attr_reader :logger
32
12
  def initialize(path, options = {})
33
13
  @path = path
34
14
  @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)
15
+ @logger = Inspec::Log
16
+ logger.level(options.fetch(:log_level, 'info').to_sym)
40
17
  end
41
18
 
42
19
  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_default_config(work_dir)
55
-
56
- # returns the path to the .hart file in the work directory
57
- build_hart
20
+ logger.info("Creating a Habitat artifact for '#{@path}'...")
21
+
22
+ # Need to create working directory first so `ensure` doesn't error
23
+ working_dir = create_working_dir
24
+
25
+ habitat_config = read_habitat_config
26
+ verify_habitat_setup(habitat_config)
27
+
28
+ output_dir = @options[:output_dir] || Dir.pwd
29
+ unless File.directory?(output_dir)
30
+ exit_with_error("Output directory #{output_dir} is not a directory " \
31
+ 'or does not exist.')
32
+ end
33
+
34
+ duplicated_profile = duplicate_profile(@path, working_dir)
35
+ prepare_profile!(duplicated_profile)
36
+
37
+ hart_file = build_hart(working_dir, habitat_config)
38
+
39
+ logger.debug("Copying artifact to #{output_dir}...")
40
+ destination = File.join(output_dir, File.basename(hart_file))
41
+ FileUtils.cp(hart_file, destination)
42
+
43
+ logger.info("Habitat artifact '#{@destination}' created.")
44
+ destination
58
45
  rescue => e
59
- @log.debug(e.backtrace.join("\n"))
60
- exit_with_error(
61
- 'Unable to generate Habitat artifact.',
62
- "#{e.class} -- #{e.message}",
63
- )
46
+ logger.debug(e.backtrace.join("\n"))
47
+ exit_with_error('Unable to create Habitat artifact.')
48
+ ensure
49
+ if Dir.exist?(working_dir)
50
+ logger.debug("Deleting working directory #{working_dir}")
51
+ FileUtils.rm_rf(working_dir)
52
+ end
64
53
  end
65
54
 
66
- def copy(hart_file)
67
- validate_output_dir
55
+ def setup(profile = profile_from_path(@path))
56
+ path = profile.root_path
57
+ logger.debug("Setting up #{path} for Habitat...")
58
+
59
+ plan_file = File.join(path, 'habitat', 'plan.sh')
60
+ logger.info("Generating Habitat plan at #{plan_file}...")
61
+ vars = {
62
+ profile: profile,
63
+ habitat_origin: read_habitat_config['origin'],
64
+ }
65
+ create_file_from_template(plan_file, 'plan.sh.erb', vars)
68
66
 
69
- @log.info("Copying artifact to #{output_dir}...")
70
- copy_hart(hart_file)
67
+ run_hook_file = File.join(path, 'habitat', 'hooks', 'run')
68
+ logger.info("Generating a Habitat run hook at #{run_hook_file}...")
69
+ create_file_from_template(run_hook_file, 'hooks/run.erb')
70
+
71
+ default_toml = File.join(path, 'habitat', 'default.toml')
72
+ logger.info("Generating a Habitat default.toml at #{default_toml}...")
73
+ create_file_from_template(default_toml, 'default.toml.erb')
74
+
75
+ config = File.join(path, 'habitat', 'config', 'inspec_exec_config.json')
76
+ logger.info("Generating #{config} for `inspec exec`...")
77
+ create_file_from_template(config, 'config/inspec_exec_config.json.erb')
71
78
  end
72
79
 
73
80
  def upload
74
- validate_habitat_auth_token
75
- hart_file = create
76
- upload_hart(hart_file)
81
+ habitat_config = read_habitat_config
82
+
83
+ if habitat_config['auth_token'].nil?
84
+ exit_with_error(
85
+ 'Unable to determine Habitat auth token for uploading.',
86
+ 'Run `hab setup` or set the HAB_AUTH_TOKEN environment variable.',
87
+ )
88
+ end
89
+
90
+ # Run create command to create habitat artifact
91
+ hart = create
92
+
93
+ logger.info("Uploading Habitat artifact #{hart}...")
94
+ upload_hart(hart, habitat_config)
95
+ logger.info("Habitat artifact #{hart} uploaded.")
77
96
  rescue => e
78
- @log.debug(e.backtrace.join("\n"))
79
- exit_with_error(
80
- 'Unable to upload Habitat artifact.',
81
- "#{e.class} -- #{e.message}",
82
- )
97
+ logger.debug(e.backtrace.join("\n"))
98
+ exit_with_error('Unable to upload Habitat artifact.')
83
99
  end
84
100
 
85
- def delete_work_dir
86
- @log.debug("Deleting work directory #{work_dir}")
87
- FileUtils.rm_rf(work_dir) if Dir.exist?(work_dir)
101
+ private
102
+
103
+ def create_working_dir
104
+ working_dir = Dir.mktmpdir
105
+ logger.debug("Generated working directory #{working_dir}")
106
+ working_dir
88
107
  end
89
108
 
90
- def setup
91
- @log.info("Setting up profile at #{path} for Habitat...")
92
- create_profile_object
93
- verify_profile
94
- vendor_profile_dependencies
95
- create_habitat_directories(path)
96
- create_plan(path)
97
- create_run_hook(path)
98
- create_default_config(path)
109
+ def duplicate_profile(path, working_dir)
110
+ profile = profile_from_path(path)
111
+ copy_profile_to_working_dir(profile, working_dir)
112
+ profile_from_path(working_dir)
99
113
  end
100
114
 
101
- private
115
+ def prepare_profile!(profile)
116
+ vendored_profile = vendor_profile_dependencies!(profile)
117
+ verify_profile(vendored_profile)
118
+ setup(vendored_profile)
119
+ end
102
120
 
103
- def create_profile_object
104
- @profile = Inspec::Profile.for_target(
121
+ def profile_from_path(path)
122
+ Inspec::Profile.for_target(
105
123
  path,
106
124
  backend: Inspec::Backend.create(Inspec::Config.mock),
107
125
  )
108
126
  end
109
127
 
110
- def verify_profile
111
- @log.info('Checking to see if the profile is valid...')
128
+ def copy_profile_to_working_dir(profile, working_dir)
129
+ logger.debug('Copying profile contents to the working directory...')
130
+ profile.files.each do |profile_file|
131
+ next if File.extname(profile_file) == '.hart'
132
+
133
+ src = File.join(profile.root_path, profile_file)
134
+ dst = File.join(working_dir, profile_file)
135
+ if File.directory?(profile_file)
136
+ logger.debug("Creating directory #{dst}")
137
+ FileUtils.mkdir_p(dst)
138
+ else
139
+ logger.debug("Copying file #{src} to #{dst}")
140
+ FileUtils.cp_r(src, dst)
141
+ end
142
+ end
143
+ end
144
+
145
+ def verify_profile(profile)
146
+ logger.debug('Checking to see if the profile is valid...')
112
147
 
113
148
  unless profile.check[:summary][:valid]
114
- exit_with_error('Profile check failed. Please fix the profile before creating a Habitat artifact.')
149
+ exit_with_error('Profile check failed. Please fix the profile ' \
150
+ 'before creating a Habitat artifact.')
115
151
  end
116
152
 
117
- @log.info('Profile is valid.')
153
+ logger.debug('Profile is valid.')
118
154
  end
119
155
 
120
- def vendor_profile_dependencies
121
- profile_vendor = Inspec::ProfileVendor.new(path)
156
+ def vendor_profile_dependencies!(profile)
157
+ profile_vendor = Inspec::ProfileVendor.new(profile.root_path)
122
158
  if profile_vendor.lockfile.exist? && profile_vendor.cache_path.exist?
123
- @log.info("Profile's dependencies are already vendored, skipping vendor process.")
159
+ logger.debug("Profile's dependencies are already vendored, skipping " \
160
+ 'vendor process.')
124
161
  else
125
- @log.info("Vendoring the profile's dependencies...")
162
+ logger.debug("Vendoring the profile's dependencies...")
126
163
  profile_vendor.vendor!
127
164
 
128
- @log.info('Ensuring all vendored content has read permissions...')
165
+ logger.debug('Ensuring all vendored content has read permissions...')
129
166
  profile_vendor.make_readable
130
-
131
- # refresh the profile object since the profile now has new files
132
- create_profile_object
133
167
  end
168
+
169
+ # Return new profile since it has changed
170
+ Inspec::Profile.for_target(
171
+ profile.root_path,
172
+ backend: Inspec::Backend.create(Inspec::Config.mock),
173
+ )
134
174
  end
135
175
 
136
- def validate_habitat_installed
137
- @log.info('Checking to see if Habitat is installed...')
176
+ def verify_habitat_setup(habitat_config)
177
+ logger.debug('Checking to see if Habitat is installed...')
138
178
  cmd = Mixlib::ShellOut.new('hab --version')
139
179
  cmd.run_command
140
- exit_with_error('Unable to run Habitat commands.', cmd.stderr) if cmd.error?
141
- end
142
-
143
- def validate_habitat_origin
144
- exit_with_error(
145
- 'Unable to determine Habitat origin name.',
146
- 'Run `hab setup` or set the HAB_ORIGIN environment variable.',
147
- ) if habitat_origin.nil?
148
- end
149
-
150
- def validate_habitat_auth_token
151
- exit_with_error(
152
- 'Unable to determine Habitat auth token for publishing.',
153
- 'Run `hab setup` or set the HAB_AUTH_TOKEN environment variable.',
154
- ) if habitat_auth_token.nil?
155
- end
156
-
157
- def validate_output_dir
158
- exit_with_error("Output directory #{output_dir} is not a directory or does not exist.") unless
159
- File.directory?(output_dir)
160
- end
161
-
162
- def work_dir
163
- return @work_dir if @work_dir
164
-
165
- @work_dir ||= Dir.mktmpdir('inspec-habitat-exporter')
166
- @log.debug("Generated work directory #{@work_dir}")
167
-
168
- @work_dir
169
- end
170
-
171
- def create_habitat_directories(parent_directory)
172
- [
173
- File.join(parent_directory, 'habitat'),
174
- File.join(parent_directory, 'habitat', 'hooks'),
175
- ].each do |dir|
176
- Dir.mkdir(dir) unless Dir.exist?(dir)
180
+ if cmd.error?
181
+ exit_with_error('Unable to run Habitat commands.', cmd.stderr)
177
182
  end
178
- end
179
183
 
180
- def copy_profile_to_work_dir
181
- @log.info('Copying profile contents to the work directory...')
182
- profile.files.each do |f|
183
- src = File.join(profile.root_path, f)
184
- dst = File.join(work_dir, f)
185
- if File.directory?(f)
186
- @log.debug("Creating directory #{dst}")
187
- FileUtils.mkdir_p(dst)
188
- else
189
- @log.debug("Copying file #{src} to #{dst}")
190
- FileUtils.cp_r(src, dst)
191
- end
184
+ if habitat_config['origin'].nil?
185
+ exit_with_error(
186
+ 'Unable to determine Habitat origin name.',
187
+ 'Run `hab setup` or set the HAB_ORIGIN environment variable.',
188
+ )
192
189
  end
193
190
  end
194
191
 
195
- def create_plan(directory)
196
- plan_file = File.join(directory, 'habitat', 'plan.sh')
197
- @log.info("Generating Habitat plan at #{plan_file}...")
198
- File.write(plan_file, plan_contents)
192
+ def create_file_from_template(file, template, vars = {})
193
+ FileUtils.mkdir_p(File.dirname(file))
194
+ template_path = File.join(__dir__, '../../templates/habitat', template)
195
+ contents = ERB.new(File.read(template_path))
196
+ .result(OpenStruct.new(vars).instance_eval { binding })
197
+ File.write(file, contents)
199
198
  end
200
199
 
201
- def create_run_hook(directory)
202
- run_hook_file = File.join(directory, 'habitat', 'hooks', 'run')
203
- @log.info("Generating a Habitat run hook at #{run_hook_file}...")
204
- File.write(run_hook_file, run_hook_contents)
205
- end
206
-
207
- def create_default_config(directory)
208
- default_toml = File.join(directory, 'habitat', 'default.toml')
209
- @log.info("Generating Habitat's default.toml configuration...")
210
- File.write(default_toml, 'sleep_time = 300')
211
- end
212
-
213
- def build_hart
214
- @log.info('Building our Habitat artifact...')
200
+ def build_hart(working_dir, habitat_config)
201
+ logger.debug('Building our Habitat artifact...')
215
202
 
216
203
  env = {
217
204
  'TERM' => 'vt100',
218
- 'HAB_ORIGIN' => habitat_origin,
205
+ 'HAB_ORIGIN' => habitat_config['origin'],
219
206
  'HAB_NONINTERACTIVE' => 'true',
220
207
  }
221
208
 
222
- env['RUST_LOG'] = 'debug' if @log.level == :debug
209
+ env['RUST_LOG'] = 'debug' if logger.level == :debug
223
210
 
224
211
  # TODO: Would love to use Mixlib::ShellOut here, but it doesn't
225
212
  # seem to preserve the STDIN tty, and docker gets angry.
226
- Dir.chdir(work_dir) do
213
+ Dir.chdir(working_dir) do
227
214
  unless system(env, 'hab pkg build .')
228
215
  exit_with_error('Unable to build the Habitat artifact.')
229
216
  end
230
217
  end
231
218
 
232
- hart_files = Dir.glob(File.join(work_dir, 'results', '*.hart'))
219
+ hart_files = Dir.glob(File.join(working_dir, 'results', '*.hart'))
233
220
 
234
221
  if hart_files.length > 1
235
- exit_with_error('More than one Habitat artifact was created which was not expected.')
222
+ exit_with_error('More than one Habitat artifact was created which ' \
223
+ 'was not expected.')
236
224
  elsif hart_files.empty?
237
225
  exit_with_error('No Habitat artifact was created.')
238
226
  end
@@ -240,21 +228,16 @@ module InspecPlugins
240
228
  hart_files.first
241
229
  end
242
230
 
243
- def copy_hart(working_dir_hart)
244
- hart_basename = File.basename(working_dir_hart)
245
- dst = File.join(output_dir, hart_basename)
246
- FileUtils.cp(working_dir_hart, dst)
247
-
248
- dst
249
- end
231
+ def upload_hart(hart_file, habitat_config)
232
+ logger.debug("Uploading '#{hart_file}' to the Habitat Builder Depot...")
250
233
 
251
- def upload_hart(hart_file)
252
- @log.info('Uploading the Habitat artifact to our Depot...')
234
+ config = habitat_config
253
235
 
254
236
  env = {
255
- 'TERM' => 'vt100',
256
- 'HAB_AUTH_TOKEN' => habitat_auth_token,
237
+ 'HAB_AUTH_TOKEN' => config['auth_token'],
257
238
  'HAB_NONINTERACTIVE' => 'true',
239
+ 'HAB_ORIGIN' => config['origin'],
240
+ 'TERM' => 'vt100',
258
241
  }
259
242
 
260
243
  env['HAB_DEPOT_URL'] = ENV['HAB_DEPOT_URL'] if ENV['HAB_DEPOT_URL']
@@ -269,124 +252,25 @@ module InspecPlugins
269
252
  )
270
253
  end
271
254
 
272
- @log.info('Upload complete!')
273
- end
274
-
275
- def habitat_origin
276
- ENV['HAB_ORIGIN'] || habitat_cli_config['origin']
277
- end
278
-
279
- def habitat_auth_token
280
- ENV['HAB_AUTH_TOKEN'] || habitat_cli_config['auth_token']
281
- end
282
-
283
- def habitat_cli_config
284
- return @cli_config if @cli_config
285
-
286
- config_file = File.join(ENV['HOME'], '.hab', 'etc', 'cli.toml')
287
- return {} unless File.exist?(config_file)
288
-
289
- @cli_config = Tomlrb.load_file(config_file)
255
+ logger.debug('Upload complete!')
290
256
  end
291
257
 
292
- def output_dir
293
- options[:output_dir] || Dir.pwd
258
+ def read_habitat_config
259
+ cli_toml = File.join(ENV['HOME'], '.hab', 'etc', 'cli.toml')
260
+ cli_toml = '/hab/etc/cli.toml' unless File.exist?(cli_toml)
261
+ cli_config = File.exist?(cli_toml) ? Tomlrb.load_file(cli_toml) : {}
262
+ cli_config['origin'] ||= ENV['HAB_ORIGIN']
263
+ cli_config['auth_token'] ||= ENV['HAB_AUTH_TOKEN']
264
+ cli_config
294
265
  end
295
266
 
296
267
  def exit_with_error(*errors)
297
268
  errors.each do |error_msg|
298
- @log.error(error_msg)
269
+ logger.error(error_msg)
299
270
  end
300
271
 
301
272
  exit 1
302
273
  end
303
-
304
- def package_name
305
- "inspec-profile-#{profile.name}"
306
- end
307
-
308
- def plan_contents
309
- plan = <<~EOL
310
- pkg_name=#{package_name}
311
- pkg_version=#{profile.version}
312
- pkg_origin=#{habitat_origin}
313
- pkg_deps=(chef/inspec)
314
- EOL
315
-
316
- plan += "pkg_license='#{profile.metadata.params[:license]}'\n\n" if profile.metadata.params[:license]
317
-
318
- plan += <<~EOL
319
- do_setup_environment() {
320
- ARCHIVE_PATH="$HAB_CACHE_SRC_PATH/$pkg_dirname/$pkg_name-$pkg_version.tar.gz"
321
- }
322
-
323
- do_build() {
324
- if [ ! -f $PLAN_CONTEXT/../inspec.yml ]; then
325
- exit_with 'Cannot find inspec.yml. Please build from profile root.' 1
326
- fi
327
-
328
- local profile_files=($(ls $PLAN_CONTEXT/../ -I habitat -I results))
329
- local profile_location="$HAB_CACHE_SRC_PATH/$pkg_dirname/build"
330
- mkdir -p $profile_location
331
-
332
- build_line "Copying profile files to $profile_location"
333
- cp -R ${profile_files[@]} $profile_location
334
-
335
- build_line "Archiving $ARCHIVE_PATH"
336
- inspec archive "$HAB_CACHE_SRC_PATH/$pkg_dirname/build" \
337
- -o $ARCHIVE_PATH \
338
- --overwrite
339
- }
340
-
341
- do_install() {
342
- mkdir -p $pkg_prefix/profiles
343
- cp $ARCHIVE_PATH $pkg_prefix/profiles
344
- }
345
- EOL
346
-
347
- plan
348
- end
349
-
350
- def run_hook_contents
351
- <<~EOL
352
- #!{{pkgPathFor "core/bash"}}/bin/bash
353
-
354
- # Redirect stderr to stdout
355
- # This will be captured by Habitat and viewable via `journalctl`
356
- # NOTE: We might want log to "{{pkg.svc_path}}/logs" and handle rotation
357
- exec 2>&1
358
-
359
- # InSpec will try to create a .cache directory in the user's home directory
360
- # so this needs to be someplace writeable by the hab user
361
- export HOME={{pkg.svc_var_path}}
362
-
363
- RESULTS_DIR="{{pkg.svc_var_path}}/inspec_results"
364
- RESULTS_FILE="${RESULTS_DIR}/{{pkg.name}}.json"
365
-
366
- # Create a directory for InSpec reporter output
367
- mkdir -p $(dirname $RESULTS_FILE)
368
-
369
- while true; do
370
- echo "Executing InSpec for {{pkg.ident}}"
371
- inspec exec "{{pkg.path}}/profiles/*" --reporter=json > ${RESULTS_FILE}
372
-
373
- EXIT_STATUS=$?
374
- if [ $EXIT_STATUS -eq 0 ]; then
375
- echo "InSpec run completed successfully."
376
- elif [ $EXIT_STATUS -eq 100 ]; then
377
- echo "InSpec run completed successfully, with at least 1 failed test"
378
- elif [ $EXIT_STATUS -eq 101 ]; then
379
- echo "InSpec run completed successfully, with skipped tests and no failures"
380
- else
381
- echo "InSpec run did not complete successfully. Exited with status: $?"
382
- fi
383
- echo "Results located here: ${RESULTS_FILE}"
384
-
385
- echo "Sleeping for {{cfg.sleep_time}} seconds"
386
- sleep {{cfg.sleep_time}}
387
- done
388
- EOL
389
- end
390
274
  end
391
275
  end
392
276
  end