inspec 0.9.0

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 (247) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rubocop.yml +65 -0
  4. data/.travis.yml +23 -0
  5. data/CHANGELOG.md +38 -0
  6. data/Gemfile +33 -0
  7. data/LICENSE +201 -0
  8. data/MAINTAINERS.md +28 -0
  9. data/MAINTAINERS.toml +42 -0
  10. data/README.md +257 -0
  11. data/Rakefile +47 -0
  12. data/bin/inspec +109 -0
  13. data/docs/ctl_inspec.rst +195 -0
  14. data/docs/dsl_inspec.rst +182 -0
  15. data/docs/readme.rst +100 -0
  16. data/docs/resources.rst +4319 -0
  17. data/docs/template.rst +51 -0
  18. data/examples/test-kitchen/.kitchen.yml +20 -0
  19. data/examples/test-kitchen/Berksfile +3 -0
  20. data/examples/test-kitchen/Gemfile +21 -0
  21. data/examples/test-kitchen/README.md +27 -0
  22. data/examples/test-kitchen/metadata.rb +7 -0
  23. data/examples/test-kitchen/recipes/default.rb +6 -0
  24. data/examples/test-kitchen/recipes/nginx.rb +30 -0
  25. data/examples/test-kitchen/test/integration/default/web_spec.rb +28 -0
  26. data/inspec.gemspec +30 -0
  27. data/lib/inspec.rb +20 -0
  28. data/lib/inspec/backend.rb +42 -0
  29. data/lib/inspec/dsl.rb +151 -0
  30. data/lib/inspec/log.rb +34 -0
  31. data/lib/inspec/metadata.rb +79 -0
  32. data/lib/inspec/plugins.rb +9 -0
  33. data/lib/inspec/plugins/resource.rb +62 -0
  34. data/lib/inspec/profile.rb +138 -0
  35. data/lib/inspec/profile_context.rb +170 -0
  36. data/lib/inspec/resource.rb +76 -0
  37. data/lib/inspec/rspec_json_formatter.rb +27 -0
  38. data/lib/inspec/rule.rb +170 -0
  39. data/lib/inspec/runner.rb +154 -0
  40. data/lib/inspec/shell.rb +66 -0
  41. data/lib/inspec/targets.rb +9 -0
  42. data/lib/inspec/targets/core.rb +27 -0
  43. data/lib/inspec/targets/dir.rb +67 -0
  44. data/lib/inspec/targets/file.rb +29 -0
  45. data/lib/inspec/targets/folder.rb +43 -0
  46. data/lib/inspec/targets/tar.rb +34 -0
  47. data/lib/inspec/targets/url.rb +39 -0
  48. data/lib/inspec/targets/zip.rb +47 -0
  49. data/lib/inspec/version.rb +7 -0
  50. data/lib/matchers/matchers.rb +221 -0
  51. data/lib/resources/apache.rb +29 -0
  52. data/lib/resources/apache_conf.rb +113 -0
  53. data/lib/resources/apt.rb +140 -0
  54. data/lib/resources/audit_policy.rb +63 -0
  55. data/lib/resources/auditd_conf.rb +56 -0
  56. data/lib/resources/auditd_rules.rb +53 -0
  57. data/lib/resources/bond.rb +65 -0
  58. data/lib/resources/bridge.rb +114 -0
  59. data/lib/resources/command.rb +57 -0
  60. data/lib/resources/csv.rb +32 -0
  61. data/lib/resources/directory.rb +15 -0
  62. data/lib/resources/etc_group.rb +150 -0
  63. data/lib/resources/file.rb +110 -0
  64. data/lib/resources/gem.rb +46 -0
  65. data/lib/resources/group.rb +132 -0
  66. data/lib/resources/host.rb +143 -0
  67. data/lib/resources/inetd_conf.rb +56 -0
  68. data/lib/resources/interface.rb +127 -0
  69. data/lib/resources/iptables.rb +65 -0
  70. data/lib/resources/json.rb +64 -0
  71. data/lib/resources/kernel_module.rb +40 -0
  72. data/lib/resources/kernel_parameter.rb +55 -0
  73. data/lib/resources/limits_conf.rb +55 -0
  74. data/lib/resources/login_def.rb +60 -0
  75. data/lib/resources/mysql.rb +81 -0
  76. data/lib/resources/mysql_conf.rb +116 -0
  77. data/lib/resources/mysql_session.rb +52 -0
  78. data/lib/resources/npm.rb +44 -0
  79. data/lib/resources/ntp_conf.rb +58 -0
  80. data/lib/resources/oneget.rb +63 -0
  81. data/lib/resources/os.rb +22 -0
  82. data/lib/resources/os_env.rb +34 -0
  83. data/lib/resources/package.rb +169 -0
  84. data/lib/resources/parse_config.rb +75 -0
  85. data/lib/resources/passwd.rb +93 -0
  86. data/lib/resources/pip.rb +75 -0
  87. data/lib/resources/port.rb +296 -0
  88. data/lib/resources/postgres.rb +37 -0
  89. data/lib/resources/postgres_conf.rb +87 -0
  90. data/lib/resources/postgres_session.rb +59 -0
  91. data/lib/resources/processes.rb +57 -0
  92. data/lib/resources/registry_key.rb +54 -0
  93. data/lib/resources/script.rb +34 -0
  94. data/lib/resources/security_policy.rb +73 -0
  95. data/lib/resources/service.rb +379 -0
  96. data/lib/resources/ssh_conf.rb +75 -0
  97. data/lib/resources/user.rb +374 -0
  98. data/lib/resources/windows_feature.rb +77 -0
  99. data/lib/resources/yaml.rb +23 -0
  100. data/lib/resources/yum.rb +154 -0
  101. data/lib/utils/convert.rb +12 -0
  102. data/lib/utils/detect.rb +15 -0
  103. data/lib/utils/find_files.rb +36 -0
  104. data/lib/utils/hash.rb +13 -0
  105. data/lib/utils/modulator.rb +12 -0
  106. data/lib/utils/parser.rb +61 -0
  107. data/lib/utils/simpleconfig.rb +115 -0
  108. data/tasks/maintainers.rb +213 -0
  109. data/test/docker_run.rb +156 -0
  110. data/test/docker_test.rb +51 -0
  111. data/test/helper.rb +200 -0
  112. data/test/integration/.kitchen.yml +42 -0
  113. data/test/integration/Berksfile +4 -0
  114. data/test/integration/cookbooks/os_prepare/metadata.rb +8 -0
  115. data/test/integration/cookbooks/os_prepare/recipes/apt.rb +20 -0
  116. data/test/integration/cookbooks/os_prepare/recipes/default.rb +9 -0
  117. data/test/integration/cookbooks/os_prepare/recipes/file.rb +21 -0
  118. data/test/integration/cookbooks/os_prepare/recipes/package.rb +26 -0
  119. data/test/integration/default/_debug_spec.rb +1 -0
  120. data/test/integration/default/apt_spec.rb +42 -0
  121. data/test/integration/default/file_spec.rb +109 -0
  122. data/test/integration/default/group_spec.rb +32 -0
  123. data/test/integration/default/kernel_module_spec.rb +17 -0
  124. data/test/integration/default/kernel_parameter_spec.rb +56 -0
  125. data/test/integration/default/package_spec.rb +11 -0
  126. data/test/integration/default/service_spec.rb +28 -0
  127. data/test/integration/default/user_spec.rb +44 -0
  128. data/test/resource/command_test.rb +33 -0
  129. data/test/resource/dsl_test.rb +45 -0
  130. data/test/resource/file_test.rb +130 -0
  131. data/test/resource/ssh_config.rb +9 -0
  132. data/test/resource/sshd_config.rb +9 -0
  133. data/test/test-extra.yaml +11 -0
  134. data/test/test.yaml +11 -0
  135. data/test/unit/mock/cmd/Get-NetAdapter +24 -0
  136. data/test/unit/mock/cmd/GetUserAccount +33 -0
  137. data/test/unit/mock/cmd/GetWin32Group +23 -0
  138. data/test/unit/mock/cmd/PATH +1 -0
  139. data/test/unit/mock/cmd/Resolve-DnsName +26 -0
  140. data/test/unit/mock/cmd/Test-NetConnection +4 -0
  141. data/test/unit/mock/cmd/auditctl +7 -0
  142. data/test/unit/mock/cmd/auditpol +2 -0
  143. data/test/unit/mock/cmd/brew-info-jq +1 -0
  144. data/test/unit/mock/cmd/chage-l-root +7 -0
  145. data/test/unit/mock/cmd/dpkg-s-curl +21 -0
  146. data/test/unit/mock/cmd/dscl +5 -0
  147. data/test/unit/mock/cmd/etc-apt +7 -0
  148. data/test/unit/mock/cmd/find-etc-rc-d-name-S +12 -0
  149. data/test/unit/mock/cmd/find-net-interface +9 -0
  150. data/test/unit/mock/cmd/gem-list-local-a-q-rubocop +1 -0
  151. data/test/unit/mock/cmd/get-net-tcpconnection +24 -0
  152. data/test/unit/mock/cmd/get-netadapter-binding-bridge +4 -0
  153. data/test/unit/mock/cmd/get-package-firefox +30 -0
  154. data/test/unit/mock/cmd/get-package-ruby +18 -0
  155. data/test/unit/mock/cmd/get-service-dhcp +10 -0
  156. data/test/unit/mock/cmd/get-windows-feature +7 -0
  157. data/test/unit/mock/cmd/getent-hosts-example.com +1 -0
  158. data/test/unit/mock/cmd/getent-passwd-root +1 -0
  159. data/test/unit/mock/cmd/id-chartmann +1 -0
  160. data/test/unit/mock/cmd/id-root +1 -0
  161. data/test/unit/mock/cmd/initctl-show-config-ssh +3 -0
  162. data/test/unit/mock/cmd/initctl-status-ssh +1 -0
  163. data/test/unit/mock/cmd/iptables-s +6 -0
  164. data/test/unit/mock/cmd/launchctl-list +3 -0
  165. data/test/unit/mock/cmd/ls-1-etc-init.d +2 -0
  166. data/test/unit/mock/cmd/ls-sys-class-net-br +2 -0
  167. data/test/unit/mock/cmd/lsmod +2 -0
  168. data/test/unit/mock/cmd/lsof-np-itcp +4 -0
  169. data/test/unit/mock/cmd/netstat-tulpen +5 -0
  170. data/test/unit/mock/cmd/npm-ls-g--json-bower +9 -0
  171. data/test/unit/mock/cmd/pacman-qi-curl +21 -0
  172. data/test/unit/mock/cmd/ping-example.com +6 -0
  173. data/test/unit/mock/cmd/pip-show-jinja2 +11 -0
  174. data/test/unit/mock/cmd/ps-aux +3 -0
  175. data/test/unit/mock/cmd/pw-usershow-root-7 +1 -0
  176. data/test/unit/mock/cmd/reg_schedule +1 -0
  177. data/test/unit/mock/cmd/rpm-qia-curl +24 -0
  178. data/test/unit/mock/cmd/sbin_sysctl +1 -0
  179. data/test/unit/mock/cmd/secedit-export +7 -0
  180. data/test/unit/mock/cmd/service-e +2 -0
  181. data/test/unit/mock/cmd/service-sendmail-onestatus +3 -0
  182. data/test/unit/mock/cmd/service-sshd-status +1 -0
  183. data/test/unit/mock/cmd/sockstat +5 -0
  184. data/test/unit/mock/cmd/success +0 -0
  185. data/test/unit/mock/cmd/systemctl-show-all-sshd +6 -0
  186. data/test/unit/mock/cmd/win32_product +8 -0
  187. data/test/unit/mock/cmd/yum-repolist-all +52 -0
  188. data/test/unit/mock/files/auditd.conf +4 -0
  189. data/test/unit/mock/files/bond0 +37 -0
  190. data/test/unit/mock/files/etcgroup +3 -0
  191. data/test/unit/mock/files/example.csv +6 -0
  192. data/test/unit/mock/files/inetd.conf +2 -0
  193. data/test/unit/mock/files/kitchen.yml +7 -0
  194. data/test/unit/mock/files/limits.conf +5 -0
  195. data/test/unit/mock/files/login.defs +5 -0
  196. data/test/unit/mock/files/mysql.conf +8 -0
  197. data/test/unit/mock/files/mysql2.conf +2 -0
  198. data/test/unit/mock/files/ntp.conf +5 -0
  199. data/test/unit/mock/files/passwd +2 -0
  200. data/test/unit/mock/files/policyfile.lock.json +12 -0
  201. data/test/unit/mock/files/ssh_config +5 -0
  202. data/test/unit/mock/files/sshd_config +7 -0
  203. data/test/unit/mock/profiles/empty/metadata.rb +0 -0
  204. data/test/unit/mock/profiles/metadata/metadata.rb +1 -0
  205. data/test/unit/profile_context_test.rb +140 -0
  206. data/test/unit/profile_test.rb +49 -0
  207. data/test/unit/resources/apt_test.rb +46 -0
  208. data/test/unit/resources/audit_policy_test.rb +13 -0
  209. data/test/unit/resources/auditd_conf_test.rb +15 -0
  210. data/test/unit/resources/auditd_rules_test.rb +21 -0
  211. data/test/unit/resources/bond_test.rb +24 -0
  212. data/test/unit/resources/bridge_test.rb +56 -0
  213. data/test/unit/resources/csv_test.rb +35 -0
  214. data/test/unit/resources/etc_group_test.rb +37 -0
  215. data/test/unit/resources/gem_test.rb +20 -0
  216. data/test/unit/resources/group_test.rb +96 -0
  217. data/test/unit/resources/host_test.rb +38 -0
  218. data/test/unit/resources/inetd_conf_test.rb +15 -0
  219. data/test/unit/resources/interface_test.rb +54 -0
  220. data/test/unit/resources/iptables_test.rb +30 -0
  221. data/test/unit/resources/json_test.rb +36 -0
  222. data/test/unit/resources/kernel_module_test.rb +23 -0
  223. data/test/unit/resources/kernel_parameter_test.rb +13 -0
  224. data/test/unit/resources/limits_conf_test.rb +14 -0
  225. data/test/unit/resources/login_def_test.rb +16 -0
  226. data/test/unit/resources/mysql_conf_test.rb +14 -0
  227. data/test/unit/resources/npm_test.rb +20 -0
  228. data/test/unit/resources/ntp_conf_test.rb +16 -0
  229. data/test/unit/resources/oneget_test.rb +45 -0
  230. data/test/unit/resources/os_env_test.rb +13 -0
  231. data/test/unit/resources/package_test.rb +51 -0
  232. data/test/unit/resources/passwd_test.rb +24 -0
  233. data/test/unit/resources/pip_test.rb +15 -0
  234. data/test/unit/resources/port_test.rb +46 -0
  235. data/test/unit/resources/processes_test.rb +32 -0
  236. data/test/unit/resources/registry_key_test.rb +19 -0
  237. data/test/unit/resources/script_test.rb +19 -0
  238. data/test/unit/resources/security_policy_test.rb +16 -0
  239. data/test/unit/resources/service_test.rb +116 -0
  240. data/test/unit/resources/ssh_conf_test.rb +33 -0
  241. data/test/unit/resources/user_test.rb +93 -0
  242. data/test/unit/resources/windows_feature.rb +17 -0
  243. data/test/unit/resources/yaml_test.rb +34 -0
  244. data/test/unit/resources/yum_test.rb +68 -0
  245. data/test/unit/simpleconfig_test.rb +80 -0
  246. data/test/unit/utils/content_parser_test.rb +30 -0
  247. metadata +555 -0
@@ -0,0 +1,213 @@
1
+ # encoding: utf-8
2
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'rake'
19
+
20
+ SOURCE = File.join(File.dirname(__FILE__), '..', 'MAINTAINERS.toml')
21
+ TARGET = File.join(File.dirname(__FILE__), '..', 'MAINTAINERS.md')
22
+
23
+ # The list of repositories that teams should own
24
+ REPOSITORIES = ['chef/inspec']
25
+
26
+ begin
27
+ require 'tomlrb'
28
+ require 'octokit'
29
+ require 'pp'
30
+ task default: :generate
31
+
32
+ namespace :maintainers do
33
+ desc 'Generate MarkDown version of MAINTAINERS file'
34
+ task :generate do
35
+ maintainers = Tomlrb.load_file SOURCE
36
+ out = "<!-- This is a generated file. Please do not edit directly -->\n\n"
37
+ out << "<!-- Modify MAINTAINERS.toml and run `rake maintainers:generate` to regenerate. -->\n\n"
38
+ out << '# ' + maintainers['Preamble']['title'] + "\n\n"
39
+ out << maintainers['Preamble']['text'] + "\n"
40
+ out << components(maintainers['people'], maintainers['Org']['Components'])
41
+ File.open(TARGET, 'w') { |fn|
42
+ fn.write out
43
+ }
44
+ end
45
+
46
+ desc 'Synchronize GitHub teams'
47
+ task :synchronize do
48
+ Octokit.auto_paginate = true
49
+ github_teams
50
+ prepare_teams(source['Org']['Components'].dup)
51
+ sync_teams!
52
+ end
53
+ end
54
+
55
+ def github
56
+ @github ||= Octokit::Client.new(netrc: true)
57
+ end
58
+
59
+ def source
60
+ @source ||= Tomlrb.load_file SOURCE
61
+ end
62
+
63
+ def teams
64
+ @teams ||= { 'inspec-maintainers' => { 'title' => 'Maintainers of the InSpec toolset' } }
65
+ end
66
+
67
+ def add_members(team, name)
68
+ teams['inspec-maintainers']['members'] ||= []
69
+ teams['inspec-maintainers']['members'] << name
70
+ teams[team] ||= {}
71
+ teams[team]['members'] ||= []
72
+ teams[team]['members'] << name
73
+ end
74
+
75
+ def set_team_title(team, title)
76
+ teams[team] ||= {}
77
+ teams[team]['title'] = title
78
+ end
79
+
80
+ def gh_teams
81
+ @gh_teams ||= {}
82
+ end
83
+
84
+ # we have to resolve team names to ids. While we're at it, we can get the privacy
85
+ # setting, so we know whether we need to update it
86
+ def github_teams
87
+ github.org_teams('chef').each do |team|
88
+ gh_teams[team[:slug]] = { 'id' => team[:id], 'privacy' => team[:privacy] }
89
+ end
90
+ end
91
+
92
+ def get_github_team(team)
93
+ github.team_members(gh_teams[team]['id']).map do |member|
94
+ member[:login]
95
+ end.sort.uniq.map(&:downcase)
96
+ rescue
97
+ []
98
+ end
99
+
100
+ def create_team(team)
101
+ puts "creating new github team: #{team} with title: #{teams[team]['title']} "
102
+ t = github.create_team('chef', name: team, description: teams[team]['title'],
103
+ privacy: 'closed', repo_names: REPOSITORIES,
104
+ accept: 'application/vnd.github.ironman-preview+json')
105
+ gh_teams[team] = { 'id' => t[:id], 'privacy' => t[:privacy] }
106
+ end
107
+
108
+ def compare_teams(current, desired)
109
+ # additions are the subtraction of the current state from the desired state
110
+ # deletions are the subtraction of the desired state from the current state
111
+ [desired - current, current - desired]
112
+ end
113
+
114
+ def prepare_teams(cmp)
115
+ %w{text paths}.each { |k| cmp.delete(k) }
116
+ if cmp.key?('team')
117
+ team = cmp.delete('team')
118
+ add_members(team, cmp.delete('lieutenant')) if cmp.key?('lieutenant')
119
+ add_members(team, cmp.delete('maintainers')) if cmp.key?('maintainers')
120
+ set_team_title(team, cmp.delete('title'))
121
+ else
122
+ %w{maintainers lieutenant title}.each { |k| cmp.delete(k) }
123
+ end
124
+ cmp.each { |_k, v| prepare_teams(v) }
125
+ end
126
+
127
+ def update_team(team, additions, deletions)
128
+ create_team(team) unless gh_teams.key?(team)
129
+ update_team_privacy(team)
130
+ add_team_members(team, additions)
131
+ remove_team_members(team, deletions)
132
+ rescue
133
+ puts "failed for #{team}"
134
+ end
135
+
136
+ def update_team_privacy(_team)
137
+ # return
138
+ # return if gh_teams[team]['privacy'] == 'closed'
139
+ # puts "Setting #{team} privacy to closed from #{gh_teams[team]['privacy']}"
140
+ # github.update_team(gh_teams[team]['id'], privacy: 'closed',
141
+ # accept: 'application/vnd.github.ironman-preview+json')
142
+ end
143
+
144
+ def add_team_members(team, additions)
145
+ additions.each do |member|
146
+ puts "Adding #{member} to #{team}"
147
+ github.add_team_membership(gh_teams[team]['id'], member, role: 'member',
148
+ accept: 'application/vnd.github.ironman-preview+json')
149
+ end
150
+ end
151
+
152
+ def remove_team_members(team, deletions)
153
+ deletions.each do |member|
154
+ puts "Removing #{member} from #{team}"
155
+ github.remove_team_membership(gh_teams[team]['id'], member,
156
+ accept: 'application/vnd.github.ironman-preview+json')
157
+ end
158
+ end
159
+
160
+ def sync_teams!
161
+ teams.each do |name, details|
162
+ current = get_github_team(name)
163
+ desired = details['members'].flatten.sort.uniq.map(&:downcase)
164
+ additions, deletions = compare_teams(current, desired)
165
+ update_team(name, additions, deletions)
166
+ end
167
+ end
168
+
169
+ def get_person(person)
170
+ source['people'][person]
171
+ end
172
+
173
+ def components(list, cmp)
174
+ out = '## ' + cmp.delete('title') + "\n\n"
175
+ out << cmp.delete('text') + "\n" if cmp.key?('text')
176
+ out << "To mention the team, use @chef/#{cmp.delete('team')}\n\n" if cmp.key?('team')
177
+ if cmp.key?('lieutenant')
178
+ out << "### Lieutenant\n\n"
179
+ out << person(list, cmp.delete('lieutenant')) + "\n\n"
180
+ end
181
+ out << maintainers(list, cmp.delete('maintainers')) + "\n" if cmp.key?('maintainers')
182
+ cmp.delete('paths')
183
+ cmp.each { |_k, v| out << components(list, v) }
184
+ out
185
+ end
186
+
187
+ def maintainers(list, people)
188
+ o = "### Maintainers\n\n"
189
+ people.each do |p|
190
+ o << person(list, p) + "\n"
191
+ end
192
+ o
193
+ end
194
+
195
+ # rubocop:disable Metrics/AbcSize
196
+ def person(list, person)
197
+ if list[person].key?('GitHub')
198
+ out = "* [#{list[person]['Name']}](https://github.com/#{list[person]['GitHub']})"
199
+ else
200
+ out = "* #{list[person]['Name']}"
201
+ end
202
+ out << "\n * IRC - #{list[person]['IRC']}" if list[person].key?('IRC')
203
+ out << "\n * [@#{list[person]['Twitter']}](https://twitter.com/#{list[person]['Twitter']})" if list[person].key?('Twitter')
204
+ out << "\n * [#{list[person]['email']}](mailto:#{list[person]['email']})" if list[person].key?('email')
205
+ out << "\n * #{list[person]['phone']}" if list[person].key?('phone')
206
+ out << "\n * [ServerFault](#{list[person]['ServerFault']})" if list[person].key?('ServerFault')
207
+ out
208
+ end
209
+ # rubocop:enable all
210
+
211
+ rescue LoadError
212
+ STDERR.puts "\n*** TomlRb not available.\n\n"
213
+ end
@@ -0,0 +1,156 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+
4
+ require 'docker'
5
+ require 'yaml'
6
+ require 'concurrent'
7
+
8
+ class DockerRunner
9
+ def initialize(conf_path = nil)
10
+ @conf_path = conf_path ||
11
+ ENV['config']
12
+
13
+ if @conf_path.nil?
14
+ fail "You must provide a configuration file with docker boxes"
15
+ end
16
+
17
+ unless File.file?(@conf_path)
18
+ fail "Can't find configuration in #{@conf_path}"
19
+ end
20
+
21
+ @conf = YAML.load_file(@conf_path)
22
+ if @conf.nil? or @conf.empty?
23
+ fail "Can't read coniguration in #{@conf_path}"
24
+ end
25
+ if @conf['images'].nil?
26
+ fail "You must configure test images in your #{@conf_path}"
27
+ end
28
+
29
+ @images = docker_images_by_tag
30
+ @image_pull_tickets = Concurrent::Semaphore.new(2)
31
+ @docker_run_tickets = Concurrent::Semaphore.new(5)
32
+ end
33
+
34
+ def run_all(&block)
35
+ fail 'You must provide a block for run_all' unless block_given?
36
+
37
+ promises = @conf['images'].map do |id|
38
+ run_on_target(id, &block)
39
+ end
40
+
41
+ # wait for all tests to be finished
42
+ sleep(0.1) until promises.all?(&:fulfilled?)
43
+
44
+ # return resulting values
45
+ promises.map(&:value)
46
+ end
47
+
48
+ def run_on_target(name, &block)
49
+ pr = Concurrent::Promise.new {
50
+ begin
51
+ container = start_container(name)
52
+ res = block.call(name, container)
53
+ # special rescue block to handle not implemented error
54
+ rescue NotImplementedError => err
55
+ raise err.message
56
+ end
57
+ # always stop the container
58
+ stop_container(container)
59
+ res
60
+ }.execute
61
+
62
+ # failure handling
63
+ pr.rescue do |err|
64
+ msg = "\033[31;1m#{err.message}\033[0m"
65
+ puts msg
66
+ msg + "\n" + err.backtrace.join("\n")
67
+ end
68
+ end
69
+
70
+ def provision_image(image, prov, files)
71
+ return image if prov['script'].nil?
72
+ path = File.join(File.dirname(@conf_path), prov['script'])
73
+ unless File.file?(path)
74
+ puts "Can't find script file #{path}"
75
+ return image
76
+ end
77
+ puts " script #{path}"
78
+ dst = "/bootstrap#{files.length}.sh"
79
+ files.push(dst)
80
+ image.insert_local('localPath' => path, 'outputPath' => dst)
81
+ end
82
+
83
+ def bootstrap_image(name, image)
84
+ files = []
85
+ provisions = Array(@conf['provision'])
86
+ puts "--> provision docker #{name}" unless provisions.empty?
87
+ provisions.each do |prov|
88
+ image = provision_image(image, prov, files)
89
+ end
90
+ [image, files]
91
+ end
92
+
93
+ def start_container(name, version = nil)
94
+ unless name.include?(':')
95
+ version ||= 'latest'
96
+ name = "#{name}:#{version}"
97
+ end
98
+ puts "--> schedule docker #{name}"
99
+
100
+ image = @images[name]
101
+ if image.nil?
102
+ puts "\033[35;1m--> pull docker images #{name} "\
103
+ "(this may take a while)\033[0m"
104
+
105
+ @image_pull_tickets.acquire(1)
106
+ puts "... start pull image #{name}"
107
+ image = Docker::Image.create('fromImage' => name)
108
+ @image_pull_tickets.release(1)
109
+
110
+ unless image.nil?
111
+ puts "\033[35;1m--> pull docker images finished for #{name}\033[0m"
112
+ end
113
+ end
114
+
115
+ fail "Can't find nor pull docker image #{name}" if image.nil?
116
+
117
+ image, scripts = bootstrap_image(name, image)
118
+
119
+ @docker_run_tickets.acquire(1)
120
+
121
+ puts "--> start docker #{name}"
122
+ container = Docker::Container.create(
123
+ 'Cmd' => %w{sleep 3600},
124
+ 'Image' => image.id,
125
+ 'OpenStdin' => true,
126
+ )
127
+ container.start
128
+
129
+ scripts.each do |script|
130
+ container.exec(%w{chmod +x}.push(script))
131
+ container.exec(%w{sh -c}.push(script))
132
+ end
133
+
134
+ container
135
+ end
136
+
137
+ def stop_container(container)
138
+ @docker_run_tickets.release(1)
139
+ puts "--> killrm docker #{container.id}"
140
+ container.kill
141
+ container.delete(force: true)
142
+ end
143
+
144
+ private
145
+
146
+ # get all docker image tags
147
+ def docker_images_by_tag
148
+ images = {}
149
+ Docker::Image.all.map do |img|
150
+ Array(img.info['RepoTags']).each do |tag|
151
+ images[tag] = img
152
+ end
153
+ end
154
+ images
155
+ end
156
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+
4
+ require_relative 'docker_run'
5
+ require_relative '../lib/inspec'
6
+
7
+ tests = ARGV
8
+ if tests.empty?
9
+ puts 'Nothing to do.'
10
+ exit 0
11
+ end
12
+
13
+ class DockerTester
14
+ def initialize(tests)
15
+ @tests = tests
16
+ @docker = DockerRunner.new
17
+ end
18
+
19
+ def run
20
+ puts ['Running tests:', @tests].flatten.join("\n- ")
21
+ puts ''
22
+
23
+ conf = RSpec.configuration
24
+ reporter = conf.reporter
25
+ results = nil
26
+
27
+ # start reporting loop
28
+ reporter.report(0) do |report|
29
+ results = @docker.run_all do |name, container|
30
+ status = test_container(container, report)
31
+ status.all? ? nil : "Failed to run tests on #{name}"
32
+ end
33
+ end
34
+
35
+ # check if we were successful
36
+ failures = results.compact
37
+ failures.each { |f| puts "\033[31;1m#{f}\033[0m\n\n" }
38
+ failures.empty? or fail 'Test failures'
39
+ end
40
+
41
+ def test_container(container, report)
42
+ puts "--> run test on docker #{container.id}"
43
+ opts = { 'target' => "docker://#{container.id}" }
44
+ runner = Inspec::Runner.new(opts)
45
+ runner.add_tests(@tests)
46
+ tests = runner.tests.ordered_example_groups
47
+ tests.map { |g| g.run(report) }
48
+ end
49
+ end
50
+
51
+ DockerTester.new(tests).run
data/test/helper.rb ADDED
@@ -0,0 +1,200 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ require 'minitest/autorun'
6
+ require 'minitest/spec'
7
+
8
+ require 'simplecov'
9
+ SimpleCov.start do
10
+ add_filter '/test/'
11
+ add_group 'Resources', 'lib/resources'
12
+ add_group 'Matchers', 'lib/matchers'
13
+ add_group 'Backends', 'lib/inspec/backend'
14
+ end
15
+
16
+ require 'inspec/resource'
17
+ require 'inspec/backend'
18
+ require 'inspec/profile'
19
+
20
+ class MockLoader
21
+ # pass the os identifier to emulate a specific operating system
22
+ def initialize(os = nil)
23
+ # collects emulation operating systems
24
+ @operating_systems = {
25
+ arch: { family: 'arch', release: nil, arch: nil },
26
+ centos5: { family: 'redhat', release: '5.11', arch: 'x86_64' },
27
+ centos6: { family: 'redhat', release: '6.6', arch: 'x86_64' },
28
+ centos7: { family: 'redhat', release: '7.1.1503', arch: 'x86_64' },
29
+ debian6: { family: 'debian', release: '6', arch: 'x86_64' },
30
+ debian7: { family: 'debian', release: '7', arch: 'x86_64' },
31
+ debian8: { family: 'debian', release: '8', arch: 'x86_64' },
32
+ freebsd9: { family: 'freebsd', release: '9', arch: 'amd64' },
33
+ freebsd10: { family: 'freebsd', release: '10', arch: 'amd64' },
34
+ osx104: { family: 'darwin', release: '10.10.4', arch: nil, name: 'mac_os_x' },
35
+ ubuntu1204: { family: 'ubuntu', release: '12.04', arch: 'x86_64' },
36
+ ubuntu1404: { family: 'ubuntu', release: '14.04', arch: 'x86_64' },
37
+ ubuntu1504: { family: 'ubuntu', release: '15.04', arch: 'x86_64' },
38
+ windows: { family: 'windows', release: nil, arch: nil },
39
+ undefined: { family: nil, release: nil, arch: nil },
40
+ }
41
+
42
+ # selects operating system
43
+ @os = @operating_systems[os || :ubuntu1404]
44
+ end
45
+
46
+ def backend
47
+ return @backend if defined?(@backend)
48
+ scriptpath = ::File.realpath(::File.dirname(__FILE__))
49
+
50
+ # create mock backend
51
+ @backend = Inspec::Backend.create({ backend: :mock })
52
+ mock = @backend.backend
53
+
54
+ # set os emulation
55
+ mock.mock_os(@os)
56
+
57
+ # create all mock files
58
+ local = Train.create('local').connection
59
+ mockfile = lambda { |x|
60
+ path = ::File.join(scriptpath, '/unit/mock/files', x)
61
+ local.file(path)
62
+ }
63
+ mockdir = lambda { |x|
64
+ md = Object.new
65
+
66
+ class << md
67
+ attr_accessor :isdir
68
+ end
69
+ md.isdir = x
70
+
71
+ def md.directory?
72
+ isdir
73
+ end
74
+ md
75
+ }
76
+
77
+ mock.files = {
78
+ '/proc/net/bonding/bond0' => mockfile.call('bond0'),
79
+ '/etc/ssh/ssh_config' => mockfile.call('ssh_config'),
80
+ '/etc/ssh/sshd_config' => mockfile.call('sshd_config'),
81
+ '/etc/passwd' => mockfile.call('passwd'),
82
+ '/etc/ntp.conf' => mockfile.call('ntp.conf'),
83
+ '/etc/login.defs' => mockfile.call('login.defs'),
84
+ '/etc/security/limits.conf' => mockfile.call('limits.conf'),
85
+ '/etc/inetd.conf' => mockfile.call('inetd.conf'),
86
+ '/etc/group' => mockfile.call('etcgroup'),
87
+ '/etc/audit/auditd.conf' => mockfile.call('auditd.conf'),
88
+ '/etc/mysql/my.cnf' => mockfile.call('mysql.conf'),
89
+ '/etc/mysql/mysql2.conf' => mockfile.call('mysql2.conf'),
90
+ 'kitchen.yml' => mockfile.call('kitchen.yml'),
91
+ 'example.csv' => mockfile.call('example.csv'),
92
+ 'policyfile.lock.json' => mockfile.call('policyfile.lock.json'),
93
+ '/sys/class/net/br0/bridge' => mockdir.call(true),
94
+ }
95
+
96
+ # create all mock commands
97
+ cmd = lambda {|x|
98
+ stdout = ::File.read(::File.join(scriptpath, '/unit/mock/cmd/'+x))
99
+ mock.mock_command('', stdout, '', 0)
100
+ }
101
+
102
+ empty = lambda {
103
+ mock.mock_command('', '', '', 0)
104
+ }
105
+
106
+ mock.commands = {
107
+ 'ps aux' => cmd.call('ps-aux'),
108
+ 'type win_secpol.cfg' => cmd.call('secedit-export'),
109
+ 'secedit /export /cfg win_secpol.cfg' => cmd.call('success'),
110
+ 'del win_secpol.cfg' => cmd.call('success'),
111
+ 'su - root -c \'echo $PATH\'' => cmd.call('PATH'),
112
+ '(Get-Item \'Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Schedule\').GetValue(\'Start\')' => cmd.call('reg_schedule'),
113
+ 'Auditpol /get /subcategory:\'User Account Management\' /r' => cmd.call('auditpol'),
114
+ '/sbin/auditctl -l' => cmd.call('auditctl'),
115
+ 'yum -v repolist all' => cmd.call('yum-repolist-all'),
116
+ 'dpkg -s curl' => cmd.call('dpkg-s-curl'),
117
+ 'rpm -qia curl' => cmd.call('rpm-qia-curl'),
118
+ 'pacman -Qi curl' => cmd.call('pacman-qi-curl'),
119
+ 'gem list --local -a -q ^rubocop$' => cmd.call('gem-list-local-a-q-rubocop'),
120
+ 'npm ls -g --json bower' => cmd.call('npm-ls-g--json-bower'),
121
+ 'pip show jinja2' => cmd.call('pip-show-jinja2'),
122
+ "Get-Package -Name 'Mozilla Firefox' | ConvertTo-Json" => cmd.call('get-package-firefox'),
123
+ "Get-Package -Name 'Ruby 2.1.6-p336-x64' | ConvertTo-Json" => cmd.call('get-package-ruby'),
124
+ "New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Service -Value (Get-Service -Name dhcp| Select-Object -Property Name, DisplayName, Status) -PassThru | Add-Member -MemberType NoteProperty -Name WMI -Value (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq 'dhcp' -or $_.DisplayName -eq 'dhcp'} | Select-Object -Property StartMode) -PassThru | ConvertTo-Json" => cmd.call('get-service-dhcp'),
125
+ "Get-WindowsFeature | Where-Object {$_.Name -eq 'dhcp' -or $_.DisplayName -eq 'dhcp'} | Select-Object -Property Name,DisplayName,Description,Installed,InstallState | ConvertTo-Json" => cmd.call('get-windows-feature'),
126
+ 'lsmod' => cmd.call('lsmod'),
127
+ '/sbin/sysctl -q -n net.ipv4.conf.all.forwarding' => cmd.call('sbin_sysctl'),
128
+ # ports on windows
129
+ 'Get-NetTCPConnection | Select-Object -Property State, Caption, Description, LocalAddress, LocalPort, RemoteAddress, RemotePort, DisplayName, Status | ConvertTo-Json' => cmd.call('get-net-tcpconnection'),
130
+ # ports on mac
131
+ 'lsof -nP -iTCP -iUDP -sTCP:LISTEN' => cmd.call('lsof-np-itcp'),
132
+ # ports on linux
133
+ 'netstat -tulpen' => cmd.call('netstat-tulpen'),
134
+ # ports on freebsd
135
+ 'sockstat -46l' => cmd.call('sockstat'),
136
+ # packages on windows
137
+ "Get-WmiObject -Class Win32_Product | Where-Object {$_.Name -eq 'Microsoft Visual C++ 2008 Redistributable - x64 9.0.30729.6161'} | Select-Object -Property Name,Version,Vendor,PackageCode,Caption,Description | ConvertTo-Json" => cmd.call('win32_product'),
138
+ # service status upstart on ubuntu
139
+ 'initctl status ssh' => cmd.call('initctl-status-ssh'),
140
+ # service config for upstart on ubuntu
141
+ 'initctl show-config ssh' => cmd.call('initctl-show-config-ssh'),
142
+ # show ssh service Centos 7
143
+ 'systemctl show --all sshd' => cmd.call('systemctl-show-all-sshd'),
144
+ # services on macos
145
+ 'launchctl list' => cmd.call('launchctl-list'),
146
+ # services on freebsd 10
147
+ 'service -e' => cmd.call('service-e'),
148
+ 'service sendmail onestatus' => cmd.call('service-sendmail-onestatus'),
149
+ # services for system 5 e.g. centos6, debian 6
150
+ 'service sshd status' => cmd.call('service-sshd-status'),
151
+ 'find /etc/rc*.d -name S*' => cmd.call('find-etc-rc-d-name-S'),
152
+ 'ls -1 /etc/init.d/' => cmd.call('ls-1-etc-init.d'),
153
+ # user information for linux
154
+ 'id root' => cmd.call('id-root'),
155
+ 'getent passwd root' => cmd.call('getent-passwd-root'),
156
+ 'chage -l root' => cmd.call('chage-l-root'),
157
+ # user info for mac
158
+ 'id chartmann' => cmd.call('id-chartmann'),
159
+ 'dscl -q . -read /Users/chartmann NFSHomeDirectory PrimaryGroupID RecordName UniqueID UserShell' => cmd.call('dscl'),
160
+ # user info for freebsd
161
+ 'pw usershow root -7' => cmd.call('pw-usershow-root-7'),
162
+ # user info for windows
163
+ '650b6b72a66316418b25421a54afe21a230704558082914c54711904bb10e370' => cmd.call('GetUserAccount'),
164
+ # group info for windows
165
+ 'Get-WmiObject Win32_Group | Select-Object -Property Caption, Domain, Name, SID, LocalAccount | ConvertTo-Json' => cmd.call('GetWin32Group'),
166
+ # network interface
167
+ '9e80f048a1af5a0f6ab8a465e46ea5ed5ba6587e9b5e54a7a0c0a1a02bb6f663' => cmd.call('find-net-interface'),
168
+ 'c33821dece09c8b334e03a5bb9daefdf622007f73af4932605e758506584ec3f' => empty.call,
169
+ 'Get-NetAdapter | Select-Object -Property Name, InterfaceDescription, Status, State, MacAddress, LinkSpeed, ReceiveLinkSpeed, TransmitLinkSpeed, Virtual | ConvertTo-Json' => cmd.call('Get-NetAdapter'),
170
+ # bridge on linux
171
+ 'ls -1 /sys/class/net/br0/brif/' => cmd.call('ls-sys-class-net-br'),
172
+ # bridge on Windows
173
+ 'Get-NetAdapterBinding -ComponentID ms_bridge | Get-NetAdapter | Select-Object -Property Name, InterfaceDescription | ConvertTo-Json' => cmd.call('get-netadapter-binding-bridge'),
174
+ # host for Windows
175
+ 'Resolve-DnsName –Type A microsoft.com | ConvertTo-Json' => cmd.call('Resolve-DnsName'),
176
+ 'Test-NetConnection -ComputerName microsoft.com | Select-Object -Property ComputerName, PingSucceeded | ConvertTo-Json' => cmd.call('Test-NetConnection'),
177
+ # host for Linux
178
+ 'getent hosts example.com' => cmd.call('getent-hosts-example.com'),
179
+ 'ping -w 1 -c 1 example.com' => cmd.call('ping-example.com'),
180
+ # apt
181
+ "find /etc/apt/ -name *.list -exec sh -c 'cat {} || echo -n' \\;" => cmd.call('etc-apt'),
182
+ # iptables
183
+ 'iptables -S' => cmd.call('iptables-s'),
184
+ }
185
+
186
+ @backend
187
+ end
188
+
189
+ # loads a resource class and instantiates the class with the given arguments
190
+ def load_resource(resource, *args)
191
+ # initialize resource with backend and parameters
192
+ @resource_class = Inspec::Resource.registry[resource]
193
+ @resource = @resource_class.new(backend, resource, *args)
194
+ end
195
+ end
196
+
197
+ def load_resource(*args)
198
+ m = MockLoader.new(:ubuntu1404)
199
+ m.send('load_resource', *args)
200
+ end