chef 12.4.3-universal-mingw32 → 12.5.1-universal-mingw32

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 (320) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +21 -25
  3. data/Gemfile +46 -0
  4. data/README.md +4 -4
  5. data/Rakefile +4 -110
  6. data/bin/chef-service-manager +3 -1
  7. data/distro/common/html/knife_cookbook_site.html +18 -18
  8. data/distro/common/man/man1/knife-cookbook-site.1 +11 -11
  9. data/lib/chef.rb +1 -1
  10. data/lib/chef/application.rb +1 -1
  11. data/lib/chef/application/apply.rb +19 -1
  12. data/lib/chef/application/client.rb +11 -5
  13. data/lib/chef/application/knife.rb +2 -2
  14. data/lib/chef/application/solo.rb +1 -1
  15. data/lib/chef/application/windows_service_manager.rb +19 -12
  16. data/lib/chef/chef_class.rb +46 -0
  17. data/lib/chef/chef_fs/config.rb +22 -24
  18. data/lib/chef/chef_fs/data_handler/client_data_handler.rb +3 -1
  19. data/lib/chef/chef_fs/file_pattern.rb +4 -15
  20. data/lib/chef/chef_fs/file_system/acl_dir.rb +3 -4
  21. data/lib/chef/chef_fs/file_system/acls_dir.rb +5 -1
  22. data/lib/chef/chef_fs/file_system/base_fs_dir.rb +0 -5
  23. data/lib/chef/chef_fs/file_system/base_fs_object.rb +5 -2
  24. data/lib/chef/chef_fs/file_system/chef_repository_file_system_cookbook_dir.rb +2 -9
  25. data/lib/chef/chef_fs/file_system/chef_repository_file_system_cookbook_entry.rb +2 -9
  26. data/lib/chef/chef_fs/file_system/chef_repository_file_system_cookbooks_dir.rb +10 -17
  27. data/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb +1 -12
  28. data/lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb +15 -11
  29. data/lib/chef/chef_fs/file_system/chef_server_root_dir.rb +8 -2
  30. data/lib/chef/chef_fs/file_system/cookbook_dir.rb +4 -4
  31. data/lib/chef/chef_fs/file_system/cookbooks_acl_dir.rb +1 -1
  32. data/lib/chef/chef_fs/file_system/cookbooks_dir.rb +3 -11
  33. data/lib/chef/chef_fs/file_system/data_bags_dir.rb +3 -5
  34. data/lib/chef/chef_fs/file_system/environments_dir.rb +1 -1
  35. data/lib/chef/chef_fs/file_system/file_system_entry.rb +7 -4
  36. data/lib/chef/chef_fs/file_system/memory_dir.rb +2 -3
  37. data/lib/chef/chef_fs/file_system/multiplexed_dir.rb +15 -0
  38. data/lib/chef/chef_fs/file_system/nodes_dir.rb +1 -1
  39. data/lib/chef/chef_fs/file_system/organization_members_entry.rb +2 -2
  40. data/lib/chef/chef_fs/file_system/rest_list_dir.rb +4 -9
  41. data/lib/chef/chef_fs/knife.rb +35 -7
  42. data/lib/chef/chef_fs/path_utils.rb +65 -34
  43. data/lib/chef/client.rb +2 -3
  44. data/lib/chef/config.rb +34 -2
  45. data/lib/chef/{mixin/wstring.rb → constants.rb} +9 -13
  46. data/lib/chef/cookbook/metadata.rb +25 -3
  47. data/lib/chef/cookbook/synchronizer.rb +1 -1
  48. data/lib/chef/cookbook_site_streaming_uploader.rb +1 -1
  49. data/lib/chef/cookbook_version.rb +3 -3
  50. data/lib/chef/delayed_evaluator.rb +21 -0
  51. data/lib/chef/deprecation/mixin/template.rb +1 -2
  52. data/lib/chef/deprecation/provider/cookbook_file.rb +1 -1
  53. data/lib/chef/deprecation/provider/file.rb +1 -1
  54. data/lib/chef/deprecation/provider/remote_directory.rb +52 -0
  55. data/lib/chef/deprecation/provider/remote_file.rb +1 -2
  56. data/lib/chef/deprecation/provider/template.rb +1 -1
  57. data/lib/chef/deprecation/warnings.rb +3 -4
  58. data/lib/chef/dsl/reboot_pending.rb +3 -2
  59. data/lib/chef/dsl/recipe.rb +26 -7
  60. data/lib/chef/dsl/resources.rb +2 -2
  61. data/lib/chef/event_dispatch/base.rb +51 -22
  62. data/lib/chef/event_dispatch/dispatcher.rb +21 -6
  63. data/lib/chef/event_dispatch/dsl.rb +64 -0
  64. data/lib/chef/exceptions.rb +28 -1
  65. data/lib/chef/file_content_management/tempfile.rb +1 -1
  66. data/lib/chef/formatters/base.rb +3 -0
  67. data/lib/chef/formatters/doc.rb +56 -6
  68. data/lib/chef/formatters/error_inspectors/compile_error_inspector.rb +36 -0
  69. data/lib/chef/formatters/minimal.rb +2 -2
  70. data/lib/chef/guard_interpreter/resource_guard_interpreter.rb +3 -1
  71. data/lib/chef/http/http_request.rb +1 -1
  72. data/lib/chef/knife.rb +35 -55
  73. data/lib/chef/knife/bootstrap.rb +41 -0
  74. data/lib/chef/knife/bootstrap/chef_vault_handler.rb +1 -0
  75. data/lib/chef/knife/bootstrap/client_builder.rb +16 -0
  76. data/lib/chef/knife/bootstrap/templates/README.md +3 -4
  77. data/lib/chef/knife/bootstrap/templates/chef-full.erb +1 -1
  78. data/lib/chef/knife/cookbook_create.rb +1 -1
  79. data/lib/chef/knife/cookbook_site_download.rb +1 -1
  80. data/lib/chef/knife/cookbook_site_install.rb +1 -1
  81. data/lib/chef/knife/cookbook_site_share.rb +6 -6
  82. data/lib/chef/knife/cookbook_site_unshare.rb +2 -2
  83. data/lib/chef/knife/core/bootstrap_context.rb +12 -4
  84. data/lib/chef/knife/core/custom_manifest_loader.rb +69 -0
  85. data/lib/chef/knife/core/gem_glob_loader.rb +138 -0
  86. data/lib/chef/knife/core/hashed_command_loader.rb +80 -0
  87. data/lib/chef/knife/core/node_presenter.rb +24 -1
  88. data/lib/chef/knife/core/object_loader.rb +1 -0
  89. data/lib/chef/knife/core/subcommand_loader.rb +131 -146
  90. data/lib/chef/knife/node_run_list_remove.rb +12 -1
  91. data/lib/chef/knife/null.rb +10 -0
  92. data/lib/chef/knife/rehash.rb +62 -0
  93. data/lib/chef/knife/search.rb +3 -3
  94. data/lib/chef/knife/ssh.rb +52 -30
  95. data/lib/chef/knife/ssl_check.rb +3 -2
  96. data/lib/chef/knife/user_edit.rb +1 -2
  97. data/lib/chef/local_mode.rb +5 -0
  98. data/lib/chef/log.rb +5 -1
  99. data/lib/chef/mixin/deprecation.rb +8 -8
  100. data/lib/chef/mixin/params_validate.rb +362 -135
  101. data/lib/chef/mixin/template.rb +48 -0
  102. data/lib/chef/mixin/which.rb +1 -1
  103. data/lib/chef/mixin/wide_string.rb +72 -0
  104. data/lib/chef/mixin/windows_architecture_helper.rb +15 -39
  105. data/lib/chef/mixin/windows_env_helper.rb +4 -1
  106. data/lib/chef/monkey_patches/webrick-utils.rb +51 -0
  107. data/lib/chef/monkey_patches/win32/registry.rb +72 -0
  108. data/lib/chef/node.rb +116 -3
  109. data/lib/chef/node_map.rb +2 -2
  110. data/lib/chef/platform/handler_map.rb +0 -5
  111. data/lib/chef/platform/provider_mapping.rb +5 -6
  112. data/lib/chef/platform/query_helpers.rb +46 -4
  113. data/lib/chef/platform/rebooter.rb +1 -1
  114. data/lib/chef/platform/service_helpers.rb +30 -32
  115. data/lib/chef/policy_builder.rb +1 -8
  116. data/lib/chef/policy_builder/dynamic.rb +186 -0
  117. data/lib/chef/policy_builder/expand_node_object.rb +30 -15
  118. data/lib/chef/policy_builder/policyfile.rb +155 -18
  119. data/lib/chef/property.rb +568 -0
  120. data/lib/chef/provider.rb +222 -13
  121. data/lib/chef/provider/batch.rb +8 -0
  122. data/lib/chef/provider/deploy.rb +5 -7
  123. data/lib/chef/provider/directory.rb +14 -2
  124. data/lib/chef/provider/dsc_resource.rb +5 -9
  125. data/lib/chef/provider/group/pw.rb +1 -1
  126. data/lib/chef/provider/ifconfig.rb +2 -2
  127. data/lib/chef/provider/lwrp_base.rb +1 -75
  128. data/lib/chef/provider/mount.rb +7 -3
  129. data/lib/chef/provider/package.rb +1 -1
  130. data/lib/chef/provider/package/dpkg.rb +5 -11
  131. data/lib/chef/provider/package/rpm.rb +2 -2
  132. data/lib/chef/provider/package/rubygems.rb +1 -1
  133. data/lib/chef/provider/package/windows/msi.rb +2 -2
  134. data/lib/chef/provider/package/yum.rb +17 -5
  135. data/lib/chef/provider/powershell_script.rb +59 -23
  136. data/lib/chef/provider/registry_key.rb +5 -5
  137. data/lib/chef/provider/remote_directory.rb +190 -102
  138. data/lib/chef/provider/service.rb +12 -2
  139. data/lib/chef/provider/service/aix.rb +1 -1
  140. data/lib/chef/provider/service/debian.rb +3 -5
  141. data/lib/chef/provider/service/freebsd.rb +1 -1
  142. data/lib/chef/provider/service/gentoo.rb +3 -3
  143. data/lib/chef/provider/service/init.rb +3 -3
  144. data/lib/chef/provider/service/insserv.rb +2 -4
  145. data/lib/chef/provider/service/invokercd.rb +2 -4
  146. data/lib/chef/provider/service/macosx.rb +5 -1
  147. data/lib/chef/provider/service/openbsd.rb +2 -1
  148. data/lib/chef/provider/service/redhat.rb +52 -16
  149. data/lib/chef/provider/service/simple.rb +2 -2
  150. data/lib/chef/provider/service/systemd.rb +3 -5
  151. data/lib/chef/provider/service/upstart.rb +4 -6
  152. data/lib/chef/provider/subversion.rb +13 -7
  153. data/lib/chef/provider/template/content.rb +16 -6
  154. data/lib/chef/provider/user/solaris.rb +32 -4
  155. data/lib/chef/provider/windows_script.rb +3 -5
  156. data/lib/chef/provider_resolver.rb +2 -2
  157. data/lib/chef/recipe.rb +1 -8
  158. data/lib/chef/resource.rb +563 -90
  159. data/lib/chef/resource/action_class.rb +83 -0
  160. data/lib/chef/resource/chef_gem.rb +3 -3
  161. data/lib/chef/resource/deploy.rb +8 -2
  162. data/lib/chef/resource/dsc_script.rb +2 -0
  163. data/lib/chef/resource/file/verification.rb +7 -1
  164. data/lib/chef/resource/lwrp_base.rb +1 -7
  165. data/lib/chef/resource/registry_key.rb +1 -1
  166. data/lib/chef/resource/service.rb +10 -2
  167. data/lib/chef/resource/subversion.rb +5 -0
  168. data/lib/chef/resource/windows_script.rb +6 -2
  169. data/lib/chef/resource/yum_package.rb +10 -1
  170. data/lib/chef/resource_resolver.rb +3 -3
  171. data/lib/chef/run_context.rb +402 -83
  172. data/lib/chef/run_list/versioned_recipe_list.rb +15 -0
  173. data/lib/chef/run_lock.rb +30 -21
  174. data/lib/chef/util/powershell/ps_credential.rb +4 -0
  175. data/lib/chef/util/windows.rb +0 -32
  176. data/lib/chef/util/windows/net_group.rb +85 -106
  177. data/lib/chef/util/windows/net_use.rb +35 -71
  178. data/lib/chef/util/windows/net_user.rb +0 -1
  179. data/lib/chef/util/windows/volume.rb +19 -19
  180. data/lib/chef/version.rb +3 -3
  181. data/lib/chef/win32/api.rb +1 -0
  182. data/lib/chef/win32/api/file.rb +20 -0
  183. data/lib/chef/win32/api/net.rb +163 -43
  184. data/lib/chef/win32/api/registry.rb +51 -0
  185. data/lib/chef/win32/api/system.rb +23 -0
  186. data/lib/chef/win32/api/unicode.rb +0 -43
  187. data/lib/chef/win32/crypto.rb +2 -1
  188. data/lib/chef/win32/file.rb +28 -3
  189. data/lib/chef/win32/mutex.rb +1 -2
  190. data/lib/chef/win32/net.rb +162 -8
  191. data/lib/chef/win32/process.rb +13 -0
  192. data/lib/chef/win32/registry.rb +35 -30
  193. data/lib/chef/win32/security.rb +1 -1
  194. data/lib/chef/win32/security/token.rb +1 -1
  195. data/lib/chef/win32/system.rb +62 -0
  196. data/lib/chef/win32/unicode.rb +7 -2
  197. data/lib/chef/win32/version.rb +0 -4
  198. data/lib/chef/workstation_config_loader.rb +3 -158
  199. data/spec/data/cookbooks/openldap/templates/default/helpers.erb +14 -0
  200. data/spec/data/cookbooks/openldap/templates/default/nested_openldap_partials.erb +1 -0
  201. data/spec/data/cookbooks/openldap/templates/default/nested_partial.erb +1 -0
  202. data/spec/data/dsc_lcm.pfx +0 -0
  203. data/spec/data/run_context/cookbooks/include/recipes/default.rb +24 -0
  204. data/spec/data/run_context/cookbooks/include/recipes/includee.rb +3 -0
  205. data/spec/functional/dsl/reboot_pending_spec.rb +33 -43
  206. data/spec/functional/knife/cookbook_delete_spec.rb +17 -7
  207. data/spec/functional/knife/ssh_spec.rb +16 -0
  208. data/spec/functional/rebooter_spec.rb +1 -1
  209. data/spec/functional/resource/deploy_revision_spec.rb +1 -1
  210. data/spec/functional/resource/dsc_resource_spec.rb +2 -0
  211. data/spec/functional/resource/dsc_script_spec.rb +91 -2
  212. data/spec/functional/resource/group_spec.rb +67 -44
  213. data/spec/functional/resource/{powershell_spec.rb → powershell_script_spec.rb} +107 -18
  214. data/spec/functional/resource/windows_service_spec.rb +1 -1
  215. data/spec/functional/run_lock_spec.rb +368 -189
  216. data/spec/functional/win32/{registry_helper_spec.rb → registry_spec.rb} +16 -23
  217. data/spec/functional/win32/service_manager_spec.rb +2 -2
  218. data/spec/integration/client/client_spec.rb +51 -0
  219. data/spec/integration/knife/chef_repo_path_spec.rb +13 -11
  220. data/spec/integration/knife/download_spec.rb +4 -0
  221. data/spec/integration/knife/list_spec.rb +8 -0
  222. data/spec/integration/knife/upload_spec.rb +1 -1
  223. data/spec/integration/recipes/recipe_dsl_spec.rb +1 -16
  224. data/spec/integration/recipes/remote_directory.rb +74 -0
  225. data/spec/integration/recipes/resource_action_spec.rb +363 -0
  226. data/spec/integration/recipes/resource_converge_if_changed_spec.rb +423 -0
  227. data/spec/integration/recipes/resource_load_spec.rb +206 -0
  228. data/spec/spec_helper.rb +9 -0
  229. data/spec/support/platform_helpers.rb +13 -0
  230. data/spec/support/shared/context/win32.rb +34 -0
  231. data/spec/support/shared/functional/win32_service.rb +2 -1
  232. data/spec/support/shared/functional/windows_script.rb +63 -26
  233. data/spec/support/shared/unit/mock_shellout.rb +46 -0
  234. data/spec/support/shared/unit/provider/file.rb +10 -4
  235. data/spec/unit/application/client_spec.rb +16 -3
  236. data/spec/unit/application/knife_spec.rb +2 -2
  237. data/spec/unit/application/solo_spec.rb +4 -3
  238. data/spec/unit/chef_class_spec.rb +23 -4
  239. data/spec/unit/chef_fs/path_util_spec.rb +108 -0
  240. data/spec/unit/client_spec.rb +6 -1
  241. data/spec/unit/config_spec.rb +31 -0
  242. data/spec/unit/cookbook/metadata_spec.rb +23 -3
  243. data/spec/unit/cookbook/syntax_check_spec.rb +3 -0
  244. data/spec/unit/deprecation_spec.rb +3 -6
  245. data/spec/unit/dsl/reboot_pending_spec.rb +12 -6
  246. data/spec/unit/event_dispatch/dispatcher_spec.rb +65 -3
  247. data/spec/unit/event_dispatch/dsl_spec.rb +83 -0
  248. data/spec/unit/formatters/doc_spec.rb +32 -0
  249. data/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb +26 -0
  250. data/spec/unit/json_compat_spec.rb +4 -3
  251. data/spec/unit/knife/bootstrap/client_builder_spec.rb +27 -0
  252. data/spec/unit/knife/bootstrap_spec.rb +55 -3
  253. data/spec/unit/knife/cookbook_site_share_spec.rb +3 -3
  254. data/spec/unit/knife/core/bootstrap_context_spec.rb +21 -4
  255. data/spec/unit/knife/core/custom_manifest_loader_spec.rb +41 -0
  256. data/spec/unit/knife/core/gem_glob_loader_spec.rb +210 -0
  257. data/spec/unit/knife/core/hashed_command_loader_spec.rb +93 -0
  258. data/spec/unit/knife/core/subcommand_loader_spec.rb +16 -192
  259. data/spec/unit/knife/node_run_list_remove_spec.rb +17 -0
  260. data/spec/unit/knife/ssl_check_spec.rb +4 -0
  261. data/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb +10 -10
  262. data/spec/unit/mixin/params_validate_spec.rb +4 -2
  263. data/spec/unit/mixin/template_spec.rb +5 -1
  264. data/spec/unit/mixin/windows_architecture_helper_spec.rb +13 -8
  265. data/spec/unit/node_spec.rb +220 -0
  266. data/spec/unit/platform/query_helpers_spec.rb +146 -3
  267. data/spec/unit/policy_builder/dynamic_spec.rb +275 -0
  268. data/spec/unit/policy_builder/expand_node_object_spec.rb +37 -38
  269. data/spec/unit/policy_builder/policyfile_spec.rb +260 -46
  270. data/spec/unit/property/state_spec.rb +506 -0
  271. data/spec/unit/property/validation_spec.rb +663 -0
  272. data/spec/unit/property_spec.rb +1094 -0
  273. data/spec/unit/provider/deploy_spec.rb +5 -5
  274. data/spec/unit/provider/directory_spec.rb +35 -0
  275. data/spec/unit/provider/dsc_resource_spec.rb +3 -10
  276. data/spec/unit/provider/ifconfig_spec.rb +22 -2
  277. data/spec/unit/provider/mount/aix_spec.rb +2 -1
  278. data/spec/unit/provider/mount/mount_spec.rb +6 -0
  279. data/spec/unit/provider/mount/windows_spec.rb +14 -0
  280. data/spec/unit/provider/mount_spec.rb +12 -1
  281. data/spec/unit/provider/package/dpkg_spec.rb +8 -1
  282. data/spec/unit/provider/package/rpm_spec.rb +18 -1
  283. data/spec/unit/provider/package/rubygems_spec.rb +18 -0
  284. data/spec/unit/provider/package/yum_spec.rb +97 -24
  285. data/spec/unit/provider/powershell_script_spec.rb +106 -0
  286. data/spec/unit/provider/registry_key_spec.rb +12 -0
  287. data/spec/unit/provider/remote_directory_spec.rb +1 -2
  288. data/spec/unit/provider/service/aix_service_spec.rb +3 -3
  289. data/spec/unit/provider/service/gentoo_service_spec.rb +4 -4
  290. data/spec/unit/provider/service/macosx_spec.rb +4 -4
  291. data/spec/unit/provider/service/openbsd_service_spec.rb +10 -8
  292. data/spec/unit/provider/service/redhat_spec.rb +88 -8
  293. data/spec/unit/provider/service/upstart_service_spec.rb +11 -7
  294. data/spec/unit/provider/service/windows_spec.rb +211 -200
  295. data/spec/unit/provider/subversion_spec.rb +50 -31
  296. data/spec/unit/provider/template/content_spec.rb +93 -2
  297. data/spec/unit/provider/user/solaris_spec.rb +66 -9
  298. data/spec/unit/provider_resolver_spec.rb +707 -650
  299. data/spec/unit/provider_spec.rb +1 -3
  300. data/spec/unit/recipe_spec.rb +0 -4
  301. data/spec/unit/resource/deploy_spec.rb +7 -1
  302. data/spec/unit/resource/dsc_script_spec.rb +4 -0
  303. data/spec/unit/resource/file/verification_spec.rb +33 -5
  304. data/spec/unit/resource/{powershell_spec.rb → powershell_script_spec.rb} +17 -13
  305. data/spec/unit/resource/service_spec.rb +4 -4
  306. data/spec/unit/resource/subversion_spec.rb +4 -0
  307. data/spec/unit/resource/yum_package_spec.rb +10 -1
  308. data/spec/unit/resource_spec.rb +2 -2
  309. data/spec/unit/run_context/child_run_context_spec.rb +133 -0
  310. data/spec/unit/run_context_spec.rb +7 -0
  311. data/spec/unit/run_list/versioned_recipe_list_spec.rb +5 -0
  312. data/spec/unit/win32/registry_spec.rb +394 -0
  313. data/tasks/external_tests.rb +47 -23
  314. data/tasks/maintainers.rb +155 -14
  315. metadata +64 -53
  316. data/lib/chef/knife/bootstrap/templates/archlinux-gems.erb +0 -76
  317. data/lib/chef/knife/bootstrap/templates/chef-aix.erb +0 -72
  318. data/spec/unit/provider/powershell_spec.rb +0 -80
  319. data/spec/unit/registry_helper_spec.rb +0 -376
  320. data/spec/unit/workstation_config_loader_spec.rb +0 -283
@@ -18,7 +18,7 @@
18
18
 
19
19
  require 'spec_helper'
20
20
 
21
- describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_gem_only do
21
+ describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_gem_only, :appveyor_only do
22
22
 
23
23
  include_context "using Win32::Service"
24
24
 
@@ -34,211 +34,232 @@ describe Chef::RunLock do
34
34
  let(:lockfile){ "#{random_temp_root}/this/long/path/does/not/exist/chef-client-running.pid" }
35
35
 
36
36
  # make sure to start with a clean slate.
37
- before(:each){ FileUtils.rm_r(random_temp_root) if File.exist?(random_temp_root) }
38
- after(:each){ FileUtils.rm_r(random_temp_root) }
37
+ before(:each){ log_event("rm -rf before"); FileUtils.rm_r(random_temp_root) if File.exist?(random_temp_root) }
38
+ after(:each){ log_event("rm -rf after"); FileUtils.rm_r(random_temp_root) if File.exist?(random_temp_root) }
39
39
 
40
- def wait_on_lock
41
- tries = 0
42
- until File.exist?(lockfile)
43
- raise "Lockfile never created, abandoning test" if tries > 10
44
- tries += 1
45
- sleep 0.1
46
- end
40
+ def log_event(message, time=Time.now.strftime("%H:%M:%S.%L"))
41
+ events << [ message, time ]
47
42
  end
48
-
49
- ##
50
- # Side channel via a pipe allows child processes to send errors to the parent
51
-
52
- # Don't lazy create the pipe or else we might not share it with subprocesses
53
- let!(:error_pipe) do
54
- r,w = IO.pipe
55
- w.sync = true
56
- [r,w]
43
+ def events
44
+ @events ||= []
57
45
  end
58
46
 
59
- let(:error_read) { error_pipe[0] }
60
- let(:error_write) { error_pipe[1] }
61
-
62
- after do
63
- error_read.close unless error_read.closed?
64
- error_write.close unless error_write.closed?
47
+ WAIT_ON_LOCK_TIME = 1.0
48
+ def wait_on_lock
49
+ Timeout::timeout(WAIT_ON_LOCK_TIME) do
50
+ until File.exist?(lockfile)
51
+ sleep 0.1
52
+ end
53
+ end
54
+ rescue Timeout::Error
55
+ raise "Lockfile never created, abandoning test"
65
56
  end
66
57
 
67
- # Send a RuntimeError from the child process to the parent process. Also
68
- # prints error to $stdout, just in case something goes wrong with the error
69
- # marshaling stuff.
70
- def send_side_channel_error(message)
71
- $stderr.puts(message)
72
- $stderr.puts(caller)
73
- e = RuntimeError.new(message)
74
- error_write.print(Marshal.dump(e))
75
- end
58
+ CLIENT_PROCESS_TIMEOUT = 10
59
+ BREATHING_ROOM = 1
76
60
 
77
- # Read the error (if any) from the error channel. If a marhaled error is
78
- # present, it is unmarshaled and raised (which will fail the test)
79
- def raise_side_channel_error!
80
- error_write.close
81
- err = error_read.read
82
- error_read.close
61
+ # ClientProcess is defined below
62
+ let!(:p1) { ClientProcess.new(self, 'p1') }
63
+ let!(:p2) { ClientProcess.new(self, 'p2') }
64
+ after(:each) do |example|
83
65
  begin
84
- # ArgumentError from Marshal.load indicates no data, which we assume
85
- # means no error in child process.
86
- raise Marshal.load(err)
87
- rescue ArgumentError
88
- nil
66
+ p1.stop
67
+ p2.stop
68
+ rescue
69
+ example.exception = $!
70
+ raise
71
+ ensure
72
+ if example.exception
73
+ print_events
74
+ end
89
75
  end
90
76
  end
91
77
 
92
- ##
93
- # Interprocess synchronization via a pipe. This allows us to control the
94
- # state of the processes competing over the lock without relying on sleep.
95
-
96
- let!(:sync_pipe) do
97
- r,w = IO.pipe
98
- w.sync = true
99
- [r,w]
100
- end
101
- let(:sync_read) { sync_pipe[0] }
102
- let(:sync_write) { sync_pipe[1] }
103
-
104
- after do
105
- sync_read.close unless sync_read.closed?
106
- sync_write.close unless sync_write.closed?
107
- end
108
-
109
- # Wait on synchronization signal. If not received within the timeout, an
110
- # error is sent via the error channel, and the process exits.
111
- def sync_wait
112
- if IO.select([sync_read], nil, nil, 20).nil?
113
- # timeout reading from the sync pipe.
114
- send_side_channel_error("Error syncing processes in run lock test (timeout)")
115
- exit!(1)
116
- else
117
- sync_read.getc
78
+ def print_events
79
+ # Consume any remaining events that went on the channel and print them all
80
+ p1.last_event
81
+ p2.last_event
82
+ events.each_with_index.sort_by { |(message, time), index| [ time, index ] }.each do |(message, time), index|
83
+ print "#{time} #{message}\n"
118
84
  end
119
85
  end
120
86
 
121
- # Sends a character in the sync pipe, which wakes ("unlocks") another
122
- # process that is waiting on the sync signal
123
- def sync_send
124
- sync_write.putc("!")
125
- sync_write.flush
126
- end
127
-
128
- ##
129
- # IPC to record test results in a pipe. Tests can read pipe contents to
130
- # check that operations occur in the expected order.
131
-
132
- let!(:results_pipe) do
133
- r,w = IO.pipe
134
- w.sync = true
135
- [r,w]
136
- end
137
- let(:results_read) { results_pipe[0] }
138
- let(:results_write) { results_pipe[1] }
139
-
140
- after do
141
- results_read.close unless results_read.closed?
142
- results_write.close unless results_write.closed?
143
- end
144
-
145
- # writes the message to the results pipe for later checking.
146
- # note that nothing accounts for the pipe filling and waiting forever on a
147
- # read or write call, so don't put too much data in.
148
- def record(message)
149
- results_write.puts(message)
150
- results_write.flush
151
- end
152
-
153
- def results
154
- results_write.flush
155
- results_write.close
156
- message = results_read.read
157
- results_read.close
158
- message
159
- end
160
-
161
- ##
162
- # Run lock is the system under test
163
- let!(:run_lock) { Chef::RunLock.new(lockfile) }
164
-
165
- it "creates the full path to the lockfile" do
166
- expect { run_lock.acquire }.not_to raise_error
167
- expect(File).to exist(lockfile)
168
- end
169
-
170
- it "sets FD_CLOEXEC on the lockfile", :supports_cloexec => true do
171
- run_lock.acquire
172
- expect(run_lock.runlock.fcntl(Fcntl::F_GETFD, 0) & Fcntl::FD_CLOEXEC).to eq(Fcntl::FD_CLOEXEC)
173
- end
174
-
175
- it "allows only one chef client run per lockfile" do
176
- # First process, gets the lock and keeps it.
177
- p1 = fork do
178
- run_lock.acquire
179
- record "p1 has lock"
180
- # Wait until the other process is trying to get the lock:
181
- sync_wait
182
- # sleep a little bit to make process p2 wait on the lock
183
- sleep 2
184
- record "p1 releasing lock"
185
- run_lock.release
186
- exit!(0)
87
+ context "when the lockfile does not already exist" do
88
+ context "when a client creates the lockfile but has not yet acquired the lock" do
89
+ before { p1.run_to("created lock") }
90
+ shared_context "second client gets the lock" do
91
+ it "the lockfile is created" do
92
+ log_event("lockfile exists? #{File.exist?(lockfile)}")
93
+ expect(File.exist?(lockfile)).to be_truthy
94
+ end
95
+
96
+ it "the lockfile is not locked" do
97
+ run_lock = Chef::RunLock.new(lockfile)
98
+ begin
99
+ expect(run_lock.test).to be_truthy
100
+ ensure
101
+ run_lock.release
102
+ end
103
+ end
104
+
105
+ it "the lockfile is empty" do
106
+ expect(IO.read(lockfile)).to eq('')
107
+ end
108
+
109
+ context "and a second client gets the lock" do
110
+ before { p2.run_to("acquired lock") }
111
+ it "the first client does not get the lock until the second finishes" do
112
+ p1.run_to("acquired lock") do
113
+ p2.run_to_completion
114
+ end
115
+ end
116
+ it "and the first client tries to get the lock and the second is killed, the first client gets the lock immediately" do
117
+ p1.run_to("acquired lock") do
118
+ sleep BREATHING_ROOM
119
+ expect(p1.last_event).to match(/after (started|created lock)/)
120
+ p2.stop
121
+ end
122
+ p1.run_to_completion
123
+ end
124
+ end
125
+ end
126
+
127
+ context "and the second client has done nothing" do
128
+ include_context "second client gets the lock"
129
+ end
130
+
131
+ context "and the second client has created the lockfile but not yet acquired the lock" do
132
+ before { p2.run_to("created lock") }
133
+ include_context "second client gets the lock"
134
+ end
187
135
  end
188
136
 
189
- # Wait until p1 creates the lockfile
190
- wait_on_lock
191
-
192
- p2 = fork do
193
- # inform process p1 that we're trying to get the lock
194
- sync_send
195
- run_lock.acquire
196
- record "p2 has lock"
197
- run_lock.release
198
- exit!(0)
137
+ context "when a client acquires the lock but has not yet saved the pid" do
138
+ before { p1.run_to("acquired lock") }
139
+
140
+ it "the lockfile is created" do
141
+ log_event("lockfile exists? #{File.exist?(lockfile)}")
142
+ expect(File.exist?(lockfile)).to be_truthy
143
+ end
144
+
145
+ it "the lockfile is locked" do
146
+ run_lock = Chef::RunLock.new(lockfile)
147
+ begin
148
+ expect(run_lock.test).to be_falsey
149
+ ensure
150
+ run_lock.release
151
+ end
152
+ end
153
+
154
+ it "sets FD_CLOEXEC on the lockfile", :supports_cloexec => true do
155
+ run_lock = File.open(lockfile)
156
+ expect(run_lock.fcntl(Fcntl::F_GETFD, 0) & Fcntl::FD_CLOEXEC).to eq(Fcntl::FD_CLOEXEC)
157
+ end
158
+
159
+ it "the lockfile is empty" do
160
+ expect(IO.read(lockfile)).to eq('')
161
+ end
162
+
163
+ it "and a second client tries to acquire the lock, it doesn't get the lock until *after* the first client exits" do
164
+ # Start p2 and tell it to move forward in the background
165
+ p2.run_to("acquired lock") do
166
+ # While p2 is trying to acquire, wait a bit and then let p1 complete
167
+ sleep(BREATHING_ROOM)
168
+ expect(p2.last_event).to match(/after (started|created lock)/)
169
+ p1.run_to_completion
170
+ end
171
+
172
+ p2.run_to_completion
173
+ end
174
+
175
+ it "and a second client tries to get the lock and the first is killed, the second client gets the lock immediately" do
176
+ p2.run_to("acquired lock") do
177
+ sleep BREATHING_ROOM
178
+ expect(p2.last_event).to match(/after (started|created lock)/)
179
+ p1.stop
180
+ end
181
+ p2.run_to_completion
182
+ end
199
183
  end
200
184
 
201
- Process.waitpid2(p1)
202
- Process.waitpid2(p2)
203
-
204
- raise_side_channel_error!
205
-
206
- expected=<<-E
207
- p1 has lock
208
- p1 releasing lock
209
- p2 has lock
210
- E
211
- expect(results).to eq(expected)
212
- end
213
-
214
- it "clears the lock if the process dies unexpectedly" do
215
- p1 = fork do
216
- run_lock.acquire
217
- record "p1 has lock"
218
- sleep 60
219
- record "p1 still has lock"
220
- exit! 1
185
+ context "when a client acquires the lock and saves the pid" do
186
+ before { p1.run_to("saved pid") }
187
+
188
+ it "the lockfile is created" do
189
+ expect(File.exist?(lockfile)).to be_truthy
190
+ end
191
+
192
+ it "the lockfile is locked" do
193
+ run_lock = Chef::RunLock.new(lockfile)
194
+ begin
195
+ expect(run_lock.test).to be_falsey
196
+ ensure
197
+ run_lock.release
198
+ end
199
+ end
200
+
201
+ it "sets FD_CLOEXEC on the lockfile", :supports_cloexec => true do
202
+ run_lock = File.open(lockfile)
203
+ expect(run_lock.fcntl(Fcntl::F_GETFD, 0) & Fcntl::FD_CLOEXEC).to eq(Fcntl::FD_CLOEXEC)
204
+ end
205
+
206
+ it "the PID is in the lockfile" do
207
+ expect(IO.read(lockfile)).to eq p1.pid.to_s
208
+ end
209
+
210
+ it "and a second client tries to acquire the lock, it doesn't get the lock until *after* the first client exits" do
211
+ # Start p2 and tell it to move forward in the background
212
+ p2.run_to("acquired lock") do
213
+ # While p2 is trying to acquire, wait a bit and then let p1 complete
214
+ sleep(BREATHING_ROOM)
215
+ expect(p2.last_event).to match(/after (started|created lock)/)
216
+ p1.run_to_completion
217
+ end
218
+
219
+ p2.run_to_completion
220
+ end
221
+
222
+ it "when a second client tries to get the lock and the first is killed, the second client gets the lock immediately" do
223
+ p2.run_to("acquired lock") do
224
+ sleep BREATHING_ROOM
225
+ expect(p2.last_event).to match(/after (started|created lock)/)
226
+ p1.stop
227
+ end
228
+ p2.run_to_completion
229
+ end
221
230
  end
222
231
 
223
- wait_on_lock
224
- Process.kill(:KILL, p1)
225
- Process.waitpid2(p1)
226
-
227
- p2 = fork do
228
- run_lock.acquire
229
- record "p2 has lock"
230
- run_lock.release
231
- exit! 0
232
+ context "when a client acquires a lock and exits normally" do
233
+ before { p1.run_to_completion }
234
+
235
+ it "the lockfile remains" do
236
+ expect(File.exist?(lockfile)).to be_truthy
237
+ end
238
+
239
+ it "the lockfile is not locked" do
240
+ run_lock = Chef::RunLock.new(lockfile)
241
+ begin
242
+ expect(run_lock.test).to be_truthy
243
+ ensure
244
+ run_lock.release
245
+ end
246
+ end
247
+
248
+ it "the PID is in the lockfile" do
249
+ expect(IO.read(lockfile)).to eq p1.pid.to_s
250
+ end
251
+
252
+ it "and a second client tries to acquire the lock, it gets the lock immediately" do
253
+ p2.run_to_completion
254
+ end
232
255
  end
233
-
234
- Process.waitpid2(p2)
235
-
236
- expect(results).to match(/p2 has lock\Z/)
237
256
  end
238
257
 
239
258
  it "test returns true and acquires the lock" do
259
+ run_lock = Chef::RunLock.new(lockfile)
240
260
  p1 = fork do
241
261
  expect(run_lock.test).to eq(true)
262
+ run_lock.save_pid
242
263
  sleep 2
243
264
  exit! 1
244
265
  end
@@ -255,8 +276,10 @@ E
255
276
  end
256
277
 
257
278
  it "test returns without waiting when the lock is acquired" do
279
+ run_lock = Chef::RunLock.new(lockfile)
258
280
  p1 = fork do
259
281
  run_lock.acquire
282
+ run_lock.save_pid
260
283
  sleep 2
261
284
  exit! 1
262
285
  end
@@ -267,20 +290,176 @@ E
267
290
  Process.waitpid2(p1)
268
291
  end
269
292
 
270
- it "doesn't truncate the lock file so that contents can be read" do
271
- p1 = fork do
272
- run_lock.acquire
273
- run_lock.save_pid
274
- sleep 2
275
- exit! 1
293
+ end
294
+
295
+ #
296
+ # Runs a process in the background that will:
297
+ #
298
+ # 1. start up (`started` event)
299
+ # 2. acquire the runlock file (`acquired lock` event)
300
+ # 3. save the pid to the lockfile (`saved pid` event)
301
+ # 4. exit
302
+ #
303
+ # You control exactly how far the client process goes with the `run_to`
304
+ # method: it will stop at any given spot so you can test for race conditions.
305
+ #
306
+ # It uses a pair of pipes to communicate with the process. The tests will
307
+ # send an event name over to the process, which gives the process permission
308
+ # to run until it reaches that event (at which point it waits for another event
309
+ # name). The process sends the name of each event it reaches back to the tests.
310
+ #
311
+ class ClientProcess
312
+ def initialize(example, name)
313
+ @example = example
314
+ @name = name
315
+ @read_from_process, @write_to_tests = IO.pipe
316
+ @read_from_tests, @write_to_process = IO.pipe
317
+ end
318
+
319
+ attr_reader :example
320
+ attr_reader :name
321
+ attr_reader :pid
322
+
323
+ def last_event
324
+ while true
325
+ line = readline_nonblock(read_from_process)
326
+ break if line.nil?
327
+ event, time = line.split("@")
328
+ example.log_event("#{name}.last_event got #{event}")
329
+ example.log_event("[#{name}] #{event}", time.strip)
330
+ @last_event = event
276
331
  end
332
+ @last_event
333
+ end
277
334
 
278
- wait_on_lock
279
- sleep 0.5 # Possible race condition on Solaris which pid is observed as 0
280
- expect(File.read(lockfile)).to eq(p1.to_s)
335
+ def run_to(to_event, &background_block)
336
+ example.log_event("#{name}.run_to(#{to_event.inspect})")
281
337
 
282
- Process.waitpid2(p1)
338
+ # Start the process if it's not started
339
+ start if !pid
340
+
341
+ # Tell the process what to stop at (also means it can go)
342
+ write_to_process.print "#{to_event}\n"
343
+
344
+ # Run the background block
345
+ background_block.call if background_block
346
+
347
+ # Wait until it gets there
348
+ Timeout::timeout(CLIENT_PROCESS_TIMEOUT) do
349
+ until @last_event == "after #{to_event}"
350
+ got_event, time = read_from_process.gets.split("@")
351
+ example.log_event("#{name}.last_event got #{got_event}")
352
+ example.log_event("[#{name}] #{got_event}", time.strip)
353
+ @last_event = got_event
354
+ end
355
+ end
356
+
357
+ example.log_event("#{name}.run_to(#{to_event.inspect}) finished")
283
358
  end
284
359
 
360
+ def run_to_completion
361
+ example.log_event("#{name}.run_to_completion")
362
+ # Start the process if it's not started
363
+ start if !pid
364
+
365
+ # Tell the process to stop at nothing (no blocking)
366
+ @write_to_process.print "nothing\n"
367
+
368
+ # Wait for the process to exit
369
+ wait_for_exit
370
+ example.log_event("#{name}.run_to_completion finished")
371
+ end
372
+
373
+ def wait_for_exit
374
+ example.log_event("#{name}.wait_for_exit (pid #{pid})")
375
+ Timeout::timeout(CLIENT_PROCESS_TIMEOUT) do
376
+ Process.wait(pid) if pid
377
+ end
378
+ example.log_event("#{name}.wait_for_exit finished (pid #{pid})")
379
+ end
380
+
381
+ def stop
382
+ if pid
383
+ example.log_event("#{name}.stop (pid #{pid})")
384
+ begin
385
+ # Send it the kill signal over and over until it dies
386
+ Timeout::timeout(CLIENT_PROCESS_TIMEOUT) do
387
+ Process.kill(:KILL, pid)
388
+ while !Process.waitpid2(pid, Process::WNOHANG)
389
+ sleep(0.05)
390
+ end
391
+ end
392
+ example.log_event("#{name}.stop finished (stopped pid #{pid})")
393
+ # Process not found is perfectly fine when we're trying to kill a process :)
394
+ rescue Errno::ESRCH
395
+ example.log_event("#{name}.stop finished (pid #{pid} wasn't running)")
396
+ end
397
+ end
398
+ end
399
+
400
+ def fire_event(event)
401
+ # Let the caller know what event we've reached
402
+ write_to_tests.print("after #{event}@#{Time.now.strftime("%H:%M:%S.%L")}\n")
403
+
404
+ # Block until the client tells us where to stop
405
+ if !@run_to_event || event == @run_to_event
406
+ write_to_tests.print("waiting for instructions after #{event}@#{Time.now.strftime("%H:%M:%S.%L")}\n")
407
+ @run_to_event = read_from_tests.gets.strip
408
+ write_to_tests.print("told to run to #{@run_to_event} after #{event}@#{Time.now.strftime("%H:%M:%S.%L")}\n")
409
+ elsif @run_to_event
410
+ write_to_tests.print("continuing until #{@run_to_event} after #{event}@#{Time.now.strftime("%H:%M:%S.%L")}\n")
411
+ end
412
+ end
413
+
414
+ private
415
+
416
+ attr_reader :read_from_process
417
+ attr_reader :write_to_tests
418
+ attr_reader :read_from_tests
419
+ attr_reader :write_to_process
420
+
421
+ class TestRunLock < Chef::RunLock
422
+ attr_accessor :client_process
423
+ def create_lock
424
+ super
425
+ client_process.fire_event("created lock")
426
+ end
427
+ end
428
+
429
+ def start
430
+ example.log_event("#{name}.start")
431
+ @pid = fork do
432
+ begin
433
+ Timeout::timeout(CLIENT_PROCESS_TIMEOUT) do
434
+ run_lock = TestRunLock.new(example.lockfile)
435
+ run_lock.client_process = self
436
+ fire_event("started")
437
+ run_lock.acquire
438
+ fire_event("acquired lock")
439
+ run_lock.save_pid
440
+ fire_event("saved pid")
441
+ exit!(0)
442
+ end
443
+ rescue
444
+ fire_event($!.message.lines.join(" // "))
445
+ raise
446
+ end
447
+ end
448
+ example.log_event("#{name}.start forked (pid #{pid})")
449
+ end
450
+
451
+ def readline_nonblock(fd)
452
+ buffer = ""
453
+ buffer << fd.read_nonblock(1) while buffer[-1] != "\n"
454
+
455
+ buffer
456
+ #rescue IO::EAGAINUnreadable
457
+ rescue IO::WaitReadable
458
+ unless buffer == ""
459
+ sleep 0.1
460
+ retry
461
+ end
462
+ nil
463
+ end
285
464
  end
286
465
  end