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
@@ -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