chef 12.0.1 → 12.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb +1 -1
  3. data/lib/chef/digester.rb +1 -0
  4. data/lib/chef/dsl/recipe.rb +2 -1
  5. data/lib/chef/exceptions.rb +5 -0
  6. data/lib/chef/knife.rb +7 -0
  7. data/lib/chef/knife/cookbook_site_install.rb +34 -10
  8. data/lib/chef/provider/link.rb +1 -1
  9. data/lib/chef/provider/package/apt.rb +2 -2
  10. data/lib/chef/provider/package/homebrew.rb +11 -2
  11. data/lib/chef/provider/package/windows/msi.rb +2 -0
  12. data/lib/chef/provider/subversion.rb +3 -3
  13. data/lib/chef/resource.rb +23 -91
  14. data/lib/chef/resource/homebrew_package.rb +2 -1
  15. data/lib/chef/resource/resource_notification.rb +109 -0
  16. data/lib/chef/resource_collection/resource_set.rb +8 -8
  17. data/lib/chef/run_context.rb +4 -4
  18. data/lib/chef/version.rb +1 -1
  19. data/lib/chef/whitelist.rb +3 -1
  20. data/lib/chef/win32/api/file.rb +17 -3
  21. data/spec/functional/notifications_spec.rb +169 -0
  22. data/spec/functional/resource/link_spec.rb +31 -32
  23. data/spec/support/platform_helpers.rb +5 -2
  24. data/spec/unit/knife/cookbook_site_install_spec.rb +157 -116
  25. data/spec/unit/knife_spec.rb +108 -78
  26. data/spec/unit/mixin/shell_out_spec.rb +39 -40
  27. data/spec/unit/node_spec.rb +34 -0
  28. data/spec/unit/provider/link_spec.rb +5 -5
  29. data/spec/unit/provider/package/apt_spec.rb +264 -257
  30. data/spec/unit/provider/package/homebrew_spec.rb +26 -0
  31. data/spec/unit/provider/package/windows/msi_spec.rb +18 -3
  32. data/spec/unit/provider/subversion_spec.rb +5 -5
  33. data/spec/unit/provider_resolver_spec.rb +2 -2
  34. data/spec/unit/recipe_spec.rb +1 -0
  35. data/spec/unit/resource/apt_package_spec.rb +3 -5
  36. data/spec/unit/resource/resource_notification_spec.rb +170 -0
  37. data/spec/unit/resource_spec.rb +0 -151
  38. data/spec/unit/run_context_spec.rb +94 -55
  39. metadata +5 -2
@@ -44,7 +44,7 @@ class Chef
44
44
  is_chef_resource!(resource)
45
45
  resource_type ||= resource.resource_name
46
46
  instance_name ||= resource.name
47
- key = ResourceSet.create_key(resource_type, instance_name)
47
+ key = create_key(resource_type, instance_name)
48
48
  @resources_by_key[key] = resource
49
49
  end
50
50
 
@@ -53,7 +53,7 @@ class Chef
53
53
  when key.kind_of?(String)
54
54
  lookup_by = key
55
55
  when key.kind_of?(Chef::Resource)
56
- lookup_by = ResourceSet.create_key(key.resource_name, key.name)
56
+ lookup_by = create_key(key.resource_name, key.name)
57
57
  else
58
58
  raise ArgumentError, "Must pass a Chef::Resource or String to lookup"
59
59
  end
@@ -128,18 +128,18 @@ class Chef
128
128
  end
129
129
  end
130
130
 
131
- def self.create_key(resource_type, instance_name)
131
+ private
132
+
133
+ def create_key(resource_type, instance_name)
132
134
  "#{resource_type}[#{instance_name}]"
133
135
  end
134
136
 
135
- private
136
-
137
137
  def find_resource_by_hash(arg)
138
138
  results = Array.new
139
139
  arg.each do |resource_type, name_list|
140
140
  instance_names = name_list.kind_of?(Array) ? name_list : [ name_list ]
141
141
  instance_names.each do |instance_name|
142
- results << lookup(ResourceSet.create_key(resource_type, instance_name))
142
+ results << lookup(create_key(resource_type, instance_name))
143
143
  end
144
144
  end
145
145
  return results
@@ -153,12 +153,12 @@ class Chef
153
153
  arg =~ /^.+\[(.+)\]$/
154
154
  resource_list = $1
155
155
  resource_list.split(",").each do |instance_name|
156
- results << lookup(ResourceSet.create_key(resource_type, instance_name))
156
+ results << lookup(create_key(resource_type, instance_name))
157
157
  end
158
158
  when SINGLE_RESOURCE_MATCH
159
159
  resource_type = $1
160
160
  name = $2
161
- results << lookup(ResourceSet.create_key(resource_type, name))
161
+ results << lookup(create_key(resource_type, name))
162
162
  else
163
163
  raise ArgumentError, "Bad string format #{arg}, you must have a string like resource_type[name]!"
164
164
  end
@@ -100,7 +100,7 @@ class Chef
100
100
  if nr.instance_of?(Chef::Resource)
101
101
  @immediate_notification_collection[nr.name] << notification
102
102
  else
103
- @immediate_notification_collection[nr.to_s] << notification
103
+ @immediate_notification_collection[nr.declared_key] << notification
104
104
  end
105
105
  end
106
106
 
@@ -111,7 +111,7 @@ class Chef
111
111
  if nr.instance_of?(Chef::Resource)
112
112
  @delayed_notification_collection[nr.name] << notification
113
113
  else
114
- @delayed_notification_collection[nr.to_s] << notification
114
+ @delayed_notification_collection[nr.declared_key] << notification
115
115
  end
116
116
  end
117
117
 
@@ -119,7 +119,7 @@ class Chef
119
119
  if resource.instance_of?(Chef::Resource)
120
120
  return @immediate_notification_collection[resource.name]
121
121
  else
122
- return @immediate_notification_collection[resource.to_s]
122
+ return @immediate_notification_collection[resource.declared_key]
123
123
  end
124
124
  end
125
125
 
@@ -127,7 +127,7 @@ class Chef
127
127
  if resource.instance_of?(Chef::Resource)
128
128
  return @delayed_notification_collection[resource.name]
129
129
  else
130
- return @delayed_notification_collection[resource.to_s]
130
+ return @delayed_notification_collection[resource.declared_key]
131
131
  end
132
132
  end
133
133
 
@@ -17,7 +17,7 @@
17
17
 
18
18
  class Chef
19
19
  CHEF_ROOT = File.dirname(File.expand_path(File.dirname(__FILE__)))
20
- VERSION = '12.0.1'
20
+ VERSION = '12.0.3'
21
21
  end
22
22
 
23
23
  #
@@ -57,7 +57,9 @@ class Chef
57
57
  all_data = all_data[part]
58
58
  end
59
59
 
60
- unless all_data[parts[-1]]
60
+ # Note: You can't do all_data[parts[-1]] here because the value
61
+ # may be false-y
62
+ unless all_data.key?(parts[-1])
61
63
  Chef::Log.warn("Could not find whitelist attribute #{item}.")
62
64
  return nil
63
65
  end
@@ -457,11 +457,25 @@ BOOL WINAPI DeviceIoControl(
457
457
  # takes the given path pre-pends "\\?\" and
458
458
  # UTF-16LE encodes it. Used to prepare paths
459
459
  # to be passed to the *W vesion of WinAPI File
460
- # functions
460
+ # functions.
461
+ # This function is used by the "Link" resources where we need
462
+ # preserve relative paths because symbolic links can actually
463
+ # point to a relative path (relative to the link itself).
461
464
  def encode_path(path)
465
+ (path_prepender << path.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR)).to_wstring
466
+ end
467
+
468
+ # Expands the path, prepends "\\?\" and UTF-16LE encodes it.
469
+ # This function is used by the "File" resources where we need
470
+ # convert relative paths to fully qualified paths.
471
+ def canonical_encode_path(path)
462
472
  Chef::Util::PathHelper.canonical_path(path).to_wstring
463
473
  end
464
474
 
475
+ def path_prepender
476
+ "\\\\?\\"
477
+ end
478
+
465
479
  # retrieves a file search handle and passes it
466
480
  # to +&block+ along with the find_data. also
467
481
  # ensures the handle is closed on exit of the block
@@ -474,7 +488,7 @@ BOOL WINAPI DeviceIoControl(
474
488
  # broader fix to map all the paths starting with "/" to
475
489
  # SYSTEM_DRIVE on windows.
476
490
  path = ::File.expand_path(path) if path.start_with? "/"
477
- path = encode_path(path)
491
+ path = canonical_encode_path(path)
478
492
  find_data = WIN32_FIND_DATA.new
479
493
  handle = FindFirstFileW(path, find_data)
480
494
  if handle == INVALID_HANDLE_VALUE
@@ -491,7 +505,7 @@ BOOL WINAPI DeviceIoControl(
491
505
  # ensures the handle is closed on exit of the block
492
506
  def file_handle(path, &block)
493
507
  begin
494
- path = encode_path(path)
508
+ path = canonical_encode_path(path)
495
509
  handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
496
510
  nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nil)
497
511
 
@@ -0,0 +1,169 @@
1
+ require 'spec_helper'
2
+ require 'chef/recipe'
3
+
4
+
5
+ # The goal of these tests is to make sure that loading resources from a file creates the necessary notifications.
6
+ # Then once converge has started, both immediate and delayed notifications are called as the resources are converged.
7
+ # We want to do this WITHOUT actually converging any resources - we don't want to take time changing the system,
8
+ # we just want to make sure the run_context, the notification DSL and the converge hooks are working together
9
+ # to perform notifications.
10
+
11
+ # This test is extremely fragile since it mocks MANY different systems at once - any of them changes, this test
12
+ # breaks
13
+ describe "Notifications" do
14
+
15
+ # We always pretend we are on OSx because that has a specific provider (HomebrewProvider) so it
16
+ # tests the translation from Provider => HomebrewProvider
17
+ let(:node) {
18
+ n = Chef::Node.new
19
+ n.override[:os] = "darwin"
20
+ n
21
+ }
22
+ let(:cookbook_collection) { double("Chef::CookbookCollection").as_null_object }
23
+ let(:events) { double("Chef::EventDispatch::Dispatcher").as_null_object }
24
+ let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
25
+ let(:recipe) { Chef::Recipe.new("notif", "test", run_context) }
26
+ let(:runner) { Chef::Runner.new(run_context) }
27
+
28
+ before do
29
+ # By default, every provider will do nothing
30
+ p = Chef::Provider.new(nil, run_context)
31
+ allow_any_instance_of(Chef::Resource).to receive(:provider_for_action).and_return(p)
32
+ allow(p).to receive(:run_action)
33
+ end
34
+
35
+ it "should subscribe from one resource to another" do
36
+ log_resource = recipe.declare_resource(:log, "subscribed-log") do
37
+ message "This is a log message"
38
+ action :nothing
39
+ subscribes :write, "package[vim]", :immediately
40
+ end
41
+
42
+ package_resource = recipe.declare_resource(:package, "vim") do
43
+ action :install
44
+ end
45
+
46
+ expect(log_resource).to receive(:run_action).with(:nothing, nil, nil).and_call_original
47
+
48
+ expect(package_resource).to receive(:run_action).with(:install, nil, nil).and_call_original
49
+ update_action(package_resource)
50
+
51
+ expect(log_resource).to receive(:run_action).with(:write, :immediate, package_resource).and_call_original
52
+
53
+ runner.converge
54
+ end
55
+
56
+ it "should notify from one resource to another immediately" do
57
+ log_resource = recipe.declare_resource(:log, "log") do
58
+ message "This is a log message"
59
+ action :write
60
+ notifies :install, "package[vim]", :immediately
61
+ end
62
+
63
+ package_resource = recipe.declare_resource(:package, "vim") do
64
+ action :nothing
65
+ end
66
+
67
+ expect(log_resource).to receive(:run_action).with(:write, nil, nil).and_call_original
68
+ update_action(log_resource)
69
+
70
+ expect(package_resource).to receive(:run_action).with(:install, :immediate, log_resource).ordered.and_call_original
71
+
72
+ expect(package_resource).to receive(:run_action).with(:nothing, nil, nil).ordered.and_call_original
73
+
74
+ runner.converge
75
+ end
76
+
77
+ it "should notify from one resource to another delayed" do
78
+ log_resource = recipe.declare_resource(:log, "log") do
79
+ message "This is a log message"
80
+ action :write
81
+ notifies :install, "package[vim]", :delayed
82
+ end
83
+
84
+ package_resource = recipe.declare_resource(:package, "vim") do
85
+ action :nothing
86
+ end
87
+
88
+ expect(log_resource).to receive(:run_action).with(:write, nil, nil).and_call_original
89
+ update_action(log_resource)
90
+
91
+ expect(package_resource).to receive(:run_action).with(:nothing, nil, nil).ordered.and_call_original
92
+
93
+ expect(package_resource).to receive(:run_action).with(:install, :delayed, nil).ordered.and_call_original
94
+
95
+ runner.converge
96
+ end
97
+
98
+ describe "when one resource is defined lazily" do
99
+
100
+ it "subscribes to a resource defined in a ruby block" do
101
+ r = recipe
102
+ t = self
103
+ ruby_block = recipe.declare_resource(:ruby_block, "rblock") do
104
+ block do
105
+ log_resource = r.declare_resource(:log, "log") do
106
+ message "This is a log message"
107
+ action :write
108
+ end
109
+ t.expect(log_resource).to t.receive(:run_action).with(:write, nil, nil).and_call_original
110
+ t.update_action(log_resource)
111
+ end
112
+ end
113
+
114
+ package_resource = recipe.declare_resource(:package, "vim") do
115
+ action :nothing
116
+ subscribes :install, "log[log]", :delayed
117
+ end
118
+
119
+ # RubyBlock needs to be able to run for our lazy examples to work - and it alone cannot affect the system
120
+ expect(ruby_block).to receive(:provider_for_action).and_call_original
121
+
122
+ expect(package_resource).to receive(:run_action).with(:nothing, nil, nil).ordered.and_call_original
123
+
124
+ expect(package_resource).to receive(:run_action).with(:install, :delayed, nil).ordered.and_call_original
125
+
126
+ runner.converge
127
+ end
128
+
129
+ it "notifies from inside a ruby_block to a resource defined outside" do
130
+ r = recipe
131
+ t = self
132
+ ruby_block = recipe.declare_resource(:ruby_block, "rblock") do
133
+ block do
134
+ log_resource = r.declare_resource(:log, "log") do
135
+ message "This is a log message"
136
+ action :write
137
+ notifies :install, "package[vim]", :immediately
138
+ end
139
+ t.expect(log_resource).to t.receive(:run_action).with(:write, nil, nil).and_call_original
140
+ t.update_action(log_resource)
141
+ end
142
+ end
143
+
144
+ package_resource = recipe.declare_resource(:package, "vim") do
145
+ action :nothing
146
+ end
147
+
148
+ # RubyBlock needs to be able to run for our lazy examples to work - and it alone cannot affect the system
149
+ expect(ruby_block).to receive(:provider_for_action).and_call_original
150
+
151
+ expect(package_resource).to receive(:run_action).with(:install, :immediate, instance_of(Chef::Resource::Log)).ordered.and_call_original
152
+
153
+ expect(package_resource).to receive(:run_action).with(:nothing, nil, nil).ordered.and_call_original
154
+
155
+ runner.converge
156
+ end
157
+
158
+ end
159
+
160
+ # Mocks having the provider run successfully and update the resource
161
+ def update_action(resource)
162
+ p = Chef::Provider.new(resource, run_context)
163
+ expect(resource).to receive(:provider_for_action).and_return(p)
164
+ expect(p).to receive(:run_action) {
165
+ resource.updated_by_last_action(true)
166
+ }
167
+ end
168
+
169
+ end
@@ -72,8 +72,8 @@ describe Chef::Resource::Link do
72
72
  end
73
73
  end
74
74
 
75
- def paths_eql?(path1, path2)
76
- Chef::Util::PathHelper.paths_eql?(path1, path2)
75
+ def canonicalize(path)
76
+ windows? ? path.gsub('/', '\\') : path
77
77
  end
78
78
 
79
79
  def symlink(a, b)
@@ -179,8 +179,8 @@ describe Chef::Resource::Link do
179
179
  end
180
180
 
181
181
  it 'links to the target file' do
182
- symlink?(target_file).should be_true
183
- paths_eql?(readlink(target_file), to).should be_true
182
+ expect(symlink?(target_file)).to be_true
183
+ expect(readlink(target_file)).to eq(canonicalize(to))
184
184
  end
185
185
  it 'marks the resource updated' do
186
186
  resource.should be_updated
@@ -200,8 +200,8 @@ describe Chef::Resource::Link do
200
200
  end
201
201
 
202
202
  it 'leaves the file linked' do
203
- symlink?(target_file).should be_true
204
- paths_eql?(readlink(target_file), to).should be_true
203
+ expect(symlink?(target_file)).to be_true
204
+ expect(readlink(target_file)).to eq(canonicalize(to))
205
205
  end
206
206
  it 'does not mark the resource updated' do
207
207
  resource.should_not be_updated
@@ -278,8 +278,8 @@ describe Chef::Resource::Link do
278
278
  context 'pointing at the target' do
279
279
  before(:each) do
280
280
  symlink(to, target_file)
281
- symlink?(target_file).should be_true
282
- paths_eql?(readlink(target_file), to).should be_true
281
+ expect(symlink?(target_file)).to be_true
282
+ expect(readlink(target_file)).to eq(canonicalize(to))
283
283
  end
284
284
  include_context 'create symbolic link is noop'
285
285
  include_context 'delete succeeds'
@@ -293,8 +293,8 @@ describe Chef::Resource::Link do
293
293
  @other_target = File.join(test_file_dir, make_tmpname('other_spec'))
294
294
  File.open(@other_target, 'w') { |file| file.write('eek') }
295
295
  symlink(@other_target, target_file)
296
- symlink?(target_file).should be_true
297
- paths_eql?(readlink(target_file), @other_target).should be_true
296
+ expect(symlink?(target_file)).to be_true
297
+ expect(readlink(target_file)).to eq(canonicalize(@other_target))
298
298
  end
299
299
  after(:each) do
300
300
  File.delete(@other_target)
@@ -310,8 +310,8 @@ describe Chef::Resource::Link do
310
310
  before(:each) do
311
311
  nonexistent = File.join(test_file_dir, make_tmpname('nonexistent_spec'))
312
312
  symlink(nonexistent, target_file)
313
- symlink?(target_file).should be_true
314
- paths_eql?(readlink(target_file), nonexistent).should be_true
313
+ expect(symlink?(target_file)).to be_true
314
+ expect(readlink(target_file)).to eq(canonicalize(nonexistent))
315
315
  end
316
316
  include_context 'create symbolic link succeeds'
317
317
  include_context 'delete succeeds'
@@ -392,8 +392,8 @@ describe Chef::Resource::Link do
392
392
  @other_target = File.join(test_file_dir, make_tmpname("other_spec"))
393
393
  File.open(@other_target, "w") { |file| file.write("eek") }
394
394
  symlink(@other_target, to)
395
- symlink?(to).should be_true
396
- paths_eql?(readlink(to), @other_target).should be_true
395
+ expect(symlink?(to)).to be_true
396
+ expect(readlink(to)).to eq(canonicalize(@other_target))
397
397
  end
398
398
  after(:each) do
399
399
  File.delete(@other_target)
@@ -407,8 +407,8 @@ describe Chef::Resource::Link do
407
407
  before(:each) do
408
408
  @other_target = File.join(test_file_dir, make_tmpname("other_spec"))
409
409
  symlink(@other_target, to)
410
- symlink?(to).should be_true
411
- paths_eql?(readlink(to), @other_target).should be_true
410
+ expect(symlink?(to)).to be_true
411
+ expect(readlink(to)).to eq(canonicalize(@other_target))
412
412
  end
413
413
  context 'and the link does not yet exist' do
414
414
  include_context 'create symbolic link succeeds'
@@ -440,8 +440,8 @@ describe Chef::Resource::Link do
440
440
  context 'when the link already exists and points at the target' do
441
441
  before(:each) do
442
442
  symlink(to, target_file)
443
- symlink?(target_file).should be_true
444
- paths_eql?(readlink(target_file), to).should be_true
443
+ expect(symlink?(target_file)).to be_true
444
+ expect(readlink(target_file)).to eq(canonicalize(to))
445
445
  end
446
446
  include_context 'create symbolic link is noop'
447
447
  include_context 'delete succeeds'
@@ -449,8 +449,8 @@ describe Chef::Resource::Link do
449
449
  context 'when the link already exists and points at the target with an absolute path' do
450
450
  before(:each) do
451
451
  symlink(absolute_to, target_file)
452
- symlink?(target_file).should be_true
453
- paths_eql?(readlink(target_file), absolute_to).should be_true
452
+ expect(symlink?(target_file)).to be_true
453
+ expect(readlink(target_file)).to eq(canonicalize(absolute_to))
454
454
  end
455
455
  include_context 'create symbolic link succeeds'
456
456
  include_context 'delete succeeds'
@@ -477,8 +477,8 @@ describe Chef::Resource::Link do
477
477
  context "and the link already exists and is a symbolic link pointing at the same file" do
478
478
  before(:each) do
479
479
  symlink(to, target_file)
480
- symlink?(target_file).should be_true
481
- paths_eql?(readlink(target_file), to).should be_true
480
+ expect(symlink?(target_file)).to be_true
481
+ expect(readlink(target_file)).to eq(canonicalize(to))
482
482
  end
483
483
  include_context 'create hard link succeeds'
484
484
  it_behaves_like 'delete errors out'
@@ -551,8 +551,8 @@ describe Chef::Resource::Link do
551
551
  @other_target = File.join(test_file_dir, make_tmpname("other_spec"))
552
552
  File.open(@other_target, "w") { |file| file.write("eek") }
553
553
  symlink(@other_target, to)
554
- symlink?(to).should be_true
555
- paths_eql?(readlink(to), @other_target).should be_true
554
+ expect(symlink?(to)).to be_true
555
+ expect(readlink(to)).to eq(canonicalize(@other_target))
556
556
  end
557
557
  after(:each) do
558
558
  File.delete(@other_target)
@@ -562,10 +562,9 @@ describe Chef::Resource::Link do
562
562
  resource.run_action(:create)
563
563
  File.exists?(target_file).should be_true
564
564
  # OS X gets angry about this sort of link. Bug in OS X, IMO.
565
- pending('OS X/FreeBSD/AIX symlink? and readlink working on hard links to symlinks', :if => (os_x? or freebsd? or aix?)) do
566
- symlink?(target_file).should be_true
567
- paths_eql?(readlink(target_file), @other_target).should be_true
568
- end
565
+ pending('OS X/FreeBSD/AIX symlink? and readlink working on hard links to symlinks') if (os_x? or freebsd? or aix?)
566
+ expect(symlink?(target_file)).to be_true
567
+ expect(readlink(target_file)).to eq(canonicalize(@other_target))
569
568
  end
570
569
  include_context 'delete is noop'
571
570
  end
@@ -574,8 +573,8 @@ describe Chef::Resource::Link do
574
573
  before(:each) do
575
574
  @other_target = File.join(test_file_dir, make_tmpname("other_spec"))
576
575
  symlink(@other_target, to)
577
- symlink?(to).should be_true
578
- paths_eql?(readlink(to), @other_target).should be_true
576
+ expect(symlink?(to)).to be_true
577
+ expect(readlink(to)).to eq(canonicalize(@other_target))
579
578
  end
580
579
  context 'and the link does not yet exist' do
581
580
  it 'links to the target file' do
@@ -587,8 +586,8 @@ describe Chef::Resource::Link do
587
586
  else
588
587
  File.exists?(target_file).should be_false
589
588
  end
590
- symlink?(target_file).should be_true
591
- paths_eql?(readlink(target_file), @other_target).should be_true
589
+ expect(symlink?(target_file)).to be_true
590
+ expect(readlink(target_file)).to eq(canonicalize(@other_target))
592
591
  end
593
592
  end
594
593
  include_context 'delete is noop'