chef 12.0.1-x86-mingw32 → 12.0.3-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -1,7 +1,10 @@
1
1
  require 'fcntl'
2
2
  require 'chef/mixin/shell_out'
3
3
 
4
- include Chef::Mixin::ShellOut
4
+
5
+ class ShellHelpers
6
+ extend Chef::Mixin::ShellOut
7
+ end
5
8
 
6
9
  def ruby_gte_20?
7
10
  RUBY_VERSION.to_f >= 2.0
@@ -78,7 +81,7 @@ end
78
81
 
79
82
  def mac_osx_106?
80
83
  if File.exists? "/usr/bin/sw_vers"
81
- result = shell_out("/usr/bin/sw_vers")
84
+ result = ShellHelpers.shell_out("/usr/bin/sw_vers")
82
85
  result.stdout.each_line do |line|
83
86
  if line =~ /^ProductVersion:\s10.6.*$/
84
87
  return true
@@ -19,132 +19,173 @@
19
19
  require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
20
20
 
21
21
  describe Chef::Knife::CookbookSiteInstall do
22
+ let(:knife) { Chef::Knife::CookbookSiteInstall.new }
23
+ let(:stdout) { StringIO.new }
24
+ let(:stderr) { StringIO.new }
25
+ let(:downloader) { Hash.new }
26
+ let(:repo) { double(:sanity_check => true, :reset_to_default_state => true,
27
+ :prepare_to_import => true, :finalize_updates_to => true,
28
+ :merge_updates_from => true) }
29
+ let(:install_path) { if Chef::Platform.windows?
30
+ 'C:/tmp/chef'
31
+ else
32
+ '/var/tmp/chef'
33
+ end }
34
+
22
35
  before(:each) do
23
36
  require 'chef/knife/core/cookbook_scm_repo'
24
- @stdout = StringIO.new
25
- @knife = Chef::Knife::CookbookSiteInstall.new
26
- @knife.ui.stub(:stdout).and_return(@stdout)
27
- @knife.config = {}
28
- if Chef::Platform.windows?
29
- @install_path = 'C:/tmp/chef'
30
- else
31
- @install_path = '/var/tmp/chef'
32
- end
33
- @knife.config[:cookbook_path] = [ @install_path ]
34
-
35
- @stdout = StringIO.new
36
- @stderr = StringIO.new
37
- @knife.stub(:stderr).and_return(@stdout)
38
- @knife.stub(:stdout).and_return(@stdout)
39
-
40
- #Assume all external commands would have succeed. :(
41
- File.stub(:unlink)
42
- File.stub(:rmtree)
43
- @knife.stub(:shell_out!).and_return(true)
44
-
45
- #CookbookSiteDownload Stup
46
- @downloader = {}
47
- @knife.stub(:download_cookbook_to).and_return(@downloader)
48
- @downloader.stub(:version).and_return do
49
- if @knife.name_args.size == 2
50
- @knife.name_args[1]
37
+
38
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
39
+ knife.config = {}
40
+ knife.config[:cookbook_path] = [ install_path ]
41
+
42
+ allow(knife).to receive(:stderr).and_return(stderr)
43
+ allow(knife).to receive(:stdout).and_return(stdout)
44
+
45
+ # Assume all external commands would have succeed. :(
46
+ allow(File).to receive(:unlink)
47
+ allow(File).to receive(:rmtree)
48
+ allow(knife).to receive(:shell_out!).and_return(true)
49
+
50
+ # CookbookSiteDownload Stup
51
+ allow(knife).to receive(:download_cookbook_to).and_return(downloader)
52
+ allow(downloader).to receive(:version) do
53
+ if knife.name_args.size == 2
54
+ knife.name_args[1]
51
55
  else
52
56
  "0.3.0"
53
57
  end
54
58
  end
55
59
 
56
- #Stubs for CookbookSCMRepo
57
- @repo = double(:sanity_check => true, :reset_to_default_state => true,
58
- :prepare_to_import => true, :finalize_updates_to => true,
59
- :merge_updates_from => true)
60
- Chef::Knife::CookbookSCMRepo.stub(:new).and_return(@repo)
60
+ # Stubs for CookbookSCMRepo
61
+ allow(Chef::Knife::CookbookSCMRepo).to receive(:new).and_return(repo)
61
62
  end
62
63
 
63
-
64
64
  describe "run" do
65
- it "should return an error if a cookbook name is not provided" do
66
- @knife.name_args = []
67
- @knife.ui.should_receive(:error).with("Please specify a cookbook to download and install.")
68
- lambda { @knife.run }.should raise_error(SystemExit)
69
- end
70
-
71
- it "should return an error if more than two arguments are given" do
72
- @knife.name_args = ["foo", "bar", "baz"]
73
- @knife.ui.should_receive(:error).with("Installing multiple cookbooks at once is not supported.")
74
- lambda { @knife.run }.should raise_error(SystemExit)
75
- end
76
-
77
- it "should return an error if the second argument is not a version" do
78
- @knife.name_args = ["getting-started", "1pass"]
79
- @knife.ui.should_receive(:error).with("Installing multiple cookbooks at once is not supported.")
80
- lambda { @knife.run }.should raise_error(SystemExit)
81
- end
82
-
83
- it "should return an error if the second argument is a four-digit version" do
84
- @knife.name_args = ["getting-started", "0.0.0.1"]
85
- @knife.ui.should_receive(:error).with("Installing multiple cookbooks at once is not supported.")
86
- lambda { @knife.run }.should raise_error(SystemExit)
87
- end
88
-
89
- it "should return an error if the second argument is a one-digit version" do
90
- @knife.name_args = ["getting-started", "1"]
91
- @knife.ui.should_receive(:error).with("Installing multiple cookbooks at once is not supported.")
92
- lambda { @knife.run }.should raise_error(SystemExit)
93
- end
94
-
95
- it "should install the specified version if second argument is a three-digit version" do
96
- @knife.name_args = ["getting-started", "0.1.0"]
97
- @knife.config[:no_deps] = true
98
- upstream_file = File.join(@install_path, "getting-started.tar.gz")
99
- @knife.should_receive(:download_cookbook_to).with(upstream_file)
100
- @knife.should_receive(:extract_cookbook).with(upstream_file, "0.1.0")
101
- @knife.should_receive(:clear_existing_files).with(File.join(@install_path, "getting-started"))
102
- @repo.should_receive(:merge_updates_from).with("getting-started", "0.1.0")
103
- @knife.run
104
- end
105
-
106
- it "should install the specified version if second argument is a two-digit version" do
107
- @knife.name_args = ["getting-started", "0.1"]
108
- @knife.config[:no_deps] = true
109
- upstream_file = File.join(@install_path, "getting-started.tar.gz")
110
- @knife.should_receive(:download_cookbook_to).with(upstream_file)
111
- @knife.should_receive(:extract_cookbook).with(upstream_file, "0.1")
112
- @knife.should_receive(:clear_existing_files).with(File.join(@install_path, "getting-started"))
113
- @repo.should_receive(:merge_updates_from).with("getting-started", "0.1")
114
- @knife.run
115
- end
116
-
117
- it "should install the latest version if only a cookbook name is given" do
118
- @knife.name_args = ["getting-started"]
119
- @knife.config[:no_deps] = true
120
- upstream_file = File.join(@install_path, "getting-started.tar.gz")
121
- @knife.should_receive(:download_cookbook_to).with(upstream_file)
122
- @knife.should_receive(:extract_cookbook).with(upstream_file, "0.3.0")
123
- @knife.should_receive(:clear_existing_files).with(File.join(@install_path, "getting-started"))
124
- @repo.should_receive(:merge_updates_from).with("getting-started", "0.3.0")
125
- @knife.run
126
- end
127
-
128
- it "should not create/reset git branches if use_current_branch is set" do
129
- @knife.name_args = ["getting-started"]
130
- @knife.config[:use_current_branch] = true
131
- @knife.config[:no_deps] = true
132
- upstream_file = File.join(@install_path, "getting-started.tar.gz")
133
- @repo.should_not_receive(:prepare_to_import)
134
- @repo.should_not_receive(:reset_to_default_state)
135
- @knife.run
136
- end
137
-
138
- it "should not raise an error if cookbook_path is a string" do
139
- @knife.config[:cookbook_path] = @install_path
140
- @knife.config[:no_deps] = true
141
- @knife.name_args = ["getting-started"]
142
- upstream_file = File.join(@install_path, "getting-started.tar.gz")
143
- @knife.should_receive(:download_cookbook_to).with(upstream_file)
144
- @knife.should_receive(:extract_cookbook).with(upstream_file, "0.3.0")
145
- @knife.should_receive(:clear_existing_files).with(File.join(@install_path, "getting-started"))
146
- @repo.should_receive(:merge_updates_from).with("getting-started", "0.3.0")
147
- lambda { @knife.run }.should_not raise_error
65
+ it "raises an error if a cookbook name is not provided" do
66
+ knife.name_args = []
67
+ expect(knife.ui).to receive(:error).with("Please specify a cookbook to download and install.")
68
+ expect { knife.run }.to raise_error(SystemExit)
69
+ end
70
+
71
+ it "raises an error if more than two arguments are given" do
72
+ knife.name_args = ["foo", "bar", "baz"]
73
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
74
+ expect { knife.run }.to raise_error(SystemExit)
75
+ end
76
+
77
+ it "raises an error if the second argument is not a version" do
78
+ knife.name_args = ["getting-started", "1pass"]
79
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
80
+ expect { knife.run }.to raise_error(SystemExit)
81
+ end
82
+
83
+ it "raises an error if the second argument is a four-digit version" do
84
+ knife.name_args = ["getting-started", "0.0.0.1"]
85
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
86
+ expect { knife.run }.to raise_error(SystemExit)
87
+ end
88
+
89
+ it "raises an error if the second argument is a one-digit version" do
90
+ knife.name_args = ["getting-started", "1"]
91
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
92
+ expect { knife.run }.to raise_error(SystemExit)
93
+ end
94
+
95
+ it "installs the specified version if second argument is a three-digit version" do
96
+ knife.name_args = ["getting-started", "0.1.0"]
97
+ knife.config[:no_deps] = true
98
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
99
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
100
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.1.0")
101
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
102
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.1.0")
103
+ knife.run
104
+ end
105
+
106
+ it "installs the specified version if second argument is a two-digit version" do
107
+ knife.name_args = ["getting-started", "0.1"]
108
+ knife.config[:no_deps] = true
109
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
110
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
111
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.1")
112
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
113
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.1")
114
+ knife.run
115
+ end
116
+
117
+ it "installs the latest version if only a cookbook name is given" do
118
+ knife.name_args = ["getting-started"]
119
+ knife.config[:no_deps] = true
120
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
121
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
122
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0")
123
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
124
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.3.0")
125
+ knife.run
126
+ end
127
+
128
+ it "does not create/reset git branches if use_current_branch is set" do
129
+ knife.name_args = ["getting-started"]
130
+ knife.config[:use_current_branch] = true
131
+ knife.config[:no_deps] = true
132
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
133
+ expect(repo).not_to receive(:prepare_to_import)
134
+ expect(repo).not_to receive(:reset_to_default_state)
135
+ knife.run
148
136
  end
137
+
138
+ it "does not raise an error if cookbook_path is a string" do
139
+ knife.config[:cookbook_path] = install_path
140
+ knife.config[:no_deps] = true
141
+ knife.name_args = ["getting-started"]
142
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
143
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
144
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0")
145
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
146
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.3.0")
147
+ expect { knife.run }.not_to raise_error
148
+ end
149
+ end # end of run
150
+
151
+ let(:metadata) { Chef::Cookbook::Metadata.new }
152
+ let(:rb_metadata_path) { File.join(install_path, "post-punk-kitchen", "metadata.rb") }
153
+ let(:json_metadata_path) { File.join(install_path, "post-punk-kitchen", "metadata.json") }
154
+
155
+ describe "preferred_metadata" do
156
+ before do
157
+ allow(Chef::Cookbook::Metadata).to receive(:new).and_return(metadata)
158
+ allow(File).to receive(:exist?).and_return(false)
159
+ knife.instance_variable_set(:@cookbook_name, "post-punk-kitchen")
160
+ knife.instance_variable_set(:@install_path, install_path)
161
+ end
162
+
163
+ it "returns a populated Metadata object if metadata.rb exists" do
164
+ allow(File).to receive(:exist?).with(rb_metadata_path).and_return(true)
165
+ expect(metadata).to receive(:from_file).with(rb_metadata_path)
166
+ knife.preferred_metadata
167
+ end
168
+
169
+ it "returns a populated Metadata object if metadata.json exists" do
170
+ allow(File).to receive(:exist?).with(json_metadata_path).and_return(true)
171
+ #expect(IO).to receive(:read).with(json_metadata_path)
172
+ allow(IO).to receive(:read)
173
+ expect(metadata).to receive(:from_json)
174
+ knife.preferred_metadata
175
+ end
176
+
177
+ it "prefers metadata.rb over metadata.json" do
178
+ allow(File).to receive(:exist?).with(rb_metadata_path).and_return(true)
179
+ allow(File).to receive(:exist?).with(json_metadata_path).and_return(true)
180
+ allow(IO).to receive(:read)
181
+ expect(metadata).to receive(:from_file).with(rb_metadata_path)
182
+ expect(metadata).not_to receive(:from_json)
183
+ knife.preferred_metadata
184
+ end
185
+
186
+ it "rasies an error if it finds no metadata file" do
187
+ expect { knife.preferred_metadata }.to raise_error(Chef::Exceptions::MetadataNotFound)
188
+ end
189
+
149
190
  end
150
191
  end
@@ -25,23 +25,26 @@ require 'spec_helper'
25
25
  require 'uri'
26
26
 
27
27
  describe Chef::Knife do
28
+
29
+ let(:stderr) { StringIO.new }
30
+
31
+ let(:knife) { Chef::Knife.new }
32
+
28
33
  before(:each) do
29
34
  Chef::Log.logger = Logger.new(StringIO.new)
30
35
 
31
36
  Chef::Config[:node_name] = "webmonkey.example.com"
32
37
 
33
38
  # Prevent gratuitous code reloading:
34
- Chef::Knife.stub(:load_commands)
35
- @knife = Chef::Knife.new
36
- @knife.ui.stub(:puts)
37
- @knife.ui.stub(:print)
38
- Chef::Log.stub(:init)
39
- Chef::Log.stub(:level)
39
+ allow(Chef::Knife).to receive(:load_commands)
40
+ allow(knife.ui).to receive(:puts)
41
+ allow(knife.ui).to receive(:print)
42
+ allow(Chef::Log).to receive(:init)
43
+ allow(Chef::Log).to receive(:level)
40
44
  [:debug, :info, :warn, :error, :crit].each do |level_sym|
41
45
  Chef::Log.stub(level_sym)
42
46
  end
43
- Chef::Knife.stub(:puts)
44
- @stderr = StringIO.new
47
+ allow(Chef::Knife).to receive(:puts)
45
48
  end
46
49
 
47
50
  after(:each) do
@@ -217,9 +220,12 @@ describe Chef::Knife do
217
220
  end
218
221
 
219
222
  it "exits if no subcommand matches the CLI args" do
220
- Chef::Knife.ui.stub(:stderr).and_return(@stderr)
221
- Chef::Knife.ui.should_receive(:fatal)
222
- lambda {Chef::Knife.run(%w{fuuu uuuu fuuuu})}.should raise_error(SystemExit) { |e| e.status.should_not == 0 }
223
+ stdout = StringIO.new
224
+
225
+ allow(Chef::Knife.ui).to receive(:stderr).and_return(stderr)
226
+ allow(Chef::Knife.ui).to receive(:stdout).and_return(stdout)
227
+ expect(Chef::Knife.ui).to receive(:fatal)
228
+ expect {Chef::Knife.run(%w{fuuu uuuu fuuuu})}.to raise_error(SystemExit) { |e| expect(e.status).not_to eq(0) }
223
229
  end
224
230
 
225
231
  it "loads lazy dependencies" do
@@ -269,157 +275,181 @@ describe Chef::Knife do
269
275
  let(:fake_config) { "/does/not/exist/knife.rb" }
270
276
 
271
277
  before do
272
- @knife.config[:verbosity] = 1
273
- @knife.config[:config_file] = fake_config
278
+ knife.config[:verbosity] = 1
279
+ knife.config[:config_file] = fake_config
274
280
  config_loader = double("Chef::WorkstationConfigLoader", :load => true, :no_config_found? => false, :chef_config_dir => "/etc/chef", :config_location => fake_config)
275
281
  allow(config_loader).to receive(:explicit_config_file=).with(fake_config).and_return(fake_config)
276
282
  allow(Chef::WorkstationConfigLoader).to receive(:new).and_return(config_loader)
277
283
  end
278
284
 
279
285
  it "prints the path to the configuration file used" do
280
- @stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new
281
- @knife.ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {})
286
+ stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
287
+ knife.ui = Chef::Knife::UI.new(stdout, stderr, stdin, {})
282
288
  expect(Chef::Log).to receive(:info).with("Using configuration from #{fake_config}")
283
- @knife.configure_chef
289
+ knife.configure_chef
284
290
  end
285
291
  end
286
292
  end
287
293
  end
288
294
 
289
295
  describe "when first created" do
296
+
297
+ let(:knife) { KnifeSpecs::TestYourself.new(%w{with some args -s scrogramming}) }
298
+
290
299
  before do
291
300
  unless KnifeSpecs.const_defined?(:TestYourself)
292
301
  Kernel.load(File.join(CHEF_SPEC_DATA, 'knife_subcommand', 'test_yourself.rb'))
293
302
  end
294
- @knife = KnifeSpecs::TestYourself.new(%w{with some args -s scrogramming})
295
303
  end
296
304
 
297
305
  it "it parses the options passed to it" do
298
- @knife.config[:scro].should == 'scrogramming'
306
+ expect(knife.config[:scro]).to eq('scrogramming')
299
307
  end
300
308
 
301
309
  it "extracts its command specific args from the full arg list" do
302
- @knife.name_args.should == %w{with some args}
310
+ expect(knife.name_args).to eq(%w{with some args})
303
311
  end
304
312
 
305
313
  it "does not have lazy dependencies loaded" do
306
- @knife.class.test_deps_loaded.should_not be_true
314
+ expect(knife.class.test_deps_loaded).to be(nil)
307
315
  end
308
316
  end
309
317
 
310
318
  describe "when formatting exceptions" do
319
+
320
+ let(:stdout) { StringIO.new }
321
+ let(:stderr) { StringIO.new }
322
+ let(:stdin) { StringIO.new }
323
+
324
+ let(:ui) { Chef::Knife::UI.new(stdout, stderr, stdin, {}) }
325
+
311
326
  before do
312
- @stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new
313
- @knife.ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {})
314
- @knife.should_receive(:exit).with(100)
327
+ knife.ui = ui
328
+ expect(knife).to receive(:exit).with(100)
315
329
  end
316
330
 
317
331
  it "formats 401s nicely" do
318
332
  response = Net::HTTPUnauthorized.new("1.1", "401", "Unauthorized")
319
333
  response.instance_variable_set(:@read, true) # I hate you, net/http.
320
- response.stub(:body).and_return(Chef::JSONCompat.to_json(:error => "y u no syncronize your clock?"))
321
- @knife.stub(:run).and_raise(Net::HTTPServerException.new("401 Unauthorized", response))
322
- @knife.run_with_pretty_exceptions
323
- @stderr.string.should match(/ERROR: Failed to authenticate to/)
324
- @stderr.string.should match(/Response: y u no syncronize your clock\?/)
334
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u no syncronize your clock?"))
335
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("401 Unauthorized", response))
336
+ knife.run_with_pretty_exceptions
337
+ expect(stderr.string).to match(/ERROR: Failed to authenticate to/)
338
+ expect(stderr.string).to match(/Response: y u no syncronize your clock\?/)
325
339
  end
326
340
 
327
341
  it "formats 403s nicely" do
328
342
  response = Net::HTTPForbidden.new("1.1", "403", "Forbidden")
329
343
  response.instance_variable_set(:@read, true) # I hate you, net/http.
330
- response.stub(:body).and_return(Chef::JSONCompat.to_json(:error => "y u no administrator"))
331
- @knife.stub(:run).and_raise(Net::HTTPServerException.new("403 Forbidden", response))
332
- @knife.stub(:username).and_return("sadpanda")
333
- @knife.run_with_pretty_exceptions
334
- @stderr.string.should match(%r[ERROR: You authenticated successfully to http.+ as sadpanda but you are not authorized for this action])
335
- @stderr.string.should match(%r[Response: y u no administrator])
344
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u no administrator"))
345
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("403 Forbidden", response))
346
+ allow(knife).to receive(:username).and_return("sadpanda")
347
+ knife.run_with_pretty_exceptions
348
+ expect(stderr.string).to match(%r[ERROR: You authenticated successfully to http.+ as sadpanda but you are not authorized for this action])
349
+ expect(stderr.string).to match(%r[Response: y u no administrator])
336
350
  end
337
351
 
338
352
  it "formats 400s nicely" do
339
353
  response = Net::HTTPBadRequest.new("1.1", "400", "Bad Request")
340
354
  response.instance_variable_set(:@read, true) # I hate you, net/http.
341
- response.stub(:body).and_return(Chef::JSONCompat.to_json(:error => "y u search wrong"))
342
- @knife.stub(:run).and_raise(Net::HTTPServerException.new("400 Bad Request", response))
343
- @knife.run_with_pretty_exceptions
344
- @stderr.string.should match(%r[ERROR: The data in your request was invalid])
345
- @stderr.string.should match(%r[Response: y u search wrong])
355
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u search wrong"))
356
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("400 Bad Request", response))
357
+ knife.run_with_pretty_exceptions
358
+ expect(stderr.string).to match(%r[ERROR: The data in your request was invalid])
359
+ expect(stderr.string).to match(%r[Response: y u search wrong])
346
360
  end
347
361
 
348
362
  it "formats 404s nicely" do
349
363
  response = Net::HTTPNotFound.new("1.1", "404", "Not Found")
350
364
  response.instance_variable_set(:@read, true) # I hate you, net/http.
351
- response.stub(:body).and_return(Chef::JSONCompat.to_json(:error => "nothing to see here"))
352
- @knife.stub(:run).and_raise(Net::HTTPServerException.new("404 Not Found", response))
353
- @knife.run_with_pretty_exceptions
354
- @stderr.string.should match(%r[ERROR: The object you are looking for could not be found])
355
- @stderr.string.should match(%r[Response: nothing to see here])
365
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "nothing to see here"))
366
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("404 Not Found", response))
367
+ knife.run_with_pretty_exceptions
368
+ expect(stderr.string).to match(%r[ERROR: The object you are looking for could not be found])
369
+ expect(stderr.string).to match(%r[Response: nothing to see here])
356
370
  end
357
371
 
358
372
  it "formats 500s nicely" do
359
373
  response = Net::HTTPInternalServerError.new("1.1", "500", "Internal Server Error")
360
374
  response.instance_variable_set(:@read, true) # I hate you, net/http.
361
- response.stub(:body).and_return(Chef::JSONCompat.to_json(:error => "sad trombone"))
362
- @knife.stub(:run).and_raise(Net::HTTPFatalError.new("500 Internal Server Error", response))
363
- @knife.run_with_pretty_exceptions
364
- @stderr.string.should match(%r[ERROR: internal server error])
365
- @stderr.string.should match(%r[Response: sad trombone])
375
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "sad trombone"))
376
+ allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("500 Internal Server Error", response))
377
+ knife.run_with_pretty_exceptions
378
+ expect(stderr.string).to match(%r[ERROR: internal server error])
379
+ expect(stderr.string).to match(%r[Response: sad trombone])
366
380
  end
367
381
 
368
382
  it "formats 502s nicely" do
369
383
  response = Net::HTTPBadGateway.new("1.1", "502", "Bad Gateway")
370
384
  response.instance_variable_set(:@read, true) # I hate you, net/http.
371
- response.stub(:body).and_return(Chef::JSONCompat.to_json(:error => "sadder trombone"))
372
- @knife.stub(:run).and_raise(Net::HTTPFatalError.new("502 Bad Gateway", response))
373
- @knife.run_with_pretty_exceptions
374
- @stderr.string.should match(%r[ERROR: bad gateway])
375
- @stderr.string.should match(%r[Response: sadder trombone])
385
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "sadder trombone"))
386
+ allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("502 Bad Gateway", response))
387
+ knife.run_with_pretty_exceptions
388
+ expect(stderr.string).to match(%r[ERROR: bad gateway])
389
+ expect(stderr.string).to match(%r[Response: sadder trombone])
376
390
  end
377
391
 
378
392
  it "formats 503s nicely" do
379
393
  response = Net::HTTPServiceUnavailable.new("1.1", "503", "Service Unavailable")
380
394
  response.instance_variable_set(:@read, true) # I hate you, net/http.
381
- response.stub(:body).and_return(Chef::JSONCompat.to_json(:error => "saddest trombone"))
382
- @knife.stub(:run).and_raise(Net::HTTPFatalError.new("503 Service Unavailable", response))
383
- @knife.run_with_pretty_exceptions
384
- @stderr.string.should match(%r[ERROR: Service temporarily unavailable])
385
- @stderr.string.should match(%r[Response: saddest trombone])
395
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "saddest trombone"))
396
+ allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("503 Service Unavailable", response))
397
+ knife.run_with_pretty_exceptions
398
+ expect(stderr.string).to match(%r[ERROR: Service temporarily unavailable])
399
+ expect(stderr.string).to match(%r[Response: saddest trombone])
386
400
  end
387
401
 
388
402
  it "formats other HTTP errors nicely" do
389
403
  response = Net::HTTPPaymentRequired.new("1.1", "402", "Payment Required")
390
404
  response.instance_variable_set(:@read, true) # I hate you, net/http.
391
- response.stub(:body).and_return(Chef::JSONCompat.to_json(:error => "nobugfixtillyoubuy"))
392
- @knife.stub(:run).and_raise(Net::HTTPServerException.new("402 Payment Required", response))
393
- @knife.run_with_pretty_exceptions
394
- @stderr.string.should match(%r[ERROR: Payment Required])
395
- @stderr.string.should match(%r[Response: nobugfixtillyoubuy])
405
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "nobugfixtillyoubuy"))
406
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("402 Payment Required", response))
407
+ knife.run_with_pretty_exceptions
408
+ expect(stderr.string).to match(%r[ERROR: Payment Required])
409
+ expect(stderr.string).to match(%r[Response: nobugfixtillyoubuy])
396
410
  end
397
411
 
398
412
  it "formats NameError and NoMethodError nicely" do
399
- @knife.stub(:run).and_raise(NameError.new("Undefined constant FUUU"))
400
- @knife.run_with_pretty_exceptions
401
- @stderr.string.should match(%r[ERROR: knife encountered an unexpected error])
402
- @stderr.string.should match(%r[This may be a bug in the 'knife' knife command or plugin])
403
- @stderr.string.should match(%r[Exception: NameError: Undefined constant FUUU])
413
+ allow(knife).to receive(:run).and_raise(NameError.new("Undefined constant FUUU"))
414
+ knife.run_with_pretty_exceptions
415
+ expect(stderr.string).to match(%r[ERROR: knife encountered an unexpected error])
416
+ expect(stderr.string).to match(%r[This may be a bug in the 'knife' knife command or plugin])
417
+ expect(stderr.string).to match(%r[Exception: NameError: Undefined constant FUUU])
404
418
  end
405
419
 
406
420
  it "formats missing private key errors nicely" do
407
- @knife.stub(:run).and_raise(Chef::Exceptions::PrivateKeyMissing.new('key not there'))
408
- @knife.stub(:api_key).and_return("/home/root/.chef/no-key-here.pem")
409
- @knife.run_with_pretty_exceptions
410
- @stderr.string.should match(%r[ERROR: Your private key could not be loaded from /home/root/.chef/no-key-here.pem])
411
- @stderr.string.should match(%r[Check your configuration file and ensure that your private key is readable])
421
+ allow(knife).to receive(:run).and_raise(Chef::Exceptions::PrivateKeyMissing.new('key not there'))
422
+ allow(knife).to receive(:api_key).and_return("/home/root/.chef/no-key-here.pem")
423
+ knife.run_with_pretty_exceptions
424
+ expect(stderr.string).to match(%r[ERROR: Your private key could not be loaded from /home/root/.chef/no-key-here.pem])
425
+ expect(stderr.string).to match(%r[Check your configuration file and ensure that your private key is readable])
412
426
  end
413
427
 
414
428
  it "formats connection refused errors nicely" do
415
- @knife.stub(:run).and_raise(Errno::ECONNREFUSED.new('y u no shut up'))
416
- @knife.run_with_pretty_exceptions
429
+ allow(knife).to receive(:run).and_raise(Errno::ECONNREFUSED.new('y u no shut up'))
430
+ knife.run_with_pretty_exceptions
417
431
  # Errno::ECONNREFUSED message differs by platform
418
432
  # *nix = Errno::ECONNREFUSED: Connection refused
419
433
  # win32: Errno::ECONNREFUSED: No connection could be made because the target machine actively refused it.
420
- @stderr.string.should match(%r[ERROR: Network Error: .* - y u no shut up])
421
- @stderr.string.should match(%r[Check your knife configuration and network settings])
434
+ expect(stderr.string).to match(%r[ERROR: Network Error: .* - y u no shut up])
435
+ expect(stderr.string).to match(%r[Check your knife configuration and network settings])
422
436
  end
437
+
438
+ it "formats SSL errors nicely and suggests to use `knife ssl check` and `knife ssl fetch`" do
439
+ error = OpenSSL::SSL::SSLError.new("SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed")
440
+ allow(knife).to receive(:run).and_raise(error)
441
+
442
+ knife.run_with_pretty_exceptions
443
+
444
+ expected_message=<<-MSG
445
+ ERROR: Could not establish a secure connection to the server.
446
+ Use `knife ssl check` to troubleshoot your SSL configuration.
447
+ If your Chef Server uses a self-signed certificate, you can use
448
+ `knife ssl fetch` to make knife trust the server's certificates.
449
+ MSG
450
+ expect(stderr.string).to include(expected_message)
451
+ end
452
+
423
453
  end
424
454
 
425
455
  end