chef 15.2.20 → 15.3.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/chef.gemspec +3 -2
  4. data/lib/chef/application.rb +1 -1
  5. data/lib/chef/application/base.rb +7 -0
  6. data/lib/chef/application/client.rb +6 -2
  7. data/lib/chef/application/solo.rb +7 -1
  8. data/lib/chef/cookbook/gem_installer.rb +7 -2
  9. data/lib/chef/exceptions.rb +12 -0
  10. data/lib/chef/knife/bootstrap.rb +8 -1
  11. data/lib/chef/knife/bootstrap/templates/chef-full.erb +1 -1
  12. data/lib/chef/knife/bootstrap/train_connector.rb +3 -3
  13. data/lib/chef/knife/cookbook_metadata_from_file.rb +1 -1
  14. data/lib/chef/node.rb +0 -2
  15. data/lib/chef/policy_builder/expand_node_object.rb +1 -1
  16. data/lib/chef/policy_builder/policyfile.rb +4 -3
  17. data/lib/chef/provider.rb +4 -2
  18. data/lib/chef/provider/ifconfig.rb +5 -3
  19. data/lib/chef/provider/package/chocolatey.rb +12 -22
  20. data/lib/chef/provider/user.rb +1 -1
  21. data/lib/chef/provider/user/dscl.rb +2 -2
  22. data/lib/chef/provider/user/mac.rb +628 -0
  23. data/lib/chef/providers.rb +1 -0
  24. data/lib/chef/resource.rb +28 -20
  25. data/lib/chef/resource/chocolatey_feature.rb +1 -1
  26. data/lib/chef/resource/chocolatey_package.rb +2 -2
  27. data/lib/chef/resource/cron_d.rb +1 -1
  28. data/lib/chef/resource/ohai.rb +1 -1
  29. data/lib/chef/resource/resource_notification.rb +17 -13
  30. data/lib/chef/resource/ruby_block.rb +1 -1
  31. data/lib/chef/resource/service.rb +1 -1
  32. data/lib/chef/resource/user.rb +1 -0
  33. data/lib/chef/resource/user/dscl_user.rb +1 -1
  34. data/lib/chef/resource/user/mac_user.rb +119 -0
  35. data/lib/chef/resource/windows_ad_join.rb +1 -1
  36. data/lib/chef/resource_collection.rb +6 -0
  37. data/lib/chef/resources.rb +1 -0
  38. data/lib/chef/run_context.rb +61 -27
  39. data/lib/chef/runner.rb +50 -12
  40. data/lib/chef/version.rb +1 -1
  41. data/spec/functional/resource/chocolatey_package_spec.rb +19 -1
  42. data/spec/functional/resource/user/mac_user_spec.rb +207 -0
  43. data/spec/integration/client/client_spec.rb +22 -0
  44. data/spec/integration/knife/raw_spec.rb +39 -19
  45. data/spec/integration/knife/redirection_spec.rb +22 -13
  46. data/spec/integration/knife/serve_spec.rb +1 -2
  47. data/spec/integration/recipes/unified_mode_spec.rb +876 -0
  48. data/spec/spec_helper.rb +1 -0
  49. data/spec/support/platform_helpers.rb +10 -0
  50. data/spec/support/shared/integration/integration_helper.rb +1 -2
  51. data/spec/unit/application/client_spec.rb +5 -6
  52. data/spec/unit/application/solo_spec.rb +3 -8
  53. data/spec/unit/application_spec.rb +1 -1
  54. data/spec/unit/cookbook/gem_installer_spec.rb +22 -1
  55. data/spec/unit/knife/bootstrap/train_connector_spec.rb +20 -7
  56. data/spec/unit/knife/bootstrap_spec.rb +13 -5
  57. data/spec/unit/provider/ifconfig_spec.rb +11 -0
  58. data/spec/unit/provider/package/chocolatey_spec.rb +34 -30
  59. data/spec/unit/provider/user/dscl_spec.rb +1 -0
  60. data/spec/unit/provider/user/mac_spec.rb +38 -0
  61. data/spec/unit/provider/user_spec.rb +38 -22
  62. data/tasks/docs.rb +14 -10
  63. metadata +25 -41
  64. data/lib/.DS_Store +0 -0
  65. data/lib/chef/.DS_Store +0 -0
  66. data/lib/chef/dsl/.DS_Store +0 -0
  67. data/lib/chef/knife/.DS_Store +0 -0
  68. data/lib/chef/mixin/.DS_Store +0 -0
  69. data/lib/chef/provider/.DS_Store +0 -0
  70. data/lib/chef/provider/package/.DS_Store +0 -0
  71. data/lib/chef/provider/package/freebsd/.DS_Store +0 -0
  72. data/lib/chef/resource/.DS_Store +0 -0
  73. data/lib/chef/resource/file/.DS_Store +0 -0
  74. data/spec/.DS_Store +0 -0
  75. data/spec/data/.DS_Store +0 -0
  76. data/spec/data/cookbooks/.DS_Store +0 -0
  77. data/spec/data/cookbooks/java/.DS_Store +0 -0
  78. data/spec/data/cookbooks/java/files/.DS_Store +0 -0
  79. data/spec/data/mac_users/.DS_Store +0 -0
  80. data/spec/data/nodes/Tim.local.json +0 -3
  81. data/spec/data/nodes/Tims-MBP.local.json +0 -3
  82. data/spec/data/run_context/.DS_Store +0 -0
  83. data/spec/data/run_context/cookbooks/.DS_Store +0 -0
  84. data/spec/functional/.DS_Store +0 -0
  85. data/spec/functional/resource/.DS_Store +0 -0
  86. data/spec/functional/util/.DS_Store +0 -0
  87. data/spec/integration/.DS_Store +0 -0
  88. data/spec/stress/.DS_Store +0 -0
  89. data/spec/support/.DS_Store +0 -0
  90. data/spec/support/shared/integration/app_server_support.rb +0 -39
  91. data/spec/unit/.DS_Store +0 -0
  92. data/tasks/.DS_Store +0 -0
@@ -0,0 +1,207 @@
1
+ #
2
+ # Copyright:: Copyright 2019, Chef Software, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require "spec_helper"
19
+ require "chef/mixin/shell_out"
20
+
21
+ metadata = {
22
+ macos_1014: true,
23
+ requires_root: true,
24
+ }
25
+
26
+ describe "Chef::Resource::User with Chef::Provider::User::MacUser provider", metadata do
27
+ include Chef::Mixin::ShellOut
28
+
29
+ def clean_user
30
+ shell_out!("/usr/bin/dscl . -delete '/Users/#{username}'")
31
+ rescue Mixlib::ShellOut::ShellCommandFailed
32
+ # Raised when the user is already cleaned
33
+ end
34
+
35
+ def ensure_file_cache_path_exists
36
+ path = Chef::Config["file_cache_path"]
37
+ FileUtils.mkdir_p(path) unless File.directory?(path)
38
+ end
39
+
40
+ def user_should_exist
41
+ expect(shell_out("/usr/bin/dscl . -read /Users/#{username}").error?).to be(false)
42
+ end
43
+
44
+ def check_password(pass)
45
+ # In order to test the password we use dscl passwd command since
46
+ # that's the only command that gets the user password from CLI.
47
+ expect(shell_out("dscl . -passwd /Users/greatchef #{pass} new_password").exitstatus).to eq(0)
48
+ # Now reset the password back
49
+ expect(shell_out("dscl . -passwd /Users/greatchef new_password #{pass}").exitstatus).to eq(0)
50
+ end
51
+
52
+ let(:node) do
53
+ n = Chef::Node.new
54
+ n.consume_external_attrs(OHAI_SYSTEM.data.dup, {})
55
+ n
56
+ end
57
+
58
+ let(:events) do
59
+ Chef::EventDispatch::Dispatcher.new
60
+ end
61
+
62
+ let(:run_context) do
63
+ Chef::RunContext.new(node, {}, events)
64
+ end
65
+
66
+ let(:username) do
67
+ "greatchef"
68
+ end
69
+
70
+ let(:uid) { nil }
71
+ let(:gid) { 20 }
72
+ let(:home) { nil }
73
+ let(:manage_home) { false }
74
+ let(:password) { "XXXYYYZZZ" }
75
+ let(:comment) { "Great Chef" }
76
+ let(:shell) { "/bin/bash" }
77
+ let(:salt) { nil }
78
+ let(:iterations) { nil }
79
+
80
+ let(:user_resource) do
81
+ r = Chef::Resource::User::MacUser.new("TEST USER RESOURCE", run_context)
82
+ r.username(username)
83
+ r.uid(uid)
84
+ r.gid(gid)
85
+ r.home(home)
86
+ r.shell(shell)
87
+ r.comment(comment)
88
+ r.manage_home(manage_home)
89
+ r.password(password)
90
+ r.salt(salt)
91
+ r.iterations(iterations)
92
+ r
93
+ end
94
+
95
+ before do
96
+ clean_user
97
+ ensure_file_cache_path_exists
98
+ end
99
+
100
+ after(:each) do
101
+ clean_user
102
+ end
103
+
104
+ describe "action :create" do
105
+ it "should create the user" do
106
+ user_resource.run_action(:create)
107
+ user_should_exist
108
+ check_password(password)
109
+ end
110
+ end
111
+
112
+ describe "when user exists" do
113
+ before do
114
+ existing_resource = user_resource.dup
115
+ existing_resource.run_action(:create)
116
+ user_should_exist
117
+ end
118
+
119
+ describe "when password is updated" do
120
+ it "should update the password of the user" do
121
+ user_resource.password("mykitchen")
122
+ user_resource.run_action(:create)
123
+ check_password("mykitchen")
124
+ end
125
+ end
126
+ end
127
+
128
+ describe "when password is being set via shadow hash" do
129
+ let(:password) do
130
+ "c734b6e4787c3727bb35e29fdd92b97c\
131
+ 1de12df509577a045728255ec7c6c5f5\
132
+ c18efa05ed02b682ffa7ebc05119900e\
133
+ b1d4880833aa7a190afc13e2bf0936b8\
134
+ 20123e8c98f0f9bcac2a629d9163caac\
135
+ 9464a8c234f3919082400b4f939bb77b\
136
+ c5adbbac718b7eb99463a7b679571e0f\
137
+ 1c9fef2ef08d0b9e9c2bcf644eed2ffc"
138
+ end
139
+
140
+ let(:iterations) { 25000 }
141
+ let(:salt) { "9e2e7d5ee473b496fd24cf0bbfcaedfcb291ee21740e570d1e917e874f8788ca" }
142
+
143
+ it "action :create should create the user" do
144
+ user_resource.run_action(:create)
145
+ user_should_exist
146
+ check_password("soawesome")
147
+ end
148
+
149
+ describe "when user exists" do
150
+ before do
151
+ existing_resource = user_resource.dup
152
+ existing_resource.run_action(:create)
153
+ user_should_exist
154
+ end
155
+
156
+ describe "when password is updated" do
157
+ describe "without salt" do
158
+ let(:salt) { nil }
159
+
160
+ it "it sets the password" do
161
+ user_resource.password("mykitchen")
162
+ user_resource.run_action(:create)
163
+ check_password("mykitchen")
164
+ end
165
+ end
166
+
167
+ describe "with salt and plaintext password" do
168
+ it "raises Chef::Exceptions::User" do
169
+ expect do
170
+ user_resource.password("notasha512")
171
+ user_resource.run_action(:create)
172
+ end.to raise_error(Chef::Exceptions::User)
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ describe "when a user is member of some groups" do
180
+ let(:groups) { %w{staff operator} }
181
+
182
+ before do
183
+ existing_resource = user_resource.dup
184
+ existing_resource.run_action(:create)
185
+
186
+ groups.each do |group|
187
+ shell_out!("/usr/bin/dscl . -append '/Groups/#{group}' GroupMembership #{username}")
188
+ end
189
+ end
190
+
191
+ after do
192
+ groups.each do |group|
193
+ # Do not raise an error when user is correctly removed
194
+ shell_out("/usr/bin/dscl . -delete '/Groups/#{group}' GroupMembership #{username}")
195
+ end
196
+ end
197
+
198
+ it ":remove action removes the user from the groups and deletes the user" do
199
+ user_resource.run_action(:remove)
200
+ groups.each do |group|
201
+ # Do not raise an error when group is empty
202
+ expect(shell_out("dscl . read /Groups/staff GroupMembership").stdout).not_to include(group)
203
+ end
204
+ end
205
+ end
206
+
207
+ end
@@ -369,6 +369,28 @@ describe "chef-client" do
369
369
  end
370
370
  end
371
371
 
372
+ when_the_repository "has a cookbook that outputs some node attributes" do
373
+ before do
374
+ file "cookbooks/x/recipes/default.rb", <<~'EOM'
375
+ puts "COOKBOOKS: #{node[:cookbooks]}"
376
+ EOM
377
+ file "cookbooks/x/metadata.rb", <<~EOM
378
+ name 'x'
379
+ version '0.0.1'
380
+ EOM
381
+ file "config/client.rb", <<~EOM
382
+ local_mode true
383
+ cookbook_path "#{path_to("cookbooks")}"
384
+ EOM
385
+ end
386
+
387
+ it "should have a cookbook attribute" do
388
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default' --no-fork", cwd: chef_dir)
389
+ result.error!
390
+ expect(result.stdout).to include('COOKBOOKS: {"x"=>{"version"=>"0.0.1"}}')
391
+ end
392
+ end
393
+
372
394
  when_the_repository "has a cookbook that should fail chef_version checks" do
373
395
  before do
374
396
  file "cookbooks/x/recipes/default.rb", ""
@@ -19,11 +19,11 @@ require "support/shared/integration/integration_helper"
19
19
  require "support/shared/context/config"
20
20
  require "chef/knife/raw"
21
21
  require "chef/knife/show"
22
+ require "tiny_server"
22
23
 
23
24
  describe "knife raw", :workstation do
24
25
  include IntegrationSupport
25
26
  include KnifeSupport
26
- include AppServerSupport
27
27
 
28
28
  include_context "default config options"
29
29
 
@@ -185,19 +185,29 @@ describe "knife raw", :workstation do
185
185
  end
186
186
 
187
187
  context "When a server returns raw json" do
188
- before :each do
189
- Chef::Config.chef_server_url = "http://localhost:9018"
190
- app = lambda do |env|
191
- [200, { "Content-Type" => "application/json" }, ['{ "x": "y", "a": "b" }'] ]
188
+ def start_tiny_server(server_opts = {})
189
+ @server = TinyServer::Manager.new(server_opts)
190
+ @server.start
191
+ @api = TinyServer::API.instance
192
+ @api.clear
193
+
194
+ @api.get("/blah", 200, nil, { "Content-Type" => "application/json" }) do
195
+ '{ "x": "y", "a": "b" }'
192
196
  end
193
- @raw_server_thread = start_app_server(app, 9018)
197
+ end
198
+
199
+ def stop_tiny_server
200
+ @server.stop
201
+ @server = @api = nil
202
+ end
203
+
204
+ before :each do
205
+ Chef::Config.chef_server_url = "http://localhost:9000"
206
+ start_tiny_server
194
207
  end
195
208
 
196
209
  after :each do
197
- if @raw_server_thread
198
- @raw_server_thread.kill
199
- @raw_server_thread.join(30)
200
- end
210
+ stop_tiny_server
201
211
  end
202
212
 
203
213
  it "knife raw /blah returns the prettified json" do
@@ -217,19 +227,29 @@ describe "knife raw", :workstation do
217
227
  end
218
228
 
219
229
  context "When a server returns text" do
220
- before :each do
221
- Chef::Config.chef_server_url = "http://localhost:9018"
222
- app = lambda do |env|
223
- [200, { "Content-Type" => "text" }, ['{ "x": "y", "a": "b" }'] ]
230
+ def start_tiny_server(server_opts = {})
231
+ @server = TinyServer::Manager.new(server_opts)
232
+ @server.start
233
+ @api = TinyServer::API.instance
234
+ @api.clear
235
+
236
+ @api.get("/blah", 200, nil, { "Content-Type" => "text" }) do
237
+ '{ "x": "y", "a": "b" }'
224
238
  end
225
- @raw_server_thread = start_app_server(app, 9018)
239
+ end
240
+
241
+ def stop_tiny_server
242
+ @server.stop
243
+ @server = @api = nil
244
+ end
245
+
246
+ before :each do
247
+ Chef::Config.chef_server_url = "http://localhost:9000"
248
+ start_tiny_server
226
249
  end
227
250
 
228
251
  after :each do
229
- if @raw_server_thread
230
- @raw_server_thread.kill
231
- @raw_server_thread.join(30)
232
- end
252
+ stop_tiny_server
233
253
  end
234
254
 
235
255
  it "knife raw /blah returns the raw text" do
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: John Keiser (<jkeiser@chef.io>)
3
- # Copyright:: Copyright 2013-2018, Chef Software Inc.
3
+ # Copyright:: Copyright 2013-2019, Chef Software Inc.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
+ require "tiny_server"
18
19
  require "support/shared/integration/integration_helper"
19
20
  require "support/shared/context/config"
20
21
  require "chef/knife/list"
@@ -22,7 +23,21 @@ require "chef/knife/list"
22
23
  describe "redirection", :workstation do
23
24
  include IntegrationSupport
24
25
  include KnifeSupport
25
- include AppServerSupport
26
+
27
+ def start_tiny_server(real_chef_server_url, server_opts = {})
28
+ @server = TinyServer::Manager.new(server_opts)
29
+ @server.start
30
+ @api = TinyServer::API.instance
31
+ @api.clear
32
+
33
+ @api.get("/roles", 302, nil, { "Content-Type" => "text", "Location" => "#{real_chef_server_url}/roles" }) do
34
+ end
35
+ end
36
+
37
+ def stop_tiny_server
38
+ @server.stop
39
+ @server = @api = nil
40
+ end
26
41
 
27
42
  include_context "default config options"
28
43
 
@@ -30,20 +45,14 @@ describe "redirection", :workstation do
30
45
  before { role "x", {} }
31
46
 
32
47
  context "and another server redirects to it with 302" do
33
- before :each do
48
+ before(:each) do
34
49
  real_chef_server_url = Chef::Config.chef_server_url
35
- Chef::Config.chef_server_url = "http://localhost:9018"
36
- app = lambda do |env|
37
- [302, { "Content-Type" => "text", "Location" => "#{real_chef_server_url}#{env["PATH_INFO"]}" }, ["302 found"] ]
38
- end
39
- @redirector_server_thread = start_app_server(app, 9018)
50
+ Chef::Config.chef_server_url = "http://localhost:9000"
51
+ start_tiny_server(real_chef_server_url)
40
52
  end
41
53
 
42
- after :each do
43
- if @redirector_thread
44
- @redirector_thread.kill
45
- @redirector_thread.join(30)
46
- end
54
+ after(:each) do
55
+ stop_tiny_server
47
56
  end
48
57
 
49
58
  it "knife list /roles returns the role" do
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: John Keiser (<jkeiser@chef.io>)
3
- # Copyright:: Copyright 2013-2016, Chef Software Inc.
3
+ # Copyright:: Copyright 2013-2019, Chef Software Inc.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,6 @@ require "chef/server_api"
22
22
  describe "knife serve", :workstation do
23
23
  include IntegrationSupport
24
24
  include KnifeSupport
25
- include AppServerSupport
26
25
 
27
26
  def with_knife_serve
28
27
  exception = nil
@@ -0,0 +1,876 @@
1
+ require "support/shared/integration/integration_helper"
2
+ require "chef/mixin/shell_out"
3
+
4
+ describe "Unified Mode" do
5
+ include IntegrationSupport
6
+ include Chef::Mixin::ShellOut
7
+
8
+ let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) }
9
+
10
+ let(:chef_client) { "bundle exec chef-client --minimal-ohai" }
11
+
12
+ when_the_repository "has a cookbook with a unified_mode resource with a delayed notification from the second block to the first block" do
13
+ before do
14
+ directory "cookbooks/x" do
15
+
16
+ file "resources/unified_mode.rb", <<-EOM
17
+ unified_mode true
18
+ resource_name :unified_mode
19
+ provides :unified_mode
20
+
21
+ action :doit do
22
+ klass = new_resource.class
23
+ var = "foo"
24
+ ruby_block "first block" do
25
+ block do
26
+ puts "\nfirst: \#\{var\}"
27
+ end
28
+ action :nothing
29
+ end
30
+ var = "bar"
31
+ ruby_block "second block" do
32
+ block do
33
+ puts "\nsecond: \#\{var\}"
34
+ end
35
+ notifies :run, "ruby_block[first block]", :delayed
36
+ end
37
+ var = "baz"
38
+ end
39
+ EOM
40
+
41
+ file "recipes/default.rb", <<-EOM
42
+ unified_mode "whatever"
43
+ EOM
44
+
45
+ end # directory 'cookbooks/x'
46
+ end
47
+
48
+ it "should complete with success" do
49
+ file "config/client.rb", <<~EOM
50
+ local_mode true
51
+ cookbook_path "#{path_to("cookbooks")}"
52
+ log_level :warn
53
+ EOM
54
+
55
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
56
+ # the "second block" runs first after "bar" is set
57
+ expect(result.stdout).to include("second: bar")
58
+ # then the "first block" runs after "baz" in the delayed phase
59
+ expect(result.stdout).to include("first: baz")
60
+ # nothing else should fire
61
+ expect(result.stdout).not_to include("first: foo")
62
+ expect(result.stdout).not_to include("first: bar")
63
+ expect(result.stdout).not_to include("second: foo")
64
+ expect(result.stdout).not_to include("second: baz")
65
+ result.error!
66
+ end
67
+ end
68
+
69
+ when_the_repository "has a cookbook with a unified_mode resource with a delayed notification from the first block to the second block" do
70
+ before do
71
+ directory "cookbooks/x" do
72
+
73
+ file "resources/unified_mode.rb", <<-EOM
74
+ unified_mode true
75
+ resource_name :unified_mode
76
+ provides :unified_mode
77
+
78
+ action :doit do
79
+ klass = new_resource.class
80
+ var = "foo"
81
+ ruby_block "first block" do
82
+ block do
83
+ puts "\nfirst: \#\{var\}"
84
+ end
85
+ notifies :run, "ruby_block[second block]", :delayed
86
+ end
87
+ var = "bar"
88
+ ruby_block "second block" do
89
+ block do
90
+ puts "\nsecond: \#\{var\}"
91
+ end
92
+ action :nothing
93
+ end
94
+ var = "baz"
95
+ end
96
+ EOM
97
+
98
+ file "recipes/default.rb", <<-EOM
99
+ unified_mode "whatever"
100
+ EOM
101
+
102
+ end # directory 'cookbooks/x'
103
+ end
104
+
105
+ it "should complete with success" do
106
+ file "config/client.rb", <<~EOM
107
+ local_mode true
108
+ cookbook_path "#{path_to("cookbooks")}"
109
+ log_level :warn
110
+ EOM
111
+
112
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default' -l debug", cwd: chef_dir)
113
+ # the first block should fire first
114
+ expect(result.stdout).to include("first: foo")
115
+ # the second block should fire in delayed phase
116
+ expect(result.stdout).to include("second: baz")
117
+ # nothing else should fire
118
+ expect(result.stdout).not_to include("first: bar")
119
+ expect(result.stdout).not_to include("first: baz")
120
+ expect(result.stdout).not_to include("second: foo")
121
+ expect(result.stdout).not_to include("second: bar")
122
+ result.error!
123
+ end
124
+ end
125
+
126
+ when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the second block to the first block" do
127
+ before do
128
+ directory "cookbooks/x" do
129
+
130
+ file "resources/unified_mode.rb", <<-EOM
131
+ unified_mode true
132
+ resource_name :unified_mode
133
+ provides :unified_mode
134
+ action :doit do
135
+ klass = new_resource.class
136
+ var = "foo"
137
+ ruby_block "first block" do
138
+ block do
139
+ puts "\nfirst: \#\{var\}"
140
+ end
141
+ action :nothing
142
+ end
143
+ var = "bar"
144
+ ruby_block "second block" do
145
+ block do
146
+ puts "\nsecond: \#\{var\}"
147
+ end
148
+ notifies :run, "ruby_block[first block]", :immediate
149
+ end
150
+ var = "baz"
151
+ end
152
+ EOM
153
+
154
+ file "recipes/default.rb", <<-EOM
155
+ unified_mode "whatever"
156
+ EOM
157
+
158
+ end # directory 'cookbooks/x'
159
+ end
160
+
161
+ it "should complete with success" do
162
+ file "config/client.rb", <<~EOM
163
+ local_mode true
164
+ cookbook_path "#{path_to("cookbooks")}"
165
+ log_level :warn
166
+ EOM
167
+
168
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
169
+ # the second resource should fire first when it is parsed
170
+ expect(result.stdout).to include("second: bar")
171
+ # the first resource should then immediately fire
172
+ expect(result.stdout).to include("first: bar")
173
+ # no other resources should fire
174
+ expect(result.stdout).not_to include("second: baz")
175
+ expect(result.stdout).not_to include("second: foo")
176
+ expect(result.stdout).not_to include("first: foo")
177
+ expect(result.stdout).not_to include("first: baz")
178
+ result.error!
179
+ end
180
+ end
181
+
182
+ when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the first block to the second block" do
183
+ before do
184
+ directory "cookbooks/x" do
185
+
186
+ file "resources/unified_mode.rb", <<-EOM
187
+ unified_mode true
188
+ resource_name :unified_mode
189
+ provides :unified_mode
190
+ action :doit do
191
+ klass = new_resource.class
192
+ var = "foo"
193
+ ruby_block "first block" do
194
+ block do
195
+ puts "\nfirst: \#\{var\}"
196
+ end
197
+ notifies :run, "ruby_block[second block]", :immediate
198
+ end
199
+ var = "bar"
200
+ ruby_block "second block" do
201
+ block do
202
+ puts "\nsecond: \#\{var\}"
203
+ end
204
+ action :nothing
205
+ end
206
+ var = "baz"
207
+ end
208
+ EOM
209
+
210
+ file "recipes/default.rb", <<-EOM
211
+ unified_mode "whatever"
212
+ EOM
213
+
214
+ end # directory 'cookbooks/x'
215
+ end
216
+
217
+ it "should complete with success" do
218
+ file "config/client.rb", <<~EOM
219
+ local_mode true
220
+ cookbook_path "#{path_to("cookbooks")}"
221
+ log_level :warn
222
+ EOM
223
+
224
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default' -l debug", cwd: chef_dir)
225
+ # both blocks should run when they're declared
226
+ expect(result.stdout).to include("first: foo")
227
+ expect(result.stdout).to include("second: bar")
228
+ # nothing else should run
229
+ expect(result.stdout).not_to include("first: bar")
230
+ expect(result.stdout).not_to include("first: baz")
231
+ expect(result.stdout).not_to include("second: foo")
232
+ expect(result.stdout).not_to include("second: baz")
233
+ result.error!
234
+ end
235
+ end
236
+
237
+ when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the first block to a block that does not exist" do
238
+ before do
239
+ directory "cookbooks/x" do
240
+
241
+ file "resources/unified_mode.rb", <<-EOM
242
+ unified_mode true
243
+ resource_name :unified_mode
244
+ provides :unified_mode
245
+ action :doit do
246
+ klass = new_resource.class
247
+ var = "foo"
248
+ ruby_block "first block" do
249
+ block do
250
+ puts "\nfirst: \#\{var\}"
251
+ end
252
+ notifies :run, "ruby_block[second block]", :immediate
253
+ end
254
+ var = "bar"
255
+ var = "baz"
256
+ end
257
+ EOM
258
+
259
+ file "recipes/default.rb", <<-EOM
260
+ unified_mode "whatever"
261
+ EOM
262
+
263
+ end # directory 'cookbooks/x'
264
+ end
265
+
266
+ it "should fail the run" do
267
+ file "config/client.rb", <<~EOM
268
+ local_mode true
269
+ cookbook_path "#{path_to("cookbooks")}"
270
+ log_level :warn
271
+ EOM
272
+
273
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
274
+ # both blocks should run when they're declared
275
+ expect(result.stdout).to include("first: foo")
276
+ # nothing else should run
277
+ expect(result.stdout).not_to include("second: bar")
278
+ expect(result.stdout).not_to include("first: bar")
279
+ expect(result.stdout).not_to include("first: baz")
280
+ expect(result.stdout).not_to include("second: foo")
281
+ expect(result.stdout).not_to include("second: baz")
282
+ expect(result.stdout).to include("Chef::Exceptions::ResourceNotFound")
283
+ expect(result.error?).to be true
284
+ end
285
+ end
286
+
287
+ when_the_repository "has a cookbook with a normal resource with an delayed notification with global resource unified mode on" do
288
+ before do
289
+ directory "cookbooks/x" do
290
+
291
+ file "resources/unified_mode.rb", <<-EOM
292
+ resource_name :unified_mode
293
+ provides :unified_mode
294
+
295
+ action :doit do
296
+ klass = new_resource.class
297
+ var = "foo"
298
+ ruby_block "second block" do
299
+ block do
300
+ puts "\nsecond: \#\{var\}"
301
+ end
302
+ action :nothing
303
+ end
304
+ var = "bar"
305
+ ruby_block "first block" do
306
+ block do
307
+ puts "\nfirst: \#\{var\}"
308
+ end
309
+ notifies :run, "ruby_block[second block]", :delayed
310
+ end
311
+ var = "baz"
312
+ end
313
+ EOM
314
+
315
+ file "recipes/default.rb", <<-EOM
316
+ unified_mode "whatever"
317
+ EOM
318
+
319
+ end # directory 'cookbooks/x'
320
+ end
321
+
322
+ it "should complete with success" do
323
+ file "config/client.rb", <<~EOM
324
+ resource_unified_mode_default true
325
+ local_mode true
326
+ cookbook_path "#{path_to("cookbooks")}"
327
+ log_level :warn
328
+ EOM
329
+
330
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
331
+ # the "first block" resource runs before the assignment to baz in compile time
332
+ expect(result.stdout).to include("first: bar")
333
+ # we should not run the "first block" at compile time
334
+ expect(result.stdout).not_to include("first: baz")
335
+ # (and certainly should run it this early)
336
+ expect(result.stdout).not_to include("first: foo")
337
+ # the delayed notification should still fire and run after everything else
338
+ expect(result.stdout).to include("second: baz")
339
+ # the action :nothing should suppress any other running of the second block
340
+ expect(result.stdout).not_to include("second: foo")
341
+ expect(result.stdout).not_to include("second: bar")
342
+ result.error!
343
+ end
344
+ end
345
+
346
+ when_the_repository "has a cookbook with a normal resource with an immediate notification with global resource unified mode on" do
347
+ before do
348
+ directory "cookbooks/x" do
349
+
350
+ file "resources/unified_mode.rb", <<-EOM
351
+ resource_name :unified_mode
352
+ provides :unified_mode
353
+ action :doit do
354
+ klass = new_resource.class
355
+ var = "foo"
356
+ ruby_block "second block" do
357
+ block do
358
+ puts "\nsecond: \#\{var\}"
359
+ end
360
+ action :nothing
361
+ end
362
+ var = "bar"
363
+ ruby_block "first block" do
364
+ block do
365
+ puts "\nfirst: \#\{var\}"
366
+ end
367
+ notifies :run, "ruby_block[second block]", :immediate
368
+ end
369
+ var = "baz"
370
+ end
371
+ EOM
372
+
373
+ file "recipes/default.rb", <<-EOM
374
+ unified_mode "whatever"
375
+ EOM
376
+
377
+ end # directory 'cookbooks/x'
378
+ end
379
+
380
+ it "should complete with success" do
381
+ file "config/client.rb", <<~EOM
382
+ resource_unified_mode_default true
383
+ local_mode true
384
+ cookbook_path "#{path_to("cookbooks")}"
385
+ log_level :warn
386
+ EOM
387
+
388
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
389
+ # the "first block" resource runs before the assignment to baz in compile time
390
+ expect(result.stdout).to include("first: bar")
391
+ # we should not run the "first block" at compile time
392
+ expect(result.stdout).not_to include("first: baz")
393
+ # (and certainly should run it this early)
394
+ expect(result.stdout).not_to include("first: foo")
395
+ # the immediate notifiation fires immediately
396
+ expect(result.stdout).to include("second: bar")
397
+ # the action :nothing should suppress any other running of the second block
398
+ expect(result.stdout).not_to include("second: foo")
399
+ expect(result.stdout).not_to include("second: baz")
400
+ result.error!
401
+ end
402
+ end
403
+
404
+ when_the_repository "has a cookbook with a unified resource with an immediate subscribes from the second resource to the first" do
405
+ before do
406
+ directory "cookbooks/x" do
407
+
408
+ file "resources/unified_mode.rb", <<-EOM
409
+ unified_mode true
410
+ resource_name :unified_mode
411
+ provides :unified_mode
412
+ action :doit do
413
+ klass = new_resource.class
414
+ var = "foo"
415
+ ruby_block "first block" do
416
+ block do
417
+ puts "\nfirst: \#\{var\}"
418
+ end
419
+ end
420
+ var = "bar"
421
+ ruby_block "second block" do
422
+ block do
423
+ puts "\nsecond: \#\{var\}"
424
+ end
425
+ subscribes :run, "ruby_block[first block]", :immediate
426
+ action :nothing
427
+ end
428
+ var = "baz"
429
+ end
430
+ EOM
431
+
432
+ file "recipes/default.rb", <<-EOM
433
+ unified_mode "whatever"
434
+ EOM
435
+
436
+ end # directory 'cookbooks/x'
437
+ end
438
+
439
+ it "should complete with success" do
440
+ file "config/client.rb", <<~EOM
441
+ local_mode true
442
+ cookbook_path "#{path_to("cookbooks")}"
443
+ log_level :warn
444
+ EOM
445
+
446
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
447
+ # the first resource fires
448
+ expect(result.stdout).to include("first: foo")
449
+ # the second resource fires when it is parsed
450
+ expect(result.stdout).to include("second: bar")
451
+ # no other actions should run
452
+ expect(result.stdout).not_to include("first: bar")
453
+ expect(result.stdout).not_to include("first: baz")
454
+ expect(result.stdout).not_to include("second: foo")
455
+ expect(result.stdout).not_to include("second: baz")
456
+ result.error!
457
+ end
458
+ end
459
+
460
+ when_the_repository "has a cookbook with a unified resource with an immediate subscribes from the first resource to the second" do
461
+ before do
462
+ directory "cookbooks/x" do
463
+
464
+ file "resources/unified_mode.rb", <<-EOM
465
+ unified_mode true
466
+ resource_name :unified_mode
467
+ provides :unified_mode
468
+ action :doit do
469
+ klass = new_resource.class
470
+ var = "foo"
471
+ ruby_block "first block" do
472
+ block do
473
+ puts "\nfirst: \#\{var\}"
474
+ end
475
+ subscribes :run, "ruby_block[second block]", :immediate
476
+ action :nothing
477
+ end
478
+ var = "bar"
479
+ ruby_block "second block" do
480
+ block do
481
+ puts "\nsecond: \#\{var\}"
482
+ end
483
+ end
484
+ var = "baz"
485
+ end
486
+ EOM
487
+
488
+ file "recipes/default.rb", <<-EOM
489
+ unified_mode "whatever"
490
+ EOM
491
+
492
+ end # directory 'cookbooks/x'
493
+ end
494
+
495
+ it "should complete with success" do
496
+ file "config/client.rb", <<~EOM
497
+ local_mode true
498
+ cookbook_path "#{path_to("cookbooks")}"
499
+ log_level :warn
500
+ EOM
501
+
502
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
503
+ # the second resource fires first after bar is set
504
+ expect(result.stdout).to include("second: bar")
505
+ # the first resource then has its immediate subscribes fire at that location
506
+ expect(result.stdout).to include("first: bar")
507
+ # no other actions should run
508
+ expect(result.stdout).not_to include("first: baz")
509
+ expect(result.stdout).not_to include("first: foo")
510
+ expect(result.stdout).not_to include("second: foo")
511
+ expect(result.stdout).not_to include("second: baz")
512
+ result.error!
513
+ end
514
+ end
515
+
516
+ when_the_repository "has a cookbook with a unified resource with an delayed subscribes from the second resource to the first" do
517
+ before do
518
+ directory "cookbooks/x" do
519
+
520
+ file "resources/unified_mode.rb", <<-EOM
521
+ unified_mode true
522
+ resource_name :unified_mode
523
+ provides :unified_mode
524
+ action :doit do
525
+ klass = new_resource.class
526
+ var = "foo"
527
+ ruby_block "first block" do
528
+ block do
529
+ puts "\nfirst: \#\{var\}"
530
+ end
531
+ end
532
+ var = "bar"
533
+ ruby_block "second block" do
534
+ block do
535
+ puts "\nsecond: \#\{var\}"
536
+ end
537
+ subscribes :run, "ruby_block[first block]", :delayed
538
+ action :nothing
539
+ end
540
+ var = "baz"
541
+ end
542
+ EOM
543
+
544
+ file "recipes/default.rb", <<-EOM
545
+ unified_mode "whatever"
546
+ EOM
547
+
548
+ end # directory 'cookbooks/x'
549
+ end
550
+
551
+ it "should complete with success" do
552
+ file "config/client.rb", <<~EOM
553
+ local_mode true
554
+ cookbook_path "#{path_to("cookbooks")}"
555
+ log_level :warn
556
+ EOM
557
+
558
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
559
+ # the first resource fires as it is parsed
560
+ expect(result.stdout).to include("first: foo")
561
+ # the second resource then fires in the delayed notifications phase
562
+ expect(result.stdout).to include("second: baz")
563
+ # no other actions should run
564
+ expect(result.stdout).not_to include("first: bar")
565
+ expect(result.stdout).not_to include("first: baz")
566
+ expect(result.stdout).not_to include("second: foo")
567
+ expect(result.stdout).not_to include("second: bar")
568
+ result.error!
569
+ end
570
+ end
571
+
572
+ when_the_repository "has a cookbook with a unified resource with an delayed subscribes from the first resource to the second" do
573
+ before do
574
+ directory "cookbooks/x" do
575
+
576
+ file "resources/unified_mode.rb", <<-EOM
577
+ unified_mode true
578
+ resource_name :unified_mode
579
+ provides :unified_mode
580
+ action :doit do
581
+ klass = new_resource.class
582
+ var = "foo"
583
+ ruby_block "first block" do
584
+ block do
585
+ puts "\nfirst: \#\{var\}"
586
+ end
587
+ subscribes :run, "ruby_block[second block]", :delayed
588
+ action :nothing
589
+ end
590
+ var = "bar"
591
+ ruby_block "second block" do
592
+ block do
593
+ puts "\nsecond: \#\{var\}"
594
+ end
595
+ end
596
+ var = "baz"
597
+ end
598
+ EOM
599
+
600
+ file "recipes/default.rb", <<-EOM
601
+ unified_mode "whatever"
602
+ EOM
603
+
604
+ end # directory 'cookbooks/x'
605
+ end
606
+
607
+ it "should complete with success" do
608
+ file "config/client.rb", <<~EOM
609
+ local_mode true
610
+ cookbook_path "#{path_to("cookbooks")}"
611
+ log_level :warn
612
+ EOM
613
+
614
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
615
+ # the second resource fires first after bar is set
616
+ expect(result.stdout).to include("second: bar")
617
+ # the first resource then fires in the delayed notifications phase
618
+ expect(result.stdout).to include("first: baz")
619
+ # no other actions should run
620
+ expect(result.stdout).not_to include("first: foo")
621
+ expect(result.stdout).not_to include("first: bar")
622
+ expect(result.stdout).not_to include("second: foo")
623
+ expect(result.stdout).not_to include("second: baz")
624
+ result.error!
625
+ end
626
+ end
627
+
628
+ when_the_repository "has a cookbook with a unified resource with a correct before notification" do
629
+ before do
630
+ directory "cookbooks/x" do
631
+
632
+ file "resources/unified_mode.rb", <<-EOM
633
+ unified_mode true
634
+ resource_name :unified_mode
635
+ provides :unified_mode
636
+ action :doit do
637
+ klass = new_resource.class
638
+ var = "foo"
639
+ ruby_block "notified block" do
640
+ block do
641
+ puts "\nnotified: \#\{var\}"
642
+ end
643
+ action :nothing
644
+ end
645
+ var = "bar"
646
+ whyrun_safe_ruby_block "notifying block" do
647
+ block do
648
+ puts "\nnotifying: \#\{var\}"
649
+ end
650
+ notifies :run, "ruby_block[notified block]", :before
651
+ end
652
+ var = "baz"
653
+ end
654
+ EOM
655
+
656
+ file "recipes/default.rb", <<-EOM
657
+ unified_mode "whatever"
658
+ EOM
659
+
660
+ end # directory 'cookbooks/x'
661
+ end
662
+
663
+ it "should complete with success" do
664
+ file "config/client.rb", <<~EOM
665
+ local_mode true
666
+ cookbook_path "#{path_to("cookbooks")}"
667
+ log_level :warn
668
+ EOM
669
+
670
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
671
+ expect(result.stdout.scan(/notifying: bar/).length).to eql(2)
672
+ expect(result.stdout).to include("Would execute the whyrun_safe_ruby_block notifying block")
673
+ expect(result.stdout).to include("notified: bar")
674
+ # no other actions should run
675
+ expect(result.stdout).not_to include("notified: foo")
676
+ expect(result.stdout).not_to include("notified: baz")
677
+ expect(result.stdout).not_to include("notifying: foo")
678
+ expect(result.stdout).not_to include("notifying: baz")
679
+ result.error!
680
+ end
681
+ end
682
+
683
+ when_the_repository "has a cookbook with a unified resource with a correct before subscribes" do
684
+ before do
685
+ directory "cookbooks/x" do
686
+
687
+ file "resources/unified_mode.rb", <<-EOM
688
+ unified_mode true
689
+ resource_name :unified_mode
690
+ provides :unified_mode
691
+ action :doit do
692
+ klass = new_resource.class
693
+ var = "foo"
694
+ ruby_block "notified block" do
695
+ block do
696
+ puts "\nnotified: \#\{var\}"
697
+ end
698
+ subscribes :run, "whyrun_safe_ruby_block[notifying block]", :before
699
+ action :nothing
700
+ end
701
+ var = "bar"
702
+ whyrun_safe_ruby_block "notifying block" do
703
+ block do
704
+ puts "\nnotifying: \#\{var\}"
705
+ end
706
+ end
707
+ var = "baz"
708
+ end
709
+ EOM
710
+
711
+ file "recipes/default.rb", <<-EOM
712
+ unified_mode "whatever"
713
+ EOM
714
+
715
+ end # directory 'cookbooks/x'
716
+ end
717
+
718
+ it "should complete with success" do
719
+ file "config/client.rb", <<~EOM
720
+ local_mode true
721
+ cookbook_path "#{path_to("cookbooks")}"
722
+ log_level :warn
723
+ EOM
724
+
725
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
726
+ expect(result.stdout.scan(/notifying: bar/).length).to eql(2)
727
+ expect(result.stdout).to include("Would execute the whyrun_safe_ruby_block notifying block")
728
+ expect(result.stdout).to include("notified: bar")
729
+ # no other actions should run
730
+ expect(result.stdout).not_to include("notified: foo")
731
+ expect(result.stdout).not_to include("notified: baz")
732
+ expect(result.stdout).not_to include("notifying: foo")
733
+ expect(result.stdout).not_to include("notifying: baz")
734
+ result.error!
735
+ end
736
+ end
737
+
738
+ when_the_repository "has a cookbook with a unified resource with a broken/reversed before notification" do
739
+ before do
740
+ directory "cookbooks/x" do
741
+
742
+ file "resources/unified_mode.rb", <<-EOM
743
+ unified_mode true
744
+ resource_name :unified_mode
745
+ provides :unified_mode
746
+ action :doit do
747
+ klass = new_resource.class
748
+ var = "foo"
749
+ whyrun_safe_ruby_block "notifying block" do
750
+ block do
751
+ puts "\nnotifying: \#\{var\}"
752
+ end
753
+ notifies :run, "ruby_block[notified block]", :before
754
+ end
755
+ var = "bar"
756
+ ruby_block "notified block" do
757
+ block do
758
+ puts "\nnotified: \#\{var\}"
759
+ end
760
+ action :nothing
761
+ end
762
+ var = "baz"
763
+ end
764
+ EOM
765
+
766
+ file "recipes/default.rb", <<-EOM
767
+ unified_mode "whatever"
768
+ EOM
769
+
770
+ end # directory 'cookbooks/x'
771
+ end
772
+
773
+ it "should fail the run" do
774
+ file "config/client.rb", <<~EOM
775
+ local_mode true
776
+ cookbook_path "#{path_to("cookbooks")}"
777
+ log_level :warn
778
+ EOM
779
+
780
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default' -l debug", cwd: chef_dir)
781
+ # this doesn't work and we can't tell the difference between it and if we were trying to do a correct :before notification but typo'd the name
782
+ # so Chef::Exceptions::ResourceNotFound is the best we can do
783
+ expect(result.stdout).to include("Chef::Exceptions::ResourceNotFound")
784
+ expect(result.error?).to be true
785
+ end
786
+ end
787
+
788
+ when_the_repository "has a cookbook with a unified resource with a broken/reversed before subscribes" do
789
+ before do
790
+ directory "cookbooks/x" do
791
+
792
+ file "resources/unified_mode.rb", <<-EOM
793
+ unified_mode true
794
+ resource_name :unified_mode
795
+ provides :unified_mode
796
+ action :doit do
797
+ klass = new_resource.class
798
+ var = "foo"
799
+ whyrun_safe_ruby_block "notifying block" do
800
+ block do
801
+ puts "\nnotifying: \#\{var\}"
802
+ end
803
+ end
804
+ var = "bar"
805
+ ruby_block "notified block" do
806
+ block do
807
+ puts "\nnotified: \#\{var\}"
808
+ end
809
+ subscribes :run, "whyrun_safe_ruby_block[notifying block]", :before
810
+ action :nothing
811
+ end
812
+ var = "baz"
813
+ end
814
+ EOM
815
+
816
+ file "recipes/default.rb", <<-EOM
817
+ unified_mode "whatever"
818
+ EOM
819
+
820
+ end # directory 'cookbooks/x'
821
+ end
822
+
823
+ it "should fail the run" do
824
+ file "config/client.rb", <<~EOM
825
+ local_mode true
826
+ cookbook_path "#{path_to("cookbooks")}"
827
+ log_level :warn
828
+ EOM
829
+
830
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
831
+ # this fires first normally before the error
832
+ expect(result.stdout).to include("notifying: foo")
833
+ # everything else does not run
834
+ expect(result.stdout).not_to include("notified: foo")
835
+ expect(result.stdout).not_to include("notified: bar")
836
+ expect(result.stdout).not_to include("notified: baz")
837
+ expect(result.stdout).not_to include("notifying: bar")
838
+ expect(result.stdout).not_to include("notifying: baz")
839
+ expect(result.stdout).to include("Chef::Exceptions::UnifiedModeBeforeSubscriptionEarlierResource")
840
+ expect(result.error?).to be true
841
+ end
842
+ end
843
+
844
+ when_the_repository "has global resource unified mode on" do
845
+ before do
846
+ directory "cookbooks/x" do
847
+
848
+ file "recipes/default.rb", <<-EOM
849
+ var = "foo"
850
+ ruby_block "first block" do
851
+ block do
852
+ puts "\nfirst: \#\{var\}"
853
+ end
854
+ end
855
+ var = "bar"
856
+ EOM
857
+
858
+ end # directory 'cookbooks/x'
859
+ end
860
+
861
+ it "recipes should still have a compile/converge mode" do
862
+ file "config/client.rb", <<~EOM
863
+ resource_unified_mode_default true
864
+ local_mode true
865
+ cookbook_path "#{path_to("cookbooks")}"
866
+ log_level :warn
867
+ EOM
868
+
869
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
870
+ # in recipe mode we should still run normally with a compile/converge mode
871
+ expect(result.stdout).to include("first: bar")
872
+ expect(result.stdout).not_to include("first: foo")
873
+ result.error!
874
+ end
875
+ end
876
+ end