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
@@ -33,6 +33,9 @@ class Chef
33
33
  # expands the run_list on a node object and then queries the chef-server
34
34
  # to find the correct set of cookbooks, given version constraints of the
35
35
  # node's environment.
36
+ #
37
+ # Note that this class should only be used via PolicyBuilder::Dynamic and
38
+ # not instantiated directly.
36
39
  class ExpandNodeObject
37
40
 
38
41
  attr_reader :events
@@ -55,9 +58,10 @@ class Chef
55
58
  @run_list_expansion = nil
56
59
  end
57
60
 
58
- # This method injects the run_context and provider and resource priority
59
- # maps into the Chef class. The run_context has to be injected here, the provider and
60
- # resource maps could be moved if a better place can be found to do this work.
61
+ # This method injects the run_context and into the Chef class.
62
+ #
63
+ # NOTE: This is duplicated with the Policyfile implementation. If
64
+ # it gets any more complicated, it needs to be moved elsewhere.
61
65
  #
62
66
  # @param run_context [Chef::RunContext] the run_context to inject
63
67
  def setup_chef_class(run_context)
@@ -93,25 +97,36 @@ class Chef
93
97
  run_context
94
98
  end
95
99
 
96
-
97
- # In client-server operation, loads the node state from the server. In
98
- # chef-solo operation, builds a new node object.
100
+ # DEPRECATED: As of Chef 12.5, chef selects either policyfile mode or
101
+ # "expand node" mode dynamically, based on the content of the node
102
+ # object, first boot JSON, and config. This happens in
103
+ # PolicyBuilder::Dynamic, which selects the implementation during
104
+ # #load_node and then delegates to either ExpandNodeObject or Policyfile
105
+ # implementations as appropriate. Tools authors should update their code
106
+ # to create a PolicyBuilder::Dynamc policy builder and allow it to select
107
+ # the proper implementation.
99
108
  def load_node
100
- events.node_load_start(node_name, Chef::Config)
109
+ Chef.log_deprecation("ExpandNodeObject#load_node is deprecated. Please use Chef::PolicyBuilder::Dynamic instead of using ExpandNodeObject directly")
110
+
111
+ events.node_load_start(node_name, config)
101
112
  Chef::Log.debug("Building node object for #{node_name}")
102
113
 
103
- if Chef::Config[:solo]
104
- @node = Chef::Node.build(node_name)
105
- else
106
- @node = Chef::Node.find_or_create(node_name)
107
- end
114
+ @node =
115
+ if Chef::Config[:solo]
116
+ Chef::Node.build(node_name)
117
+ else
118
+ Chef::Node.find_or_create(node_name)
119
+ end
120
+ finish_load_node(node)
121
+ node
108
122
  rescue Exception => e
109
- # TODO: wrap this exception so useful error info can be given to the
110
- # user.
111
- events.node_load_failed(node_name, e, Chef::Config)
123
+ events.node_load_failed(node_name, e, config)
112
124
  raise
113
125
  end
114
126
 
127
+ def finish_load_node(node)
128
+ @node = node
129
+ end
115
130
 
116
131
  # Applies environment, external JSON attributes, and override run list to
117
132
  # the node, Then expands the run_list.
@@ -68,22 +68,20 @@ class Chef
68
68
 
69
69
  @node = nil
70
70
 
71
- Chef::Log.warn("Using experimental Policyfile feature")
72
-
73
71
  if Chef::Config[:solo]
74
- raise UnsupportedFeature, "Policyfile does not support chef-solo at this time."
72
+ raise UnsupportedFeature, "Policyfile does not support chef-solo. Use chef-client local mode instead."
75
73
  end
76
74
 
77
75
  if override_runlist
78
- raise UnsupportedFeature, "Policyfile does not support override run lists at this time"
76
+ raise UnsupportedFeature, "Policyfile does not support override run lists. Use named run_lists instead."
79
77
  end
80
78
 
81
79
  if json_attribs && json_attribs.key?("run_list")
82
- raise UnsupportedFeature, "Policyfile does not support setting the run_list in json data at this time"
80
+ raise UnsupportedFeature, "Policyfile does not support setting the run_list in json data."
83
81
  end
84
82
 
85
83
  if Chef::Config[:environment] && !Chef::Config[:environment].chomp.empty?
86
- raise UnsupportedFeature, "Policyfile does not work with Chef Environments"
84
+ raise UnsupportedFeature, "Policyfile does not work with Chef Environments."
87
85
  end
88
86
  end
89
87
 
@@ -112,18 +110,11 @@ class Chef
112
110
 
113
111
  ## PolicyBuilder API ##
114
112
 
115
- # Loads the node state from the server.
116
- def load_node
117
- events.node_load_start(node_name, Chef::Config)
118
- Chef::Log.debug("Building node object for #{node_name}")
119
-
120
- @node = Chef::Node.find_or_create(node_name)
113
+ def finish_load_node(node)
114
+ @node = node
115
+ select_policy_name_and_group
121
116
  validate_policyfile
122
117
  events.policyfile_loaded(policy)
123
- node
124
- rescue Exception => e
125
- events.node_load_failed(node_name, e, Chef::Config)
126
- raise
127
118
  end
128
119
 
129
120
  # Applies environment, external JSON attributes, and override run list to
@@ -154,25 +145,42 @@ class Chef
154
145
  raise
155
146
  end
156
147
 
148
+ # Synchronizes cookbooks and initializes the run context object for the
149
+ # run.
150
+ #
151
+ # @return [Chef::RunContext]
157
152
  def setup_run_context(specific_recipes=nil)
158
153
  Chef::Cookbook::FileVendor.fetch_from_remote(http_api)
159
154
  sync_cookbooks
160
155
  cookbook_collection = Chef::CookbookCollection.new(cookbooks_to_sync)
161
156
  run_context = Chef::RunContext.new(node, cookbook_collection, events)
162
157
 
158
+ setup_chef_class(run_context)
159
+
163
160
  run_context.load(run_list_expansion_ish)
164
161
 
162
+ setup_chef_class(run_context)
165
163
  run_context
166
164
  end
167
165
 
166
+ # Sets `run_list` on the node from the policy, sets `roles` and `recipes`
167
+ # attributes on the node accordingly.
168
+ #
169
+ # @return [RunListExpansionIsh] A RunListExpansion duck-type.
168
170
  def expand_run_list
171
+ CookbookCacheCleaner.instance.skip_removal = true if named_run_list_requested?
172
+
169
173
  node.run_list(run_list)
170
174
  node.automatic_attrs[:roles] = []
171
175
  node.automatic_attrs[:recipes] = run_list_expansion_ish.recipes
172
176
  run_list_expansion_ish
173
177
  end
174
178
 
175
-
179
+ # Synchronizes cookbooks. In a normal chef-client run, this is handled by
180
+ # #setup_run_context, but may be called directly in some circumstances.
181
+ #
182
+ # @return [Hash{String => Chef::CookbookManifest}] A map of
183
+ # CookbookManifest objects by cookbook name.
176
184
  def sync_cookbooks
177
185
  Chef::Log.debug("Synchronizing cookbooks")
178
186
  synchronizer = Chef::CookbookSynchronizer.new(cookbooks_to_sync, events)
@@ -186,12 +194,18 @@ class Chef
186
194
 
187
195
  # Whether or not this is a temporary policy. Since PolicyBuilder doesn't
188
196
  # support override_runlist, this is always false.
197
+ #
198
+ # @return [false]
189
199
  def temporary_policy?
190
200
  false
191
201
  end
192
202
 
193
203
  ## Internal Public API ##
194
204
 
205
+ # @api private
206
+ #
207
+ # Generates an array of strings with recipe names including version and
208
+ # identifier info.
195
209
  def run_list_with_versions_for_display
196
210
  run_list.map do |recipe_spec|
197
211
  cookbook, recipe = parse_recipe_spec(recipe_spec)
@@ -201,6 +215,11 @@ class Chef
201
215
  end
202
216
  end
203
217
 
218
+ # @api private
219
+ #
220
+ # Sets up a RunListExpansionIsh object so that it can be used in place of
221
+ # a RunListExpansion object, to satisfy the API contract of
222
+ # #expand_run_list
204
223
  def run_list_expansion_ish
205
224
  recipes = run_list.map do |recipe_spec|
206
225
  cookbook, recipe = parse_recipe_spec(recipe_spec)
@@ -209,11 +228,15 @@ class Chef
209
228
  RunListExpansionIsh.new(recipes, [])
210
229
  end
211
230
 
231
+ # @api private
232
+ #
233
+ # Sets attributes from the policyfile on the node, using the role priority.
212
234
  def apply_policyfile_attributes
213
235
  node.attributes.role_default = policy["default_attributes"]
214
236
  node.attributes.role_override = policy["override_attributes"]
215
237
  end
216
238
 
239
+ # @api private
217
240
  def parse_recipe_spec(recipe_spec)
218
241
  rmatch = recipe_spec.match(/recipe\[([^:]+)::([^:]+)\]/)
219
242
  if rmatch.nil?
@@ -223,20 +246,31 @@ class Chef
223
246
  end
224
247
  end
225
248
 
249
+ # @api private
226
250
  def cookbook_lock_for(cookbook_name)
227
251
  cookbook_locks[cookbook_name]
228
252
  end
229
253
 
254
+ # @api private
230
255
  def run_list
231
- policy["run_list"]
256
+ if named_run_list_requested?
257
+ named_run_list or
258
+ raise ConfigurationError,
259
+ "Policy '#{retrieved_policy_name}' revision '#{revision_id}' does not have named_run_list '#{named_run_list_name}'" +
260
+ "(available named_run_lists: [#{available_named_run_lists.join(', ')}])"
261
+ else
262
+ policy["run_list"]
263
+ end
232
264
  end
233
265
 
266
+ # @api private
234
267
  def policy
235
268
  @policy ||= http_api.get(policyfile_location)
236
269
  rescue Net::HTTPServerException => e
237
270
  raise ConfigurationError, "Error loading policyfile from `#{policyfile_location}': #{e.class} - #{e.message}"
238
271
  end
239
272
 
273
+ # @api private
240
274
  def policyfile_location
241
275
  if Chef::Config[:policy_document_native_api]
242
276
  validate_policy_config!
@@ -273,6 +307,7 @@ class Chef
273
307
  end
274
308
  end
275
309
 
310
+ # @api private
276
311
  def validate_recipe_spec(recipe_spec)
277
312
  parse_recipe_spec(recipe_spec)
278
313
  nil
@@ -282,11 +317,13 @@ class Chef
282
317
 
283
318
  class ConfigurationError < StandardError; end
284
319
 
320
+ # @api private
285
321
  def deployment_group
286
322
  Chef::Config[:deployment_group] or
287
323
  raise ConfigurationError, "Setting `deployment_group` is not configured."
288
324
  end
289
325
 
326
+ # @api private
290
327
  def validate_policy_config!
291
328
  policy_group or
292
329
  raise ConfigurationError, "Setting `policy_group` is not configured."
@@ -295,14 +332,75 @@ class Chef
295
332
  raise ConfigurationError, "Setting `policy_name` is not configured."
296
333
  end
297
334
 
335
+ # @api private
298
336
  def policy_group
299
337
  Chef::Config[:policy_group]
300
338
  end
301
339
 
340
+ # @api private
302
341
  def policy_name
303
342
  Chef::Config[:policy_name]
304
343
  end
305
344
 
345
+ # @api private
346
+ #
347
+ # Selects the `policy_name` and `policy_group` from the following sources
348
+ # in priority order:
349
+ #
350
+ # 1. JSON attribs (i.e., `-j JSON_FILE`)
351
+ # 2. `Chef::Config`
352
+ # 3. The node object
353
+ #
354
+ # The selected values are then copied to `Chef::Config` and the node.
355
+ def select_policy_name_and_group
356
+ policy_name_to_set =
357
+ policy_name_from_json_attribs ||
358
+ policy_name_from_config ||
359
+ policy_name_from_node
360
+
361
+ policy_group_to_set =
362
+ policy_group_from_json_attribs ||
363
+ policy_group_from_config ||
364
+ policy_group_from_node
365
+
366
+ node.policy_name = policy_name_to_set
367
+ node.policy_group = policy_group_to_set
368
+
369
+ Chef::Config[:policy_name] = policy_name_to_set
370
+ Chef::Config[:policy_group] = policy_group_to_set
371
+ end
372
+
373
+ # @api private
374
+ def policy_group_from_json_attribs
375
+ json_attribs["policy_group"]
376
+ end
377
+
378
+ # @api private
379
+ def policy_name_from_json_attribs
380
+ json_attribs["policy_name"]
381
+ end
382
+
383
+ # @api private
384
+ def policy_group_from_config
385
+ Chef::Config[:policy_group]
386
+ end
387
+
388
+ # @api private
389
+ def policy_name_from_config
390
+ Chef::Config[:policy_name]
391
+ end
392
+
393
+ # @api private
394
+ def policy_group_from_node
395
+ node.policy_group
396
+ end
397
+
398
+ # @api private
399
+ def policy_name_from_node
400
+ node.policy_name
401
+ end
402
+
403
+ # @api private
306
404
  # Builds a 'cookbook_hash' map of the form
307
405
  # "COOKBOOK_NAME" => "IDENTIFIER"
308
406
  #
@@ -330,6 +428,7 @@ class Chef
330
428
  raise
331
429
  end
332
430
 
431
+ # @api private
333
432
  # Fetches the CookbookVersion object for the given name and identifer
334
433
  # specified in the lock_data.
335
434
  # TODO: This only implements Chef 11 compatibility mode, which means that
@@ -343,20 +442,58 @@ class Chef
343
442
  end
344
443
  end
345
444
 
445
+ # @api private
346
446
  def cookbook_locks
347
447
  policy["cookbook_locks"]
348
448
  end
349
449
 
450
+ # @api private
451
+ def revision_id
452
+ policy["revision_id"]
453
+ end
454
+
455
+ # @api private
350
456
  def http_api
351
457
  @api_service ||= Chef::REST.new(config[:chef_server_url])
352
458
  end
353
459
 
460
+ # @api private
354
461
  def config
355
462
  Chef::Config
356
463
  end
357
464
 
358
465
  private
359
466
 
467
+ # This method injects the run_context and into the Chef class.
468
+ #
469
+ # NOTE: This is duplicated with the ExpandNodeObject implementation. If
470
+ # it gets any more complicated, it needs to be moved elsewhere.
471
+ #
472
+ # @param run_context [Chef::RunContext] the run_context to inject
473
+ def setup_chef_class(run_context)
474
+ Chef.set_run_context(run_context)
475
+ end
476
+
477
+ def retrieved_policy_name
478
+ policy["name"]
479
+ end
480
+
481
+ def named_run_list
482
+ policy["named_run_lists"] && policy["named_run_lists"][named_run_list_name]
483
+ end
484
+
485
+ def available_named_run_lists
486
+ (policy["named_run_lists"] || {}).keys
487
+ end
488
+
489
+ def named_run_list_requested?
490
+ !!Chef::Config[:named_run_list]
491
+ end
492
+
493
+ def named_run_list_name
494
+ Chef::Config[:named_run_list]
495
+ end
496
+
360
497
  def compat_mode_manifest_for(cookbook_name, lock_data)
361
498
  xyz_version = lock_data["dotted_decimal_identifier"]
362
499
  rel_url = "cookbooks/#{cookbook_name}/#{xyz_version}"
@@ -0,0 +1,568 @@
1
+ #
2
+ # Author:: John Keiser <jkeiser@chef.io>
3
+ # Copyright:: Copyright (c) 2015 John Keiser.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/exceptions'
20
+ require 'chef/delayed_evaluator'
21
+
22
+ class Chef
23
+ #
24
+ # Type and validation information for a property on a resource.
25
+ #
26
+ # A property named "x" manipulates the "@x" instance variable on a
27
+ # resource. The *presence* of the variable (`instance_variable_defined?(@x)`)
28
+ # tells whether the variable is defined; it may have any actual value,
29
+ # constrained only by validation.
30
+ #
31
+ # Properties may have validation, defaults, and coercion, and have full
32
+ # support for lazy values.
33
+ #
34
+ # @see Chef::Resource.property
35
+ # @see Chef::DelayedEvaluator
36
+ #
37
+ class Property
38
+ #
39
+ # Create a reusable property type that can be used in multiple properties
40
+ # in different resources.
41
+ #
42
+ # @param options [Hash<Symbol,Object>] Validation options. See Chef::Resource.property for
43
+ # the list of options.
44
+ #
45
+ # @example
46
+ # Property.derive(default: 'hi')
47
+ #
48
+ def self.derive(**options)
49
+ new(**options)
50
+ end
51
+
52
+ #
53
+ # Create a new property.
54
+ #
55
+ # @param options [Hash<Symbol,Object>] Property options, including
56
+ # control options here, as well as validation options (see
57
+ # Chef::Mixin::ParamsValidate#validate for a description of validation
58
+ # options).
59
+ # @option options [Symbol] :name The name of this property.
60
+ # @option options [Class] :declared_in The class this property comes from.
61
+ # @option options [Symbol] :instance_variable_name The instance variable
62
+ # tied to this property. Must include a leading `@`. Defaults to `@<name>`.
63
+ # `nil` means the property is opaque and not tied to a specific instance
64
+ # variable.
65
+ # @option options [Boolean] :desired_state `true` if this property is part of desired
66
+ # state. Defaults to `true`.
67
+ # @option options [Boolean] :identity `true` if this property is part of object
68
+ # identity. Defaults to `false`.
69
+ # @option options [Boolean] :name_property `true` if this
70
+ # property defaults to the same value as `name`. Equivalent to
71
+ # `default: lazy { name }`, except that #property_is_set? will
72
+ # return `true` if the property is set *or* if `name` is set.
73
+ # @option options [Object] :default The value this property
74
+ # will return if the user does not set one. If this is `lazy`, it will
75
+ # be run in the context of the instance (and able to access other
76
+ # properties) and cached. If not, the value will be frozen with Object#freeze
77
+ # to prevent users from modifying it in an instance.
78
+ # @option options [Proc] :coerce A proc which will be called to
79
+ # transform the user input to canonical form. The value is passed in,
80
+ # and the transformed value returned as output. Lazy values will *not*
81
+ # be passed to this method until after they are evaluated. Called in the
82
+ # context of the resource (meaning you can access other properties).
83
+ # @option options [Boolean] :required `true` if this property
84
+ # must be present; `false` otherwise. This is checked after the resource
85
+ # is fully initialized.
86
+ #
87
+ def initialize(**options)
88
+ options.each { |k,v| options[k.to_sym] = v if k.is_a?(String) }
89
+
90
+ # Replace name_attribute with name_property
91
+ if options.has_key?(:name_attribute)
92
+ # If we have both name_attribute and name_property and they differ, raise an error
93
+ if options.has_key?(:name_property)
94
+ raise ArgumentError, "Cannot specify both name_property and name_attribute together on property #{options[:name]}#{options[:declared_in] ? " of resource #{options[:declared_in].resource_name}" : ""}."
95
+ end
96
+ # replace name_property with name_attribute in place
97
+ options = Hash[options.map { |k,v| k == :name_attribute ? [ :name_property, v ] : [ k,v ] }]
98
+ end
99
+
100
+ # Only pick the first of :default, :name_property and :name_attribute if
101
+ # more than one is specified.
102
+ if options.has_key?(:default) && options[:name_property]
103
+ if options[:default].nil? || options.keys.index(:name_property) < options.keys.index(:default)
104
+ options.delete(:default)
105
+ preferred_default = :name_property
106
+ else
107
+ options.delete(:name_property)
108
+ preferred_default = :default
109
+ end
110
+ Chef.log_deprecation("Cannot specify both default and name_property together on property #{options[:name]}#{options[:declared_in] ? " of resource #{options[:declared_in].resource_name}" : ""}. Only one (#{preferred_default}) will be obeyed. In Chef 13, this will become an error.")
111
+ end
112
+
113
+ @options = options
114
+
115
+ options[:name] = options[:name].to_sym if options[:name]
116
+ options[:instance_variable_name] = options[:instance_variable_name].to_sym if options[:instance_variable_name]
117
+ end
118
+
119
+ #
120
+ # The name of this property.
121
+ #
122
+ # @return [String]
123
+ #
124
+ def name
125
+ options[:name]
126
+ end
127
+
128
+ #
129
+ # The class this property was defined in.
130
+ #
131
+ # @return [Class]
132
+ #
133
+ def declared_in
134
+ options[:declared_in]
135
+ end
136
+
137
+ #
138
+ # The instance variable associated with this property.
139
+ #
140
+ # Defaults to `@<name>`
141
+ #
142
+ # @return [Symbol]
143
+ #
144
+ def instance_variable_name
145
+ if options.has_key?(:instance_variable_name)
146
+ options[:instance_variable_name]
147
+ elsif name
148
+ :"@#{name}"
149
+ end
150
+ end
151
+
152
+ #
153
+ # The raw default value for this resource.
154
+ #
155
+ # Does not coerce or validate the default. Does not evaluate lazy values.
156
+ #
157
+ # Defaults to `lazy { name }` if name_property is true; otherwise defaults to
158
+ # `nil`
159
+ #
160
+ def default
161
+ return options[:default] if options.has_key?(:default)
162
+ return Chef::DelayedEvaluator.new { name } if name_property?
163
+ nil
164
+ end
165
+
166
+ #
167
+ # Whether this is part of the resource's natural identity or not.
168
+ #
169
+ # @return [Boolean]
170
+ #
171
+ def identity?
172
+ options[:identity]
173
+ end
174
+
175
+ #
176
+ # Whether this is part of desired state or not.
177
+ #
178
+ # Defaults to true.
179
+ #
180
+ # @return [Boolean]
181
+ #
182
+ def desired_state?
183
+ return true if !options.has_key?(:desired_state)
184
+ options[:desired_state]
185
+ end
186
+
187
+ #
188
+ # Whether this is name_property or not.
189
+ #
190
+ # @return [Boolean]
191
+ #
192
+ def name_property?
193
+ options[:name_property]
194
+ end
195
+
196
+ #
197
+ # Whether this property has a default value.
198
+ #
199
+ # @return [Boolean]
200
+ #
201
+ def has_default?
202
+ options.has_key?(:default) || name_property?
203
+ end
204
+
205
+ #
206
+ # Whether this property is required or not.
207
+ #
208
+ # @return [Boolean]
209
+ #
210
+ def required?
211
+ options[:required]
212
+ end
213
+
214
+ #
215
+ # Validation options. (See Chef::Mixin::ParamsValidate#validate.)
216
+ #
217
+ # @return [Hash<Symbol,Object>]
218
+ #
219
+ def validation_options
220
+ @validation_options ||= options.reject { |k,v|
221
+ [:declared_in,:name,:instance_variable_name,:desired_state,:identity,:default,:name_property,:coerce,:required].include?(k)
222
+ }
223
+ end
224
+
225
+ #
226
+ # Handle the property being called.
227
+ #
228
+ # The base implementation does the property get-or-set:
229
+ #
230
+ # ```ruby
231
+ # resource.myprop # get
232
+ # resource.myprop value # set
233
+ # ```
234
+ #
235
+ # Subclasses may implement this with any arguments they want, as long as
236
+ # the corresponding DSL calls it correctly.
237
+ #
238
+ # @param resource [Chef::Resource] The resource to get the property from.
239
+ # @param value The value to set (or NOT_PASSED if it is a get).
240
+ #
241
+ # @return The current value of the property. If it is a `set`, lazy values
242
+ # will be returned without running, validating or coercing. If it is a
243
+ # `get`, the non-lazy, coerced, validated value will always be returned.
244
+ #
245
+ def call(resource, value=NOT_PASSED)
246
+ if value == NOT_PASSED
247
+ return get(resource)
248
+ end
249
+
250
+ # myprop nil is sometimes a get (backcompat)
251
+ if value.nil? && !explicitly_accepts_nil?(resource)
252
+ # If you say "my_property nil" and the property explicitly accepts
253
+ # nil values, we consider this a get.
254
+ Chef.log_deprecation("#{name} nil currently does not overwrite the value of #{name}. This will change in Chef 13, and the value will be set to nil instead. Please change your code to explicitly accept nil using \"property :#{name}, [MyType, nil]\", or stop setting this value to nil.")
255
+ return get(resource)
256
+ end
257
+
258
+ # Anything else (myprop value) is a set
259
+ set(resource, value)
260
+ end
261
+
262
+ #
263
+ # Get the property value from the resource, handling lazy values,
264
+ # defaults, and validation.
265
+ #
266
+ # - If the property's value is lazy, it is evaluated, coerced and validated.
267
+ # - If the property has no value, and is required, raises ValidationFailed.
268
+ # - If the property has no value, but has a lazy default, it is evaluated,
269
+ # coerced and validated. If the evaluated value is frozen, the resulting
270
+ # - If the property has no value, but has a default, the default value
271
+ # will be returned and frozen. If the default value is lazy, it will be
272
+ # evaluated, coerced and validated, and the result stored in the property.
273
+ # - If the property has no value, but is name_property, `resource.name`
274
+ # is retrieved, coerced, validated and stored in the property.
275
+ # - Otherwise, `nil` is returned.
276
+ #
277
+ # @param resource [Chef::Resource] The resource to get the property from.
278
+ #
279
+ # @return The value of the property.
280
+ #
281
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
282
+ # this property, or if the value is required and not set.
283
+ #
284
+ def get(resource)
285
+ if is_set?(resource)
286
+ value = get_value(resource)
287
+ if value.is_a?(DelayedEvaluator)
288
+ value = exec_in_resource(resource, value)
289
+ value = coerce(resource, value)
290
+ validate(resource, value)
291
+ end
292
+ value
293
+
294
+ else
295
+ if has_default?
296
+ value = default
297
+ if value.is_a?(DelayedEvaluator)
298
+ value = exec_in_resource(resource, value)
299
+ end
300
+
301
+ value = coerce(resource, value)
302
+
303
+ # We don't validate defaults
304
+
305
+ # If the value is mutable (non-frozen), we set it on the instance
306
+ # so that people can mutate it. (All constant default values are
307
+ # frozen.)
308
+ if !value.frozen? && !value.nil?
309
+ set_value(resource, value)
310
+ end
311
+
312
+ value
313
+
314
+ elsif required?
315
+ raise Chef::Exceptions::ValidationFailed, "#{name} is required"
316
+ end
317
+ end
318
+ end
319
+
320
+ #
321
+ # Set the value of this property in the given resource.
322
+ #
323
+ # Non-lazy values are coerced and validated before being set. Coercion
324
+ # and validation of lazy values is delayed until they are first retrieved.
325
+ #
326
+ # @param resource [Chef::Resource] The resource to set this property in.
327
+ # @param value The value to set.
328
+ #
329
+ # @return The value that was set, after coercion (if lazy, still returns
330
+ # the lazy value)
331
+ #
332
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
333
+ # this property.
334
+ #
335
+ def set(resource, value)
336
+ unless value.is_a?(DelayedEvaluator)
337
+ value = coerce(resource, value)
338
+ validate(resource, value)
339
+ end
340
+ set_value(resource, value)
341
+ end
342
+
343
+ #
344
+ # Find out whether this property has been set.
345
+ #
346
+ # This will be true if:
347
+ # - The user explicitly set the value
348
+ # - The property has a default, and the value was retrieved.
349
+ #
350
+ # From this point of view, it is worth looking at this as "what does the
351
+ # user think this value should be." In order words, if the user grabbed
352
+ # the value, even if it was a default, they probably based calculations on
353
+ # it. If they based calculations on it and the value changes, the rest of
354
+ # the world gets inconsistent.
355
+ #
356
+ # @param resource [Chef::Resource] The resource to get the property from.
357
+ #
358
+ # @return [Boolean]
359
+ #
360
+ def is_set?(resource)
361
+ value_is_set?(resource)
362
+ end
363
+
364
+ #
365
+ # Reset the value of this property so that is_set? will return false and the
366
+ # default will be returned in the future.
367
+ #
368
+ # @param resource [Chef::Resource] The resource to get the property from.
369
+ #
370
+ def reset(resource)
371
+ reset_value(resource)
372
+ end
373
+
374
+ #
375
+ # Coerce an input value into canonical form for the property.
376
+ #
377
+ # After coercion, the value is suitable for storage in the resource.
378
+ # You must validate values after coercion, however.
379
+ #
380
+ # Does no special handling for lazy values.
381
+ #
382
+ # @param resource [Chef::Resource] The resource we're coercing against
383
+ # (to provide context for the coerce).
384
+ # @param value The value to coerce.
385
+ #
386
+ # @return The coerced value.
387
+ #
388
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
389
+ # this property.
390
+ #
391
+ def coerce(resource, value)
392
+ if options.has_key?(:coerce)
393
+ value = exec_in_resource(resource, options[:coerce], value)
394
+ end
395
+ value
396
+ end
397
+
398
+ #
399
+ # Validate a value.
400
+ #
401
+ # Calls Chef::Mixin::ParamsValidate#validate with #validation_options as
402
+ # options.
403
+ #
404
+ # @param resource [Chef::Resource] The resource we're validating against
405
+ # (to provide context for the validate).
406
+ # @param value The value to validate.
407
+ #
408
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
409
+ # this property.
410
+ #
411
+ def validate(resource, value)
412
+ resource.validate({ name => value }, { name => validation_options })
413
+ end
414
+
415
+ #
416
+ # Derive a new Property that is just like this one, except with some added or
417
+ # changed options.
418
+ #
419
+ # @param options [Hash<Symbol,Object>] List of options that would be passed
420
+ # to #initialize.
421
+ #
422
+ # @return [Property] The new property type.
423
+ #
424
+ def derive(**modified_options)
425
+ # Since name_property, name_attribute and default override each other,
426
+ # if you specify one of them in modified_options it overrides anything in
427
+ # the original options.
428
+ options = self.options
429
+ if modified_options.has_key?(:name_property) ||
430
+ modified_options.has_key?(:name_attribute) ||
431
+ modified_options.has_key?(:default)
432
+ options = options.reject { |k,v| k == :name_attribute || k == :name_property || k == :default }
433
+ end
434
+ Property.new(options.merge(modified_options))
435
+ end
436
+
437
+ #
438
+ # Emit the DSL for this property into the resource class (`declared_in`).
439
+ #
440
+ # Creates a getter and setter for the property.
441
+ #
442
+ def emit_dsl
443
+ # We don't create the getter/setter if it's a custom property; we will
444
+ # be using the existing getter/setter to manipulate it instead.
445
+ return if !instance_variable_name
446
+
447
+ # We prefer this form because the property name won't show up in the
448
+ # stack trace if you use `define_method`.
449
+ declared_in.class_eval <<-EOM, __FILE__, __LINE__+1
450
+ def #{name}(value=NOT_PASSED)
451
+ self.class.properties[#{name.inspect}].call(self, value)
452
+ end
453
+ def #{name}=(value)
454
+ self.class.properties[#{name.inspect}].set(self, value)
455
+ end
456
+ EOM
457
+ rescue SyntaxError
458
+ # If the name is not a valid ruby name, we use define_method.
459
+ declared_in.define_method(name) do |value=NOT_PASSED|
460
+ self.class.properties[name].call(self, value)
461
+ end
462
+ declared_in.define_method("#{name}=") do |value|
463
+ self.class.properties[name].set(self, value)
464
+ end
465
+ end
466
+
467
+ protected
468
+
469
+ #
470
+ # The options this Property will use for get/set behavior and validation.
471
+ #
472
+ # @see #initialize for a list of valid options.
473
+ #
474
+ attr_reader :options
475
+
476
+ #
477
+ # Find out whether this type accepts nil explicitly.
478
+ #
479
+ # A type accepts nil explicitly if "is" allows nil, it validates as nil, *and* is not simply
480
+ # an empty type.
481
+ #
482
+ # These examples accept nil explicitly:
483
+ # ```ruby
484
+ # property :a, [ String, nil ]
485
+ # property :a, [ String, NilClass ]
486
+ # property :a, [ String, proc { |v| v.nil? } ]
487
+ # ```
488
+ #
489
+ # This does not (because the "is" doesn't exist or doesn't have nil):
490
+ #
491
+ # ```ruby
492
+ # property :x, String
493
+ # ```
494
+ #
495
+ # These do not, even though nil would validate fine (because they do not
496
+ # have "is"):
497
+ #
498
+ # ```ruby
499
+ # property :a
500
+ # property :a, equal_to: [ 1, 2, 3, nil ]
501
+ # property :a, kind_of: [ String, NilClass ]
502
+ # property :a, respond_to: [ ]
503
+ # property :a, callbacks: { "a" => proc { |v| v.nil? } }
504
+ # ```
505
+ #
506
+ # @param resource [Chef::Resource] The resource we're coercing against
507
+ # (to provide context for the coerce).
508
+ #
509
+ # @return [Boolean] Whether this value explicitly accepts nil.
510
+ #
511
+ # @api private
512
+ def explicitly_accepts_nil?(resource)
513
+ options.has_key?(:is) && resource.send(:_pv_is, { name => nil }, name, options[:is], raise_error: false)
514
+ end
515
+
516
+ def get_value(resource)
517
+ if instance_variable_name
518
+ resource.instance_variable_get(instance_variable_name)
519
+ else
520
+ resource.send(name)
521
+ end
522
+ end
523
+
524
+ def set_value(resource, value)
525
+ if instance_variable_name
526
+ resource.instance_variable_set(instance_variable_name, value)
527
+ else
528
+ resource.send(name, value)
529
+ end
530
+ end
531
+
532
+ def value_is_set?(resource)
533
+ if instance_variable_name
534
+ resource.instance_variable_defined?(instance_variable_name)
535
+ else
536
+ true
537
+ end
538
+ end
539
+
540
+ def reset_value(resource)
541
+ if instance_variable_name
542
+ if value_is_set?(resource)
543
+ resource.remove_instance_variable(instance_variable_name)
544
+ end
545
+ else
546
+ raise ArgumentError, "Property #{name} has no instance variable defined and cannot be reset"
547
+ end
548
+ end
549
+
550
+ def exec_in_resource(resource, proc, *args)
551
+ if resource
552
+ if proc.arity > args.size
553
+ value = proc.call(resource, *args)
554
+ else
555
+ value = resource.instance_exec(*args, &proc)
556
+ end
557
+ else
558
+ value = proc.call
559
+ end
560
+
561
+ if value.is_a?(DelayedEvaluator)
562
+ value = coerce(resource, value)
563
+ validate(resource, value)
564
+ end
565
+ value
566
+ end
567
+ end
568
+ end