chef 12.0.0.rc.0 → 12.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +1 -1
  3. data/lib/chef/api_client/registration.rb +3 -1
  4. data/lib/chef/chef_fs/data_handler/group_data_handler.rb +4 -0
  5. data/lib/chef/config.rb +46 -38
  6. data/lib/chef/event_loggers/windows_eventlog.rb +5 -6
  7. data/lib/chef/exceptions.rb +13 -1
  8. data/lib/chef/file_content_management/tempfile.rb +33 -5
  9. data/lib/chef/knife.rb +11 -3
  10. data/lib/chef/knife/bootstrap.rb +8 -7
  11. data/lib/chef/mixin/deep_merge.rb +15 -54
  12. data/lib/chef/mixin/which.rb +37 -0
  13. data/lib/chef/node.rb +14 -25
  14. data/lib/chef/node/attribute.rb +227 -41
  15. data/lib/chef/node/attribute_collections.rb +117 -3
  16. data/lib/chef/node/immutable_collections.rb +6 -6
  17. data/lib/chef/platform/provider_priority_map.rb +3 -2
  18. data/lib/chef/platform/service_helpers.rb +37 -8
  19. data/lib/chef/provider/service/aixinit.rb +1 -1
  20. data/lib/chef/provider/service/arch.rb +1 -1
  21. data/lib/chef/provider/service/debian.rb +5 -1
  22. data/lib/chef/provider/service/init.rb +4 -0
  23. data/lib/chef/provider/service/insserv.rb +5 -1
  24. data/lib/chef/provider/service/invokercd.rb +5 -1
  25. data/lib/chef/provider/service/redhat.rb +5 -1
  26. data/lib/chef/provider/service/systemd.rb +50 -32
  27. data/lib/chef/provider/service/upstart.rb +5 -2
  28. data/lib/chef/provider_resolver.rb +30 -16
  29. data/lib/chef/resource.rb +2 -1
  30. data/lib/chef/resources.rb +7 -0
  31. data/lib/chef/run_context.rb +0 -5
  32. data/lib/chef/run_list/run_list_expansion.rb +2 -2
  33. data/lib/chef/shell.rb +2 -2
  34. data/lib/chef/util/selinux.rb +2 -10
  35. data/lib/chef/version.rb +1 -1
  36. data/lib/chef/workstation_config_loader.rb +1 -1
  37. data/spec/support/shared/unit/resource/static_provider_resolution.rb +1 -6
  38. data/spec/unit/api_client/registration_spec.rb +22 -0
  39. data/spec/unit/application/knife_spec.rb +6 -2
  40. data/spec/unit/chef_fs/data_handler/group_handler_spec.rb +63 -0
  41. data/spec/unit/config_spec.rb +5 -5
  42. data/spec/unit/knife/bootstrap_spec.rb +27 -1
  43. data/spec/unit/knife_spec.rb +5 -0
  44. data/spec/unit/mixin/deep_merge_spec.rb +0 -40
  45. data/spec/unit/node/attribute_spec.rb +37 -50
  46. data/spec/unit/node_spec.rb +321 -13
  47. data/spec/unit/provider/file/content_spec.rb +23 -2
  48. data/spec/unit/provider/service/systemd_service_spec.rb +173 -158
  49. data/spec/unit/provider_resolver_spec.rb +175 -10
  50. data/spec/unit/resource/timestamped_deploy_spec.rb +8 -29
  51. data/spec/unit/runner_spec.rb +3 -1
  52. metadata +141 -191
  53. data/spec/.DS_Store +0 -0
  54. data/spec/data/.DS_Store +0 -0
  55. data/spec/data/lwrp/.DS_Store +0 -0
  56. data/spec/data/lwrp/providers/.DS_Store +0 -0
  57. data/spec/data/lwrp/resources/.DS_Store +0 -0
  58. data/spec/data/lwrp_override/.DS_Store +0 -0
  59. data/spec/data/lwrp_override/providers/.DS_Store +0 -0
  60. data/spec/data/lwrp_override/resources/.DS_Store +0 -0
@@ -0,0 +1,37 @@
1
+ #--
2
+ # Author:: Lamont Granquist <lamont@getchef.io>
3
+ # Copyright:: Copyright (c) 2010 Opscode, Inc.
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
+ class Chef
19
+ module Mixin
20
+ module Which
21
+ def which(cmd, opts = {})
22
+ extra_path =
23
+ if opts[:extra_path].nil?
24
+ [ '/bin', '/usr/bin', '/sbin', '/usr/sbin' ]
25
+ else
26
+ [ opts[:extra_path] ].flatten
27
+ end
28
+ paths = ENV['PATH'].split(File::PATH_SEPARATOR) + extra_path
29
+ paths.each do |path|
30
+ filename = File.join(path, cmd)
31
+ return filename if File.executable?(filename)
32
+ end
33
+ false
34
+ end
35
+ end
36
+ end
37
+ end
@@ -42,6 +42,8 @@ class Chef
42
42
  extend Forwardable
43
43
 
44
44
  def_delegators :attributes, :keys, :each_key, :each_value, :key?, :has_key?
45
+ def_delegators :attributes, :rm, :rm_default, :rm_normal, :rm_override
46
+ def_delegators :attributes, :default!, :normal!, :override!, :force_default!, :force_override!
45
47
 
46
48
  attr_accessor :recipe_list, :run_state, :override_runlist
47
49
 
@@ -125,6 +127,7 @@ class Chef
125
127
  # Set a normal attribute of this node, but auto-vivify any Mashes that
126
128
  # might be missing
127
129
  def normal
130
+ attributes.top_level_breadcrumb = nil
128
131
  attributes.set_unless_value_present = false
129
132
  attributes.normal
130
133
  end
@@ -134,28 +137,25 @@ class Chef
134
137
  # Set a normal attribute of this node, auto-vivifying any mashes that are
135
138
  # missing, but if the final value already exists, don't set it
136
139
  def normal_unless
140
+ attributes.top_level_breadcrumb = nil
137
141
  attributes.set_unless_value_present = true
138
142
  attributes.normal
139
143
  end
144
+
140
145
  alias_method :set_unless, :normal_unless
141
146
 
142
147
  # Set a default of this node, but auto-vivify any Mashes that might
143
148
  # be missing
144
149
  def default
150
+ attributes.top_level_breadcrumb = nil
145
151
  attributes.set_unless_value_present = false
146
152
  attributes.default
147
153
  end
148
154
 
149
- # Set a force default attribute. Intermediate mashes will be created by
150
- # auto-vivify if necessary.
151
- def default!
152
- attributes.set_unless_value_present = false
153
- attributes.default!
154
- end
155
-
156
155
  # Set a default attribute of this node, auto-vivifying any mashes that are
157
156
  # missing, but if the final value already exists, don't set it
158
157
  def default_unless
158
+ attributes.top_level_breadcrumb = nil
159
159
  attributes.set_unless_value_present = true
160
160
  attributes.default
161
161
  end
@@ -163,49 +163,38 @@ class Chef
163
163
  # Set an override attribute of this node, but auto-vivify any Mashes that
164
164
  # might be missing
165
165
  def override
166
+ attributes.top_level_breadcrumb = nil
166
167
  attributes.set_unless_value_present = false
167
168
  attributes.override
168
169
  end
169
170
 
170
- # Set a force override attribute. Intermediate mashes will be created by
171
- # auto-vivify if needed.
172
- def override!
173
- attributes.set_unless_value_present = false
174
- attributes.override!
175
- end
176
-
177
171
  # Set an override attribute of this node, auto-vivifying any mashes that
178
172
  # are missing, but if the final value already exists, don't set it
179
173
  def override_unless
174
+ attributes.top_level_breadcrumb = nil
180
175
  attributes.set_unless_value_present = true
181
176
  attributes.override
182
177
  end
183
178
 
184
- def override_attrs
185
- attributes.override
186
- end
179
+ alias :override_attrs :override
180
+ alias :default_attrs :default
181
+ alias :normal_attrs :normal
187
182
 
188
183
  def override_attrs=(new_values)
189
184
  attributes.override = new_values
190
185
  end
191
186
 
192
- def default_attrs
193
- attributes.default
194
- end
195
-
196
187
  def default_attrs=(new_values)
197
188
  attributes.default = new_values
198
189
  end
199
190
 
200
- def normal_attrs
201
- attributes.normal
202
- end
203
-
204
191
  def normal_attrs=(new_values)
205
192
  attributes.normal = new_values
206
193
  end
207
194
 
208
195
  def automatic_attrs
196
+ attributes.top_level_breadcrumb = nil
197
+ attributes.set_unless_value_present = false
209
198
  attributes.automatic
210
199
  end
211
200
 
@@ -58,7 +58,6 @@ class Chef
58
58
  :@force_default
59
59
  ]
60
60
 
61
-
62
61
  OVERRIDE_COMPONENTS = [
63
62
  :@override,
64
63
  :@role_override,
@@ -146,7 +145,6 @@ class Chef
146
145
  METHOD_DEFN
147
146
  end
148
147
 
149
-
150
148
  # return the cookbook level default attribute component
151
149
  attr_reader :default
152
150
 
@@ -159,11 +157,6 @@ class Chef
159
157
  # return the force_default level attribute component
160
158
  attr_reader :force_default
161
159
 
162
- # default! is the "advertised" method for force_default, but is
163
- # implemented as an alias because instance variables can't (easily) have
164
- # +!+ characters.
165
- alias :default! :force_default
166
-
167
160
  # return the "normal" level attribute component
168
161
  attr_reader :normal
169
162
 
@@ -179,14 +172,22 @@ class Chef
179
172
  # return the force override level attribute component
180
173
  attr_reader :force_override
181
174
 
182
- # +override!+ is the "advertised" method for +force_override+ but is
183
- # implemented as an alias because instance variables can't easily have
184
- # +!+ characters.
185
- alias :override! :force_override
186
-
187
175
  # return the automatic level attribute component
188
176
  attr_reader :automatic
189
177
 
178
+ # This is used to track the top level key as we descend through method chaining into
179
+ # a precedence level (e.g. node.default['foo']['bar']['baz']= results in 'foo' here). We
180
+ # need this so that when we hit the end of a method chain which results in a mutator method
181
+ # that we can invalidate the whole top-level deep merge cache for the top-level key. It is
182
+ # the responsibility of the accessor on the Chef::Node object to reset this to nil, and then
183
+ # the first VividMash#[] call can ||= and set this to the first key we encounter.
184
+ attr_accessor :top_level_breadcrumb
185
+
186
+ # Cache of deep merged values by top-level key. This is a simple hash which has keys that are the
187
+ # top-level keys of the node object, and we save the computed deep-merge for that key here. There is
188
+ # no cache of subtrees.
189
+ attr_accessor :deep_merge_cache
190
+
190
191
  def initialize(normal, default, override, automatic)
191
192
  @set_unless_present = false
192
193
 
@@ -207,6 +208,8 @@ class Chef
207
208
  @merged_attributes = nil
208
209
  @combined_override = nil
209
210
  @combined_default = nil
211
+ @top_level_breadcrumb = nil
212
+ @deep_merge_cache = {}
210
213
  end
211
214
 
212
215
  # Debug what's going on with an attribute. +args+ is a path spec to the
@@ -242,13 +245,16 @@ class Chef
242
245
  @set_unless_present = setting
243
246
  end
244
247
 
245
- # Clears merged_attributes, which will cause it to be recomputed on the
246
- # next access.
247
- def reset_cache
248
- @merged_attributes = nil
249
- @combined_default = nil
250
- @combined_override = nil
251
- @set_unless_present = false
248
+ # Invalidate a key in the deep_merge_cache. If called with nil, or no arg, this will invalidate
249
+ # the entire deep_merge cache. In the case of the user doing node.default['foo']['bar']['baz']=
250
+ # that eventually results in a call to reset_cache('foo') here. A node.default=hash_thing call
251
+ # must invalidate the entire cache and re-deep-merge the entire node object.
252
+ def reset_cache(path = nil)
253
+ if path.nil?
254
+ @deep_merge_cache = {}
255
+ else
256
+ deep_merge_cache.delete(path)
257
+ end
252
258
  end
253
259
 
254
260
  alias :reset :reset_cache
@@ -311,30 +317,136 @@ class Chef
311
317
  @automatic = VividMash.new(self, new_data)
312
318
  end
313
319
 
314
- def merged_attributes
315
- @merged_attributes ||= begin
316
- components = [merge_defaults, @normal, merge_overrides, @automatic]
317
- resolved_attrs = components.inject(Mash.new) do |merged, component|
318
- Chef::Mixin::DeepMerge.hash_only_merge(merged, component)
319
- end
320
- immutablize(resolved_attrs)
321
- end
320
+ #
321
+ # Deleting attributes
322
+ #
323
+
324
+ # clears attributes from all precedence levels
325
+ def rm(*args)
326
+ reset(args[0])
327
+ # just easier to compute our retval, rather than collect+merge sub-retvals
328
+ ret = args.inject(merged_attributes) do |attr, arg|
329
+ if attr.nil? || !attr.respond_to?(:[])
330
+ nil
331
+ else
332
+ begin
333
+ attr[arg]
334
+ rescue TypeError
335
+ raise TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)"
336
+ end
337
+ end
338
+ end
339
+ rm_default(*args)
340
+ rm_normal(*args)
341
+ rm_override(*args)
342
+ ret
343
+ end
344
+
345
+ # does <level>['foo']['bar'].delete('baz')
346
+ def remove_from_precedence_level(level, *args, key)
347
+ multimash = level.element(*args)
348
+ multimash.nil? ? nil : multimash.delete(key)
349
+ end
350
+
351
+ private :remove_from_precedence_level
352
+
353
+ # clears attributes from all default precedence levels
354
+ #
355
+ # equivalent to: force_default!['foo']['bar'].delete('baz')
356
+ def rm_default(*args)
357
+ reset(args[0])
358
+ remove_from_precedence_level(force_default!(autovivify: false), *args)
359
+ end
360
+
361
+ # clears attributes from normal precedence
362
+ #
363
+ # equivalent to: normal!['foo']['bar'].delete('baz')
364
+ def rm_normal(*args)
365
+ reset(args[0])
366
+ remove_from_precedence_level(normal!(autovivify: false), *args)
367
+ end
368
+
369
+ # clears attributes from all override precedence levels
370
+ #
371
+ # equivalent to: force_override!['foo']['bar'].delete('baz')
372
+ def rm_override(*args)
373
+ reset(args[0])
374
+ remove_from_precedence_level(force_override!(autovivify: false), *args)
375
+ end
376
+
377
+ #
378
+ # Replacing attributes without merging
379
+ #
380
+
381
+ # sets default attributes without merging
382
+ def default!(opts={})
383
+ # FIXME: do not flush whole cache
384
+ reset
385
+ MultiMash.new(self, @default, [], opts)
386
+ end
387
+
388
+ # sets normal attributes without merging
389
+ def normal!(opts={})
390
+ # FIXME: do not flush whole cache
391
+ reset
392
+ MultiMash.new(self, @normal, [], opts)
393
+ end
394
+
395
+ # sets override attributes without merging
396
+ def override!(opts={})
397
+ # FIXME: do not flush whole cache
398
+ reset
399
+ MultiMash.new(self, @override, [], opts)
322
400
  end
323
401
 
324
- def combined_override
325
- @combined_override ||= immutablize(merge_overrides)
402
+ # clears from all default precedence levels and then sets force_default
403
+ def force_default!(opts={})
404
+ # FIXME: do not flush whole cache
405
+ reset
406
+ MultiMash.new(self, @force_default, [@default, @env_default, @role_default], opts)
326
407
  end
327
408
 
328
- def combined_default
329
- @combined_default ||= immutablize(merge_defaults)
409
+ # clears from all override precedence levels and then sets force_override
410
+ def force_override!(opts={})
411
+ # FIXME: do not flush whole cache
412
+ reset
413
+ MultiMash.new(self, @force_override, [@override, @env_override, @role_override], opts)
414
+ end
415
+
416
+ #
417
+ # Accessing merged attributes.
418
+ #
419
+ # Note that merged_attributes('foo', 'bar', 'baz') can be called to compute only the
420
+ # deep merge of node['foo']['bar']['baz'], but in practice we currently always compute
421
+ # all of node['foo'] even if the user only requires node['foo']['bar']['baz'].
422
+ #
423
+
424
+ def merged_attributes(*path)
425
+ # immutablize(
426
+ merge_all(path)
427
+ # )
428
+ end
429
+
430
+ def combined_override(*path)
431
+ immutablize(merge_overrides(path))
432
+ end
433
+
434
+ def combined_default(*path)
435
+ immutablize(merge_defaults(path))
330
436
  end
331
437
 
332
438
  def [](key)
333
- merged_attributes[key]
439
+ if deep_merge_cache.has_key?(key)
440
+ # return the cache of the deep merged values by top-level key
441
+ deep_merge_cache[key]
442
+ else
443
+ # save all the work of computing node[key]
444
+ deep_merge_cache[key] = merged_attributes(key)
445
+ end
334
446
  end
335
447
 
336
448
  def []=(key, value)
337
- merged_attributes[key] = value
449
+ raise Exceptions::ImmutableAttributeModification
338
450
  end
339
451
 
340
452
  def has_key?(key)
@@ -377,20 +489,94 @@ class Chef
377
489
 
378
490
  private
379
491
 
380
- def merge_defaults
381
- DEFAULT_COMPONENTS.inject(Mash.new) do |merged, component_ivar|
382
- component_value = instance_variable_get(component_ivar)
383
- Chef::Mixin::DeepMerge.merge(merged, component_value)
492
+ # Helper method for merge_all/merge_defaults/merge_overrides.
493
+ #
494
+ # apply_path(thing, [ "foo", "bar", "baz" ]) = thing["foo"]["bar"]["baz"]
495
+ #
496
+ # The path value can be nil in which case the whole component is returned.
497
+ #
498
+ # It returns nil (does not raise an exception) if it walks off the end of an Mash/Hash/Array, it does not
499
+ # raise any TypeError if it attempts to apply a hash key to an Integer/String/TrueClass, and just returns
500
+ # nil in that case.
501
+ #
502
+ def apply_path(component, path)
503
+ path ||= []
504
+ path.inject(component) do |val, path_arg|
505
+ if val.respond_to?(:[])
506
+ # Have an Array-like or Hash-like thing
507
+ if !val.respond_to?(:has_key?)
508
+ # Have an Array-like thing
509
+ val[path_arg]
510
+ elsif val.has_key?(path_arg)
511
+ # Hash-like thing (must check has_key? first to protect against Autovivification)
512
+ val[path_arg]
513
+ else
514
+ nil
515
+ end
516
+ else
517
+ nil
518
+ end
519
+ end
520
+ end
521
+
522
+ # For elements like Fixnums, true, nil...
523
+ def safe_dup(e)
524
+ e.dup
525
+ rescue TypeError
526
+ e
527
+ end
528
+
529
+ # Deep merge all attribute levels using hash-only merging between different precidence
530
+ # levels (so override arrays completely replace arrays set at any default level).
531
+ #
532
+ # The path allows for selectively deep-merging a subtree of the node object.
533
+ #
534
+ # @param path [Array] Array of args to method chain to descend into the node object
535
+ # @return [attr] Deep Merged values (may be VividMash, Hash, Array, etc) from the node object
536
+ def merge_all(path)
537
+ components = [
538
+ merge_defaults(path),
539
+ apply_path(@normal, path),
540
+ merge_overrides(path),
541
+ apply_path(@automatic, path),
542
+ ]
543
+
544
+ components.map! do |component|
545
+ safe_dup(component)
546
+ end
547
+
548
+ return nil if components.compact.empty?
549
+
550
+ components.inject(ImmutableMash.new({})) do |merged, component|
551
+ Chef::Mixin::DeepMerge.hash_only_merge!(merged, component)
384
552
  end
385
553
  end
386
554
 
387
- def merge_overrides
388
- OVERRIDE_COMPONENTS.inject(Mash.new) do |merged, component_ivar|
389
- component_value = instance_variable_get(component_ivar)
390
- Chef::Mixin::DeepMerge.merge(merged, component_value)
555
+ # Deep merge the default attribute levels with array merging.
556
+ #
557
+ # The path allows for selectively deep-merging a subtree of the node object.
558
+ #
559
+ # @param path [Array] Array of args to method chain to descend into the node object
560
+ # @return [attr] Deep Merged values (may be VividMash, Hash, Array, etc) from the node object
561
+ def merge_defaults(path)
562
+ DEFAULT_COMPONENTS.inject(nil) do |merged, component_ivar|
563
+ component_value = apply_path(instance_variable_get(component_ivar), path)
564
+ Chef::Mixin::DeepMerge.deep_merge(component_value, merged)
391
565
  end
392
566
  end
393
567
 
568
+ # Deep merge the override attribute levels with array merging.
569
+ #
570
+ # The path allows for selectively deep-merging a subtree of the node object.
571
+ #
572
+ # @param path [Array] Array of args to method chain to descend into the node object
573
+ # @return [attr] Deep Merged values (may be VividMash, Hash, Array, etc) from the node object
574
+ def merge_overrides(path)
575
+ OVERRIDE_COMPONENTS.inject(nil) do |merged, component_ivar|
576
+ component_value = apply_path(instance_variable_get(component_ivar), path)
577
+ Chef::Mixin::DeepMerge.deep_merge(component_value, merged)
578
+ end
579
+ end
394
580
 
395
581
  end
396
582