chef 13.6.4 → 13.7.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (257) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/acceptance/Gemfile +2 -2
  4. data/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb +1 -6
  5. data/distro/powershell/chef/chef.psm1 +1 -5
  6. data/lib/chef/api_client.rb +5 -5
  7. data/lib/chef/api_client_v1.rb +6 -6
  8. data/lib/chef/application.rb +3 -2
  9. data/lib/chef/application/knife.rb +4 -0
  10. data/lib/chef/chef_class.rb +2 -2
  11. data/lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb +1 -1
  12. data/lib/chef/chef_fs/data_handler/data_handler_base.rb +2 -4
  13. data/lib/chef/client.rb +3 -3
  14. data/lib/chef/cookbook/chefignore.rb +4 -0
  15. data/lib/chef/cookbook/cookbook_collection.rb +2 -2
  16. data/lib/chef/cookbook/metadata.rb +2 -2
  17. data/lib/chef/data_bag.rb +1 -1
  18. data/lib/chef/deprecated.rb +10 -0
  19. data/lib/chef/event_dispatch/base.rb +2 -2
  20. data/lib/chef/http.rb +10 -10
  21. data/lib/chef/knife.rb +16 -15
  22. data/lib/chef/knife/configure.rb +12 -36
  23. data/lib/chef/knife/cookbook_upload.rb +4 -4
  24. data/lib/chef/knife/core/bootstrap_context.rb +1 -1
  25. data/lib/chef/knife/core/status_presenter.rb +6 -2
  26. data/lib/chef/knife/core/ui.rb +1 -1
  27. data/lib/chef/knife/data_bag_secret_options.rb +1 -1
  28. data/lib/chef/knife/data_bag_show.rb +1 -1
  29. data/lib/chef/knife/edit.rb +1 -1
  30. data/lib/chef/knife/ssh.rb +47 -35
  31. data/lib/chef/knife/user_create.rb +2 -0
  32. data/lib/chef/knife/user_delete.rb +2 -0
  33. data/lib/chef/knife/user_edit.rb +2 -0
  34. data/lib/chef/knife/user_reregister.rb +2 -0
  35. data/lib/chef/knife/user_show.rb +2 -0
  36. data/lib/chef/mixin/powershell_out.rb +1 -1
  37. data/lib/chef/node/attribute.rb +46 -70
  38. data/lib/chef/node/attribute_collections.rb +5 -5
  39. data/lib/chef/node/common_api.rb +1 -1
  40. data/lib/chef/node/immutable_collections.rb +180 -23
  41. data/lib/chef/node/mixin/state_tracking.rb +6 -6
  42. data/lib/chef/node_map.rb +63 -45
  43. data/lib/chef/property.rb +8 -8
  44. data/lib/chef/provider.rb +9 -3
  45. data/lib/chef/provider/apt_preference.rb +1 -1
  46. data/lib/chef/provider/apt_repository.rb +1 -1
  47. data/lib/chef/provider/apt_update.rb +1 -1
  48. data/lib/chef/provider/file.rb +1 -1
  49. data/lib/chef/provider/group/dscl.rb +6 -2
  50. data/lib/chef/provider/ifconfig.rb +96 -34
  51. data/lib/chef/provider/launchd.rb +0 -1
  52. data/lib/chef/provider/log.rb +3 -13
  53. data/lib/chef/provider/package/dnf.rb +1 -1
  54. data/lib/chef/provider/package/smartos.rb +2 -2
  55. data/lib/chef/provider/reboot.rb +12 -0
  56. data/lib/chef/provider/remote_directory.rb +1 -1
  57. data/lib/chef/provider/remote_file/http.rb +3 -2
  58. data/lib/chef/provider/service/solaris.rb +6 -2
  59. data/lib/chef/provider/systemd_unit.rb +34 -33
  60. data/lib/chef/provider/user/dscl.rb +1 -1
  61. data/lib/chef/provider/windows_path.rb +6 -7
  62. data/lib/chef/provider/windows_task.rb +89 -33
  63. data/lib/chef/provider/yum_repository.rb +24 -9
  64. data/lib/chef/resource/apt_package.rb +1 -0
  65. data/lib/chef/resource/apt_preference.rb +4 -0
  66. data/lib/chef/resource/apt_repository.rb +4 -0
  67. data/lib/chef/resource/apt_update.rb +3 -0
  68. data/lib/chef/resource/bash.rb +4 -0
  69. data/lib/chef/resource/batch.rb +5 -0
  70. data/lib/chef/resource/bff_package.rb +4 -0
  71. data/lib/chef/resource/breakpoint.rb +6 -0
  72. data/lib/chef/resource/cab_package.rb +6 -6
  73. data/lib/chef/resource/chef_gem.rb +13 -0
  74. data/lib/chef/resource/chocolatey_package.rb +4 -6
  75. data/lib/chef/resource/cookbook_file.rb +13 -15
  76. data/lib/chef/resource/cron.rb +2 -0
  77. data/lib/chef/resource/csh.rb +4 -0
  78. data/lib/chef/resource/directory.rb +8 -26
  79. data/lib/chef/resource/dnf_package.rb +5 -0
  80. data/lib/chef/resource/dpkg_package.rb +2 -0
  81. data/lib/chef/resource/dsc_resource.rb +5 -0
  82. data/lib/chef/resource/dsc_script.rb +6 -0
  83. data/lib/chef/resource/env.rb +3 -0
  84. data/lib/chef/resource/erl_call.rb +5 -0
  85. data/lib/chef/resource/execute.rb +5 -1
  86. data/lib/chef/resource/file.rb +2 -1
  87. data/lib/chef/resource/file/verification.rb +10 -0
  88. data/lib/chef/resource/freebsd_package.rb +10 -2
  89. data/lib/chef/resource/gem_package.rb +2 -0
  90. data/lib/chef/resource/git.rb +2 -0
  91. data/lib/chef/resource/group.rb +1 -0
  92. data/lib/chef/resource/homebrew_package.rb +3 -0
  93. data/lib/chef/resource/http_request.rb +2 -0
  94. data/lib/chef/resource/ifconfig.rb +23 -150
  95. data/lib/chef/resource/ips_package.rb +1 -0
  96. data/lib/chef/resource/ksh.rb +6 -0
  97. data/lib/chef/resource/launchd.rb +5 -4
  98. data/lib/chef/resource/link.rb +10 -0
  99. data/lib/chef/resource/log.rb +19 -46
  100. data/lib/chef/resource/macports_package.rb +1 -0
  101. data/lib/chef/resource/mdadm.rb +4 -0
  102. data/lib/chef/resource/mount.rb +1 -0
  103. data/lib/chef/resource/msu_package.rb +7 -8
  104. data/lib/chef/resource/ohai.rb +2 -0
  105. data/lib/chef/resource/openbsd_package.rb +3 -0
  106. data/lib/chef/resource/osx_profile.rb +10 -40
  107. data/lib/chef/resource/package.rb +6 -0
  108. data/lib/chef/resource/pacman_package.rb +1 -0
  109. data/lib/chef/resource/paludis_package.rb +3 -0
  110. data/lib/chef/resource/perl.rb +4 -0
  111. data/lib/chef/resource/portage_package.rb +1 -0
  112. data/lib/chef/resource/powershell_package.rb +5 -0
  113. data/lib/chef/resource/powershell_script.rb +8 -0
  114. data/lib/chef/resource/python.rb +4 -0
  115. data/lib/chef/resource/reboot.rb +14 -20
  116. data/lib/chef/resource/registry_key.rb +1 -0
  117. data/lib/chef/resource/remote_directory.rb +3 -0
  118. data/lib/chef/resource/remote_file.rb +2 -0
  119. data/lib/chef/resource/resource_notification.rb +17 -0
  120. data/lib/chef/resource/route.rb +1 -0
  121. data/lib/chef/resource/rpm_package.rb +1 -0
  122. data/lib/chef/resource/ruby.rb +4 -0
  123. data/lib/chef/resource/ruby_block.rb +3 -0
  124. data/lib/chef/resource/script.rb +4 -0
  125. data/lib/chef/resource/service.rb +1 -0
  126. data/lib/chef/resource/smartos_package.rb +1 -0
  127. data/lib/chef/resource/solaris_package.rb +1 -0
  128. data/lib/chef/resource/subversion.rb +1 -0
  129. data/lib/chef/resource/systemd_unit.rb +6 -0
  130. data/lib/chef/resource/template.rb +9 -0
  131. data/lib/chef/resource/user.rb +1 -0
  132. data/lib/chef/resource/windows_package.rb +2 -0
  133. data/lib/chef/resource/windows_path.rb +5 -10
  134. data/lib/chef/resource/windows_service.rb +3 -0
  135. data/lib/chef/resource/windows_task.rb +66 -87
  136. data/lib/chef/resource/yum_repository.rb +26 -22
  137. data/lib/chef/resource/zypper_package.rb +2 -0
  138. data/lib/chef/resource/zypper_repository.rb +6 -1
  139. data/lib/chef/run_context.rb +8 -2
  140. data/lib/chef/server_api.rb +1 -0
  141. data/lib/chef/util/selinux.rb +5 -4
  142. data/lib/chef/version.rb +1 -1
  143. data/lib/chef/version/platform.rb +18 -0
  144. data/lib/chef/version_constraint/platform.rb +2 -0
  145. data/spec/data/client.d_00/02-strings.rb +2 -0
  146. data/spec/functional/assets/chefinittest +6 -4
  147. data/spec/functional/knife/ssh_spec.rb +54 -7
  148. data/spec/functional/resource/bff_spec.rb +3 -3
  149. data/spec/functional/resource/ifconfig_spec.rb +1 -1
  150. data/spec/functional/resource/mount_spec.rb +7 -3
  151. data/spec/functional/resource/user/useradd_spec.rb +4 -4
  152. data/spec/functional/resource/windows_task_spec.rb +6 -6
  153. data/spec/functional/win32/security_spec.rb +7 -33
  154. data/spec/integration/knife/data_bag_show_spec.rb +1 -1
  155. data/spec/integration/recipes/noop_resource_spec.rb +1 -1
  156. data/spec/integration/recipes/recipe_dsl_spec.rb +30 -30
  157. data/spec/integration/recipes/resource_action_spec.rb +2 -2
  158. data/spec/integration/recipes/resource_converge_if_changed_spec.rb +71 -15
  159. data/spec/spec_helper.rb +19 -0
  160. data/spec/support/shared/functional/execute_resource.rb +1 -1
  161. data/spec/support/shared/unit/application_dot_d.rb +2 -0
  162. data/spec/support/shared/unit/execute_resource.rb +8 -1
  163. data/spec/support/shared/unit/provider/file.rb +9 -1
  164. data/spec/unit/chef_fs/data_handler/data_bag_item_data_handler.rb +10 -7
  165. data/spec/unit/chef_fs/file_system/repository/directory_spec.rb +2 -2
  166. data/spec/unit/client_spec.rb +1 -1
  167. data/spec/unit/deprecated_spec.rb +4 -4
  168. data/spec/unit/http_spec.rb +9 -0
  169. data/spec/unit/knife/bootstrap_spec.rb +5 -0
  170. data/spec/unit/knife/configure_spec.rb +10 -60
  171. data/spec/unit/knife/data_bag_create_spec.rb +40 -2
  172. data/spec/unit/knife/data_bag_show_spec.rb +16 -2
  173. data/spec/unit/knife/ssh_spec.rb +85 -39
  174. data/spec/unit/knife_spec.rb +2 -0
  175. data/spec/unit/lwrp_spec.rb +5 -3
  176. data/spec/unit/mixin/powershell_type_coercions_spec.rb +7 -6
  177. data/spec/unit/node/attribute_spec.rb +55 -24
  178. data/spec/unit/node/immutable_collections_spec.rb +28 -14
  179. data/spec/unit/node/vivid_mash_spec.rb +27 -10
  180. data/spec/unit/node_map_spec.rb +34 -0
  181. data/spec/unit/property_spec.rb +13 -13
  182. data/spec/unit/provider/group/dscl_spec.rb +14 -5
  183. data/spec/unit/provider/ifconfig_spec.rb +10 -3
  184. data/spec/unit/provider/remote_file/http_spec.rb +23 -19
  185. data/spec/unit/provider/service/solaris_smf_service_spec.rb +6 -5
  186. data/spec/unit/provider/user/dscl_spec.rb +26 -0
  187. data/spec/unit/provider/windows_task_spec.rb +148 -4
  188. data/spec/unit/provider_spec.rb +1 -1
  189. data/spec/unit/resource/apt_package_spec.rb +1 -1
  190. data/spec/unit/resource/bash_spec.rb +8 -10
  191. data/spec/unit/resource/batch_spec.rb +1 -1
  192. data/spec/unit/resource/cab_package_spec.rb +19 -1
  193. data/spec/unit/resource/chef_gem_spec.rb +3 -3
  194. data/spec/unit/resource/chocolatey_package_spec.rb +10 -10
  195. data/spec/unit/resource/conditional_spec.rb +2 -2
  196. data/spec/unit/resource/cookbook_file_spec.rb +24 -30
  197. data/spec/unit/resource/cron_spec.rb +79 -82
  198. data/spec/unit/resource/csh_spec.rb +8 -10
  199. data/spec/unit/resource/deploy_spec.rb +1 -1
  200. data/spec/unit/resource/directory_spec.rb +28 -31
  201. data/spec/unit/resource/dnf_package_spec.rb +9 -9
  202. data/spec/unit/resource/env_spec.rb +7 -7
  203. data/spec/unit/resource/erl_call_spec.rb +9 -9
  204. data/spec/unit/resource/execute_spec.rb +6 -6
  205. data/spec/unit/resource/file/verification_spec.rb +18 -4
  206. data/spec/unit/resource/file_spec.rb +53 -56
  207. data/spec/unit/resource/freebsd_package_spec.rb +7 -7
  208. data/spec/unit/resource/gem_package_spec.rb +1 -1
  209. data/spec/unit/resource/git_spec.rb +7 -9
  210. data/spec/unit/resource/group_spec.rb +60 -70
  211. data/spec/unit/resource/http_request_spec.rb +16 -19
  212. data/spec/unit/resource/ifconfig_spec.rb +3 -3
  213. data/spec/unit/resource/ips_package_spec.rb +3 -5
  214. data/spec/unit/resource/ksh_spec.rb +8 -10
  215. data/spec/unit/resource/launchd_spec.rb +17 -10
  216. data/spec/unit/resource/link_spec.rb +53 -53
  217. data/spec/unit/resource/log_spec.rb +24 -28
  218. data/spec/unit/resource/mdadm_spec.rb +42 -44
  219. data/spec/unit/resource/mount_spec.rb +97 -99
  220. data/spec/unit/resource/msu_package_spec.rb +14 -8
  221. data/spec/unit/resource/ohai_spec.rb +15 -17
  222. data/spec/unit/resource/openbsd_package_spec.rb +3 -3
  223. data/spec/unit/resource/osx_profile_spec.rb +7 -7
  224. data/spec/unit/resource/package_spec.rb +36 -40
  225. data/spec/unit/resource/perl_spec.rb +8 -11
  226. data/spec/unit/resource/portage_package_spec.rb +8 -10
  227. data/spec/unit/resource/powershell_package_spec.rb +9 -9
  228. data/spec/unit/resource/python_spec.rb +8 -11
  229. data/spec/unit/resource/reboot_spec.rb +50 -0
  230. data/spec/unit/resource/registry_key_spec.rb +84 -98
  231. data/spec/unit/resource/remote_directory_spec.rb +40 -42
  232. data/spec/unit/resource/remote_file_spec.rb +78 -80
  233. data/spec/unit/resource/route_spec.rb +42 -44
  234. data/spec/unit/resource/rpm_package_spec.rb +5 -7
  235. data/spec/unit/resource/ruby_block_spec.rb +14 -16
  236. data/spec/unit/resource/ruby_spec.rb +8 -12
  237. data/spec/unit/resource/scm_spec.rb +66 -69
  238. data/spec/unit/resource/script_spec.rb +1 -1
  239. data/spec/unit/resource/service_spec.rb +80 -83
  240. data/spec/unit/resource/smartos_package_spec.rb +5 -0
  241. data/spec/unit/resource/solaris_package_spec.rb +3 -5
  242. data/spec/unit/resource/subversion_spec.rb +18 -16
  243. data/spec/unit/resource/systemd_unit_spec.rb +50 -54
  244. data/spec/unit/resource/template_spec.rb +56 -61
  245. data/spec/unit/resource/user_spec.rb +47 -53
  246. data/spec/unit/resource/windows_package_spec.rb +1 -1
  247. data/spec/unit/resource/windows_path_spec.rb +11 -8
  248. data/spec/unit/resource/windows_task_spec.rb +129 -33
  249. data/spec/unit/resource/yum_package_spec.rb +1 -1
  250. data/spec/unit/resource/yum_repository_spec.rb +61 -8
  251. data/spec/unit/resource/zypper_repository_spec.rb +17 -18
  252. data/spec/unit/util/selinux_spec.rb +3 -6
  253. data/tasks/dependencies.rb +0 -5
  254. data/tasks/rspec.rb +1 -1
  255. metadata +6 -6
  256. data/acceptance/top-cookbooks/.kitchen.docker.yml +0 -13
  257. data/acceptance/top-cookbooks/.kitchen.git.yml +0 -11
@@ -63,13 +63,13 @@ class Chef
63
63
  MUTATOR_METHODS.each do |mutator|
64
64
  define_method(mutator) do |*args, &block|
65
65
  ret = super(*args, &block)
66
- send_reset_cache
66
+ send_reset_cache(__path__)
67
67
  ret
68
68
  end
69
69
  end
70
70
 
71
71
  def delete(key, &block)
72
- send_reset_cache(__path__, key)
72
+ send_reset_cache(__path__)
73
73
  super
74
74
  end
75
75
 
@@ -147,13 +147,13 @@ class Chef
147
147
  # object.
148
148
 
149
149
  def delete(key, &block)
150
- send_reset_cache(__path__, key)
150
+ send_reset_cache(__path__)
151
151
  super
152
152
  end
153
153
 
154
154
  MUTATOR_METHODS.each do |mutator|
155
155
  define_method(mutator) do |*args, &block|
156
- send_reset_cache
156
+ send_reset_cache(__path__)
157
157
  super(*args, &block)
158
158
  end
159
159
  end
@@ -174,7 +174,7 @@ class Chef
174
174
 
175
175
  def []=(key, value)
176
176
  ret = super
177
- send_reset_cache(__path__, key)
177
+ send_reset_cache(__path__)
178
178
  ret
179
179
  end
180
180
 
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright:: Copyright 2016, Chef Software, Inc.
2
+ # Copyright:: Copyright 2016-2017, Chef Software Inc.
3
3
  # License:: Apache License, Version 2.0
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,22 +30,16 @@ class Chef
30
30
  e
31
31
  end
32
32
 
33
- def convert_value(value)
33
+ def convert_value(value, path = nil)
34
34
  case value
35
35
  when Hash
36
- ImmutableMash.new(value, __root__, __node__, __precedence__)
36
+ ImmutableMash.new({}, __root__, __node__, __precedence__, path)
37
37
  when Array
38
- ImmutableArray.new(value, __root__, __node__, __precedence__)
39
- when ImmutableMash, ImmutableArray
40
- value
38
+ ImmutableArray.new([], __root__, __node__, __precedence__, path)
41
39
  else
42
40
  safe_dup(value).freeze
43
41
  end
44
42
  end
45
-
46
- def immutablize(value)
47
- convert_value(value)
48
- end
49
43
  end
50
44
 
51
45
  # == ImmutableArray
@@ -59,17 +53,51 @@ class Chef
59
53
  # Chef::Node::Attribute's values, it overrides all reader methods to
60
54
  # detect staleness and raise an error if accessed when stale.
61
55
  class ImmutableArray < Array
56
+ alias_method :internal_clear, :clear
57
+ alias_method :internal_replace, :replace
58
+ alias_method :internal_push, :<<
59
+ alias_method :internal_to_a, :to_a
60
+ alias_method :internal_each, :each
61
+ private :internal_push, :internal_replace, :internal_clear, :internal_each
62
+ protected :internal_to_a
63
+
62
64
  include Immutablize
63
65
 
64
- alias :internal_push :<<
65
- private :internal_push
66
+ methods = Array.instance_methods - Object.instance_methods +
67
+ [ :!, :!=, :<=>, :==, :===, :eql?, :to_s, :hash, :key, :has_key?, :inspect, :pretty_print, :pretty_print_inspect, :pretty_print_cycle, :pretty_print_instance_variables ]
66
68
 
67
- def initialize(array_data = [])
68
- array_data.each do |value|
69
- internal_push(immutablize(value))
69
+ methods.each do |method|
70
+ define_method method do |*args, &block|
71
+ ensure_generated_cache!
72
+ super(*args, &block)
70
73
  end
71
74
  end
72
75
 
76
+ def each
77
+ ensure_generated_cache!
78
+ # aggressively pre generate the cache, works around ruby being too smart and fiddling with internals
79
+ internal_each { |i| i.ensure_generated_cache! if i.respond_to?(:ensure_generated_cache!) }
80
+ super
81
+ end
82
+
83
+ # because sometimes ruby gives us back Arrays or ImmutableArrays out of objects from things like #uniq or array slices
84
+ def return_normal_array(array)
85
+ if array.respond_to?(:internal_to_a, true)
86
+ array.internal_to_a
87
+ else
88
+ array.to_a
89
+ end
90
+ end
91
+
92
+ def uniq
93
+ ensure_generated_cache!
94
+ return_normal_array(super)
95
+ end
96
+
97
+ def initialize(array_data = [])
98
+ # Immutable collections no longer have initialized state
99
+ end
100
+
73
101
  # For elements like Fixnums, true, nil...
74
102
  def safe_dup(e)
75
103
  e.dup
@@ -96,8 +124,81 @@ class Chef
96
124
 
97
125
  alias_method :to_array, :to_a
98
126
 
127
+ def [](*args)
128
+ ensure_generated_cache!
129
+ args.length > 1 ? return_normal_array(super) : super # correctly handle array slices
130
+ end
131
+
132
+ def reset
133
+ @generated_cache = false
134
+ @short_circuit_attr_level = nil
135
+ internal_clear # redundant?
136
+ end
137
+
138
+ # @api private
139
+ def ensure_generated_cache!
140
+ generate_cache unless @generated_cache
141
+ @generated_cache = true
142
+ end
143
+
144
+ # This can be set to e.g. [ :@default ] by the parent container to cause this container
145
+ # to only use the default level and to bypass deep merging (the common case is either
146
+ # default-level or automatic-level and we aren't doing any deep merging). Right now it
147
+ # "optimized" for the case where we're no longer merging anything and only tracking a
148
+ # single level, and setting this to anything other than a size=1 array would behave
149
+ # in a broken fashion. That could be fixed, but the perf boost would likely not be
150
+ # that large in the typical case.
151
+ #
152
+ # @api private
153
+ attr_accessor :short_circuit_attr_levels
154
+
99
155
  private
100
156
 
157
+ # deep merging of array attribute within normal and override where they are merged together
158
+ def combined_components(components)
159
+ combined_values = nil
160
+ components.each do |component|
161
+ values = __node__.attributes.instance_variable_get(component).read(*__path__)
162
+ next unless values.is_a?(Array)
163
+ @tracked_components << component
164
+ combined_values ||= []
165
+ combined_values += values
166
+ end
167
+ combined_values
168
+ end
169
+
170
+ def get_array(component)
171
+ array = __node__.attributes.instance_variable_get(component).read(*__path__)
172
+ if array.is_a?(Array)
173
+ @tracked_components << component
174
+ array
175
+ end # else nil
176
+ end
177
+
178
+ def generate_cache
179
+ internal_clear
180
+ components = []
181
+ @tracked_components = []
182
+ if short_circuit_attr_levels
183
+ components << get_array(short_circuit_attr_levels.first)
184
+ else
185
+ components << combined_components(Attribute::DEFAULT_COMPONENTS)
186
+ components << get_array(:@normal)
187
+ components << combined_components(Attribute::OVERRIDE_COMPONENTS)
188
+ components << get_array(:@automatic)
189
+ end
190
+ highest = components.compact.last
191
+ if highest.is_a?(Array)
192
+ internal_replace( highest.each_with_index.map { |x, i| convert_value(x, __path__ + [ i ] ) } )
193
+ end
194
+ if @tracked_components.size == 1
195
+ # tracked_components is accurate enough to tell us if we're not really merging
196
+ internal_each do |key, value|
197
+ value.short_circuit_attr_levels = @tracked_components if value.respond_to?(:short_circuit_attr_levels)
198
+ end
199
+ end
200
+ end
201
+
101
202
  # needed for __path__
102
203
  def convert_key(key)
103
204
  key
@@ -120,19 +221,31 @@ class Chef
120
221
  # it is stale.
121
222
  # * Values can be accessed in attr_reader-like fashion via method_missing.
122
223
  class ImmutableMash < Mash
224
+ alias_method :internal_clear, :clear
225
+ alias_method :internal_key?, :key? # FIXME: could bypass convert_key in Mash for perf
226
+ alias_method :internal_each, :each
227
+
123
228
  include Immutablize
124
229
  include CommonAPI
125
230
 
231
+ methods = Hash.instance_methods - Object.instance_methods +
232
+ [ :!, :!=, :<=>, :==, :===, :eql?, :to_s, :hash, :key, :has_key?, :inspect, :pretty_print, :pretty_print_inspect, :pretty_print_cycle, :pretty_print_instance_variables ]
233
+
234
+ methods.each do |method|
235
+ define_method method do |*args, &block|
236
+ ensure_generated_cache!
237
+ super(*args, &block)
238
+ end
239
+ end
240
+
126
241
  # this is for deep_merge usage, chef users must never touch this API
127
242
  # @api private
128
243
  def internal_set(key, value)
129
- regular_writer(key, convert_value(value))
244
+ regular_writer(key, convert_value(value, __path__ + [ key ]))
130
245
  end
131
246
 
132
247
  def initialize(mash_data = {})
133
- mash_data.each do |key, value|
134
- internal_set(key, value)
135
- end
248
+ # Immutable collections no longer have initialized state
136
249
  end
137
250
 
138
251
  alias :attribute? :has_key?
@@ -168,11 +281,55 @@ class Chef
168
281
 
169
282
  alias_method :to_hash, :to_h
170
283
 
171
- # For elements like Fixnums, true, nil...
172
- def safe_dup(e)
173
- e.dup
174
- rescue TypeError
175
- e
284
+ def [](key)
285
+ ensure_generated_cache!
286
+ super
287
+ end
288
+
289
+ alias_method :to_hash, :to_h
290
+
291
+ def reset
292
+ @generated_cache = false
293
+ @short_circuit_attr_level = nil
294
+ internal_clear # redundant?
295
+ end
296
+
297
+ # @api private
298
+ def ensure_generated_cache!
299
+ generate_cache unless @generated_cache
300
+ @generated_cache = true
301
+ end
302
+
303
+ # @api private
304
+ attr_accessor :short_circuit_attr_levels
305
+
306
+ private
307
+
308
+ def generate_cache
309
+ internal_clear
310
+ components = short_circuit_attr_levels ? short_circuit_attr_levels : Attribute::COMPONENTS.reverse
311
+ # tracked_components is not entirely accurate due to the short-circuit
312
+ tracked_components = []
313
+ components.each do |component|
314
+ subhash = __node__.attributes.instance_variable_get(component).read(*__path__)
315
+ unless subhash.nil? # FIXME: nil is used for not present
316
+ tracked_components << component
317
+ if subhash.kind_of?(Hash)
318
+ subhash.keys.each do |key|
319
+ next if internal_key?(key)
320
+ internal_set(key, subhash[key])
321
+ end
322
+ else
323
+ break
324
+ end
325
+ end
326
+ end
327
+ if tracked_components.size == 1
328
+ # tracked_components is accurate enough to tell us if we're not really merging
329
+ internal_each do |key, value|
330
+ value.short_circuit_attr_levels = tracked_components if value.respond_to?(:short_circuit_attr_levels)
331
+ end
332
+ end
176
333
  end
177
334
 
178
335
  prepend Chef::Node::Mixin::StateTracking
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright:: Copyright 2016, Chef Software, Inc.
2
+ # Copyright:: Copyright 2016-2017, Chef Software Inc.
3
3
  # License:: Apache License, Version 2.0
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,11 +24,12 @@ class Chef
24
24
  attr_reader :__node__
25
25
  attr_reader :__precedence__
26
26
 
27
- def initialize(data = nil, root = self, node = nil, precedence = nil)
27
+ def initialize(data = nil, root = self, node = nil, precedence = nil, path = nil)
28
28
  # __path__ and __root__ must be nil when we call super so it knows
29
29
  # to avoid resetting the cache on construction
30
30
  data.nil? ? super() : super(data)
31
- @__path__ = []
31
+ @__path__ = path
32
+ @__path__ ||= []
32
33
  @__root__ = root
33
34
  @__node__ = node
34
35
  @__precedence__ = precedence
@@ -76,9 +77,8 @@ class Chef
76
77
  end
77
78
  end
78
79
 
79
- def send_reset_cache(path = nil, key = nil)
80
- next_path = [ path, key ].flatten.compact
81
- __root__.reset_cache(next_path.first) if !__root__.nil? && __root__.respond_to?(:reset_cache) && !next_path.nil?
80
+ def send_reset_cache(path)
81
+ __root__.reset_cache(*path) if !__root__.nil? && __root__.respond_to?(:reset_cache) && !path.nil?
82
82
  end
83
83
 
84
84
  def copy_state_to(ret, next_path)
@@ -16,6 +16,25 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
+ #
20
+ # example of a NodeMap entry for the user resource (as typed on the DSL):
21
+ #
22
+ # :user=>
23
+ # [{:klass=>Chef::Resource::User::AixUser, :os=>"aix"},
24
+ # {:klass=>Chef::Resource::User::DsclUser, :os=>"darwin"},
25
+ # {:klass=>Chef::Resource::User::PwUser, :os=>"freebsd"},
26
+ # {:klass=>Chef::Resource::User::LinuxUser, :os=>"linux"},
27
+ # {:klass=>Chef::Resource::User::SolarisUser,
28
+ # :os=>["omnios", "solaris2"]},
29
+ # {:klass=>Chef::Resource::User::WindowsUser, :os=>"windows"}],
30
+ #
31
+ # the entries in the array are pre-sorted into priority order (blocks/platform_version/platform/platform_family/os/none) so that
32
+ # the first entry's :klass that matches the filter is returned when doing a get.
33
+ #
34
+ # note that as this examples show filter values may be a scalar string or an array of scalar strings.
35
+ #
36
+ # XXX: confusingly, in the *_priority_map the :klass may be an array of Strings of class names
37
+ #
19
38
  class Chef
20
39
  class NodeMap
21
40
 
@@ -31,13 +50,12 @@ class Chef
31
50
  #
32
51
  # @return [NodeMap] Returns self for possible chaining
33
52
  #
34
- def set(key, value, platform: nil, platform_version: nil, platform_family: nil, os: nil, canonical: nil, override: nil, &block)
35
- filters = {}
36
- filters[:platform] = platform if platform
37
- filters[:platform_version] = platform_version if platform_version
38
- filters[:platform_family] = platform_family if platform_family
39
- filters[:os] = os if os
40
- new_matcher = { value: value, filters: filters }
53
+ def set(key, klass, platform: nil, platform_version: nil, platform_family: nil, os: nil, canonical: nil, override: nil, &block)
54
+ new_matcher = { klass: klass }
55
+ new_matcher[:platform] = platform if platform
56
+ new_matcher[:platform_version] = platform_version if platform_version
57
+ new_matcher[:platform_family] = platform_family if platform_family
58
+ new_matcher[:os] = os if os
41
59
  new_matcher[:block] = block if block
42
60
  new_matcher[:canonical] = canonical if canonical
43
61
  new_matcher[:override] = override if override
@@ -48,7 +66,10 @@ class Chef
48
66
  map[key] ||= []
49
67
  map[key].each_with_index do |matcher, index|
50
68
  cmp = compare_matchers(key, new_matcher, matcher)
51
- insert_at ||= index if cmp && cmp <= 0
69
+ if cmp && cmp <= 0
70
+ insert_at = index
71
+ break
72
+ end
52
73
  end
53
74
  if insert_at
54
75
  map[key].insert(insert_at, new_matcher)
@@ -68,11 +89,14 @@ class Chef
68
89
  # @param canonical [Boolean] `true` or `false` to match canonical or
69
90
  # non-canonical values only. `nil` to ignore canonicality. Default: `nil`
70
91
  #
71
- # @return [Object] Value
92
+ # @return [Object] Class
72
93
  #
73
94
  def get(node, key, canonical: nil)
74
- raise ArgumentError, "first argument must be a Chef::Node" unless node.is_a?(Chef::Node) || node.nil?
75
- list(node, key, canonical: canonical).first
95
+ return nil unless map.has_key?(key)
96
+ map[key].map do |matcher|
97
+ return matcher[:klass] if node_matches?(node, matcher) && canonical_matches?(canonical, matcher)
98
+ end
99
+ nil
76
100
  end
77
101
 
78
102
  #
@@ -85,23 +109,22 @@ class Chef
85
109
  # @param canonical [Boolean] `true` or `false` to match canonical or
86
110
  # non-canonical values only. `nil` to ignore canonicality. Default: `nil`
87
111
  #
88
- # @return [Object] Value
112
+ # @return [Object] Class
89
113
  #
90
114
  def list(node, key, canonical: nil)
91
- raise ArgumentError, "first argument must be a Chef::Node" unless node.is_a?(Chef::Node) || node.nil?
92
115
  return [] unless map.has_key?(key)
93
116
  map[key].select do |matcher|
94
117
  node_matches?(node, matcher) && canonical_matches?(canonical, matcher)
95
- end.map { |matcher| matcher[:value] }
118
+ end.map { |matcher| matcher[:klass] }
96
119
  end
97
120
 
98
121
  # Seriously, don't use this, it's nearly certain to change on you
99
122
  # @return remaining
100
123
  # @api private
101
- def delete_canonical(key, value)
124
+ def delete_canonical(key, klass)
102
125
  remaining = map[key]
103
126
  if remaining
104
- remaining.delete_if { |matcher| matcher[:canonical] && Array(matcher[:value]) == Array(value) }
127
+ remaining.delete_if { |matcher| matcher[:canonical] && Array(matcher[:klass]) == Array(klass) }
105
128
  if remaining.empty?
106
129
  map.delete(key)
107
130
  remaining = nil
@@ -143,7 +166,7 @@ class Chef
143
166
 
144
167
  filter_values.empty? ||
145
168
  Array(filter_values).any? do |v|
146
- Chef::VersionConstraint::Platform.new(v).include?(value)
169
+ Gem::Requirement.new(v).satisfied_by?(Gem::Version.new(value))
147
170
  end
148
171
  end
149
172
 
@@ -161,7 +184,7 @@ class Chef
161
184
 
162
185
  def node_matches?(node, matcher)
163
186
  return true if !node
164
- filters_match?(node, matcher[:filters]) && block_matches?(node, matcher[:block])
187
+ filters_match?(node, matcher) && block_matches?(node, matcher[:block])
165
188
  end
166
189
 
167
190
  def canonical_matches?(canonical, matcher)
@@ -171,17 +194,17 @@ class Chef
171
194
 
172
195
  # @api private
173
196
  def dispatch_compare_matchers(key, new_matcher, matcher)
174
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:block] }
197
+ cmp = compare_matcher_properties(new_matcher[:block], matcher[:block])
175
198
  return cmp if cmp != 0
176
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:platform_version] }
199
+ cmp = compare_matcher_properties(new_matcher[:platform_version], matcher[:platform_version])
177
200
  return cmp if cmp != 0
178
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:platform] }
201
+ cmp = compare_matcher_properties(new_matcher[:platform], matcher[:platform])
179
202
  return cmp if cmp != 0
180
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:platform_family] }
203
+ cmp = compare_matcher_properties(new_matcher[:platform_family], matcher[:platform_family])
181
204
  return cmp if cmp != 0
182
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:os] }
205
+ cmp = compare_matcher_properties(new_matcher[:os], matcher[:os])
183
206
  return cmp if cmp != 0
184
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:override] }
207
+ cmp = compare_matcher_properties(new_matcher[:override], matcher[:override])
185
208
  return cmp if cmp != 0
186
209
  # If all things are identical, return 0
187
210
  0
@@ -195,37 +218,32 @@ class Chef
195
218
  if cmp == 0
196
219
  # Sort by class name (ascending) as well, if all other properties
197
220
  # are exactly equal
198
- if new_matcher[:value].is_a?(Class) && !new_matcher[:override]
199
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:value].name }
221
+ # XXX: remove this in Chef-14 and use last-writer-wins (prepend if they match)
222
+ if !new_matcher[:override]
223
+ # we only sort classes, which only sorts the handler array, this magically does not sort
224
+ # the priority array via the invisible else here.
225
+ if new_matcher[:klass].is_a?(Class)
226
+ cmp = compare_matcher_properties(new_matcher[:klass].name, matcher[:klass].name)
227
+ end
200
228
  end
201
229
  end
202
230
  cmp
203
231
  end
204
232
 
205
- def compare_matcher_properties(new_matcher, matcher)
206
- a = yield(new_matcher)
207
- b = yield(matcher)
233
+ def compare_matcher_properties(a, b)
234
+ # falsity comparisons here handle both "nil" and "false"
235
+ return 1 if !a && b
236
+ return -1 if !b && a
237
+ return 0 if !a && !b
208
238
 
209
- # Check for blcacklists ('!windows'). Those always come *after* positive
239
+ # Check for blacklists ('!windows'). Those always come *after* positive
210
240
  # whitelists.
211
241
  a_negated = Array(a).any? { |f| f.is_a?(String) && f.start_with?("!") }
212
242
  b_negated = Array(b).any? { |f| f.is_a?(String) && f.start_with?("!") }
213
- if a_negated != b_negated
214
- return 1 if a_negated
215
- return -1 if b_negated
216
- end
243
+ return 1 if a_negated && !b_negated
244
+ return -1 if b_negated && !a_negated
217
245
 
218
- # We treat false / true and nil / not-nil with the same comparison
219
- a = nil if a == false
220
- b = nil if b == false
221
- cmp = a <=> b
222
- # This is the case where one is non-nil, and one is nil. The one that is
223
- # nil is "greater" (i.e. it should come last).
224
- if cmp.nil?
225
- return 1 if a.nil?
226
- return -1 if b.nil?
227
- end
228
- cmp
246
+ a <=> b
229
247
  end
230
248
 
231
249
  def map