knife 17.4.18 → 17.4.46

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70c4e18afa9b4762387d8a0a6c9c75bca9e1e42723a041147bea36c2efaae176
4
- data.tar.gz: '00953cd3c86ab1e11eb9ddb9a8fdf8fffbae40705671090169411877fa72a069'
3
+ metadata.gz: 5bce1fdf8a026140f71d734c579f29ac19eeb68da7965ebc5426cdad7c00eb51
4
+ data.tar.gz: b10eafb114347dec04f3e1f0aaf0c1a9d9806ba2f76cfad8b57c1065e72bb0b9
5
5
  SHA512:
6
- metadata.gz: f2d2130d599b56a3d43bbbbe5f64407dfc19aaec9df4ea87af2b835b950dda18ef1b9eb6cee28fae9ee05bafef8b6fe180219b36f1f7fbe5333b4efcd3236002
7
- data.tar.gz: 0d500fcfd9e9aa0830d04bae3f9d4e3712ab52bf6fcd9b87c4f0e3bbb2149f8568d179c41656189b7614bdbdd91e8dbd5d14800c38771275466c5e3c9e0c78f0
6
+ metadata.gz: 4a5df15d3687dddd23e0469fb170fd2ea6325ee186378f98a334f7a1d42757195d2dc541ff9b370b624f949a0ab06de11d607b2029559788f0a1e406b0d8ac8d
7
+ data.tar.gz: 24414b3f673ae91d6c837c3b3c8740cf60746dc337c770b752fa1baf65f1773510ea9a8c72ee19e2979d70688ca1810a502afbe43092c321ae0cc1dceb8a8e69
data/Gemfile CHANGED
@@ -17,10 +17,10 @@ group(:omnibus_package, :pry) do
17
17
  end
18
18
 
19
19
  group(:chefstyle) do
20
- gem "chefstyle", git: "https://github.com/chef/chefstyle.git", branch: "master"
20
+ gem "chefstyle", git: "https://github.com/chef/chefstyle.git", branch: "main"
21
21
  end
22
22
 
23
- gem "ohai", git: "https://github.com/chef/ohai.git", branch: "master"
23
+ gem "ohai", git: "https://github.com/chef/ohai.git", branch: "main"
24
24
  gem "chef", path: ".."
25
25
  gem "chef-utils", path: File.expand_path("../chef-utils", __dir__) if File.exist?(File.expand_path("../chef-utils", __dir__))
26
26
  gem "chef-config", path: File.expand_path("../chef-config", __dir__) if File.exist?(File.expand_path("../chef-config", __dir__))
data/knife.gemspec CHANGED
@@ -30,8 +30,7 @@ Gem::Specification.new do |s|
30
30
  s.add_dependency "net-ssh-multi", "~> 1.2", ">= 1.2.1"
31
31
  s.add_dependency "ed25519", "~> 1.2" # ed25519 ssh key support
32
32
  s.add_dependency "bcrypt_pbkdf", "~> 1.1" # ed25519 ssh key support
33
- # we can't use this gem until illegal instruction issues are resolved
34
- # s.add_dependency "x25519" # ed25519 KEX module
33
+ s.add_dependency "x25519", ">= 1.0.9" # ed25519 KEX module. 1.0.9+ required to resolve sigill failures
35
34
  s.add_dependency "highline", ">= 1.6.9", "< 3" # Used in UI to present a list, no other usage.
36
35
 
37
36
  s.add_dependency "tty-prompt", "~> 0.21" # knife ui.ask prompt
@@ -52,7 +51,7 @@ Gem::Specification.new do |s|
52
51
 
53
52
  s.metadata = {
54
53
  "bug_tracker_uri" => "https://github.com/chef/chef/issues",
55
- "changelog_uri" => "https://github.com/chef/chef/blob/master/CHANGELOG.md",
54
+ "changelog_uri" => "https://github.com/chef/chef/blob/main/CHANGELOG.md",
56
55
  "documentation_uri" => "https://docs.chef.io/",
57
56
  "homepage_uri" => "https://www.chef.io",
58
57
  "mailing_list_uri" => "https://discourse.chef.io/",
@@ -54,6 +54,10 @@ class Chef
54
54
  @client_field ||= Chef::ApiClientV1.new
55
55
  end
56
56
 
57
+ def file
58
+ config[:file]
59
+ end
60
+
57
61
  def create_client(client)
58
62
  # should not be using save :( bad behavior
59
63
  Chef::ApiClientV1.from_hash(client).save
@@ -81,13 +85,7 @@ class Chef
81
85
  client.public_key File.read(File.expand_path(config[:public_key]))
82
86
  end
83
87
 
84
- # Check the file before creating the client so the api is more transactional.
85
- if config[:file]
86
- file = config[:file]
87
- dir_name = File.dirname(file)
88
- check_writable_or_exists(dir_name, "Directory")
89
- check_writable_or_exists(file, "File")
90
- end
88
+ file_is_writable!
91
89
 
92
90
  output = edit_hash(client)
93
91
  final_client = create_client(output)
@@ -105,15 +103,35 @@ class Chef
105
103
  end
106
104
  end
107
105
 
108
- # To check if file or directory exists or writable and raise exception accordingly
109
- def check_writable_or_exists(file, type)
110
- if File.exist?(file)
111
- unless File.writable?(file)
112
- ui.fatal "#{type} #{file} is not writable. Check permissions."
113
- exit 1
114
- end
115
- else
116
- ui.fatal "#{type} #{file} does not exist."
106
+ #
107
+ # This method is used to verify that the file and it's containing
108
+ # directory are writable. This ensures that you don't create the client
109
+ # and then lose the private key because you weren't able to write it to
110
+ # disk.
111
+ #
112
+ # @return [void]
113
+ #
114
+ def file_is_writable!
115
+ return unless file
116
+
117
+ dir = File.dirname(File.expand_path(file))
118
+ unless File.exist?(dir)
119
+ ui.fatal "Directory #{dir} does not exist. Please create and retry."
120
+ exit 1
121
+ end
122
+
123
+ unless File.directory?(dir)
124
+ ui.fatal "#{dir} exists, but is not a directory. Please update your file path (--file #{file}) or re-create #{dir} as a directory."
125
+ exit 1
126
+ end
127
+
128
+ unless File.writable?(dir)
129
+ ui.fatal "Directory #{dir} is not writable. Please check the permissions."
130
+ exit 1
131
+ end
132
+
133
+ if File.exist?(file) && !File.writable?(file)
134
+ ui.fatal "File #{file} is not writable. Please check the permissions."
117
135
  exit 1
118
136
  end
119
137
  end
@@ -71,6 +71,11 @@ class Chef
71
71
  long: "--include-dependencies",
72
72
  description: "Also upload cookbook dependencies."
73
73
 
74
+ option :check_dependencies,
75
+ boolean: true, long: "--[no-]check-dependencies",
76
+ description: "Whether or not cookbook dependencies are verified before uploading cookbook(s) to #{ChefUtils::Dist::Server::PRODUCT}. You shouldn't disable this unless you really know what you're doing.",
77
+ default: true
78
+
74
79
  def run
75
80
  # Sanity check before we load anything from the server
76
81
  if ! config[:all] && @name_args.empty?
@@ -86,11 +91,6 @@ class Chef
86
91
  upload_failures = 0
87
92
  upload_ok = 0
88
93
 
89
- # Get a list of cookbooks and their versions from the server
90
- # to check for the existence of a cookbook's dependencies.
91
- @server_side_cookbooks = Chef::CookbookVersion.list_all_versions
92
- justify_width = @server_side_cookbooks.map(&:size).max.to_i + 2
93
-
94
94
  cookbooks = []
95
95
  cookbooks_to_upload.each do |cookbook_name, cookbook|
96
96
  raise Chef::Exceptions::MetadataNotFound.new(cookbook.root_paths[0], cookbook_name) unless cookbook.has_metadata_file?
@@ -120,7 +120,7 @@ class Chef
120
120
  if config[:all]
121
121
  if cookbooks_for_upload.any?
122
122
  begin
123
- upload(cookbooks_for_upload, justify_width)
123
+ upload(cookbooks_for_upload)
124
124
  rescue Chef::Exceptions::CookbookFrozen
125
125
  ui.warn("Not updating version constraints for some cookbooks in the environment as the cookbook is frozen.")
126
126
  ui.error("Uploading of some of the cookbooks must be failed. Remove cookbook whose version is frozen from your cookbooks repo OR use --force option.")
@@ -133,7 +133,7 @@ class Chef
133
133
  else
134
134
  tmp_cl.each do |cookbook_name, cookbook|
135
135
 
136
- upload([cookbook], justify_width)
136
+ upload([cookbook])
137
137
  upload_ok += 1
138
138
  rescue Exceptions::CookbookNotFoundInRepo => e
139
139
  upload_failures += 1
@@ -165,6 +165,27 @@ class Chef
165
165
  end
166
166
  end
167
167
 
168
+ def server_side_cookbooks
169
+ @server_side_cookbooks ||= Chef::CookbookVersion.list_all_versions
170
+ end
171
+
172
+ def justify_width
173
+ @justify_width ||= server_side_cookbooks.map(&:size).max.to_i + 2
174
+ end
175
+
176
+ #
177
+ # @param cookbook [Chef::CookbookVersion]
178
+ #
179
+ def left_justify_name(cookbook)
180
+ # We only want to lookup justify width value if we're already loading
181
+ # cookbooks to check dependencies exist in Chef Infra Server.
182
+ if config[:check_dependencies] == true
183
+ cookbook.name.to_s.ljust(justify_width + 10)
184
+ else
185
+ cookbook.name.to_s.ljust(24)
186
+ end
187
+ end
188
+
168
189
  def cookbooks_to_upload
169
190
  @cookbooks_to_upload ||=
170
191
  if config[:all]
@@ -220,11 +241,11 @@ class Chef
220
241
  end
221
242
  end
222
243
 
223
- def upload(cookbooks, justify_width)
244
+ def upload(cookbooks)
224
245
  cookbooks.each do |cb|
225
- ui.info("Uploading #{cb.name.to_s.ljust(justify_width + 10)} [#{cb.version}]")
246
+ ui.info("Uploading #{left_justify_name(cb)} [#{cb.version}]")
226
247
  check_for_broken_links!(cb)
227
- check_for_dependencies!(cb)
248
+ check_for_dependencies!(cb) if config[:check_dependencies] == true
228
249
  end
229
250
  Chef::CookbookUploader.new(cookbooks, force: config[:force], concurrency: config[:concurrency]).upload_cookbooks
230
251
  rescue Chef::Exceptions::CookbookFrozen => e
@@ -265,12 +286,12 @@ class Chef
265
286
  end
266
287
 
267
288
  def check_server_side_cookbooks(cookbook_name, version)
268
- if @server_side_cookbooks[cookbook_name].nil?
289
+ if server_side_cookbooks[cookbook_name].nil?
269
290
  false
270
291
  else
271
- versions = @server_side_cookbooks[cookbook_name]["versions"].collect { |versions| versions["version"] }
292
+ versions = server_side_cookbooks[cookbook_name]["versions"].collect { |versions| versions["version"] }
272
293
  Log.debug "Versions of cookbook '#{cookbook_name}' returned by the server: #{versions.join(", ")}"
273
- @server_side_cookbooks[cookbook_name]["versions"].each do |versions_hash|
294
+ server_side_cookbooks[cookbook_name]["versions"].each do |versions_hash|
274
295
  if Chef::VersionConstraint.new(version).include?(versions_hash["version"])
275
296
  Log.debug "Matched cookbook '#{cookbook_name}' with constraint '#{version}' to cookbook version '#{versions_hash["version"]}' on the server"
276
297
  return true
@@ -17,7 +17,7 @@
17
17
  class Chef
18
18
  class Knife
19
19
  KNIFE_ROOT = File.expand_path("../..", __dir__)
20
- VERSION = "17.4.18".freeze
20
+ VERSION = "17.4.46".freeze
21
21
  end
22
22
  end
23
23
 
@@ -50,7 +50,6 @@ describe "knife client create", :workstation do
50
50
 
51
51
  it "saves the private key to a file" do
52
52
  Dir.mktmpdir do |tgt|
53
- File.open("#{tgt}/bah.pem", "w") { |pub| pub.write("test key") }
54
53
  knife("client create -f #{tgt}/bah.pem bah").should_succeed stderr: out
55
54
  expect(File).to exist("#{tgt}/bah.pem")
56
55
  end
@@ -54,6 +54,19 @@ describe Chef::Knife::ClientCreate do
54
54
  Chef::Config[:node_name] = "webmonkey.example.com"
55
55
  end
56
56
 
57
+ let(:tmpdir) { Dir.mktmpdir }
58
+ let(:file_path) { File.join(tmpdir, "client.pem") }
59
+ let(:dir_path) { File.dirname(file_path) }
60
+
61
+ before do
62
+ allow(File).to receive(:exist?).and_call_original
63
+ allow(File).to receive(:exist?).with(file_path).and_return(false)
64
+ allow(File).to receive(:exist?).with(dir_path).and_return(true)
65
+ allow(File).to receive(:directory?).with(dir_path).and_return(true)
66
+ allow(File).to receive(:writable?).with(file_path).and_return(true)
67
+ allow(File).to receive(:writable?).with(dir_path).and_return(true)
68
+ end
69
+
57
70
  describe "run" do
58
71
  context "when nothing is passed" do
59
72
  # from spec/support/shared/unit/knife_shared.rb
@@ -118,18 +131,66 @@ describe Chef::Knife::ClientCreate do
118
131
 
119
132
  describe "with -f or --file" do
120
133
  before do
134
+ knife.config[:file] = file_path
121
135
  client.private_key "woot"
122
136
  end
123
137
 
124
138
  it "should write the private key to a file" do
125
- file = Tempfile.new
126
- file_path = file.path
127
- knife.config[:file] = file_path
128
139
  filehandle = double("Filehandle")
129
140
  expect(filehandle).to receive(:print).with("woot")
130
141
  expect(File).to receive(:open).with(file_path, "w").and_yield(filehandle)
131
142
  knife.run
132
143
  end
144
+
145
+ context "when the directory does not exist" do
146
+ before { allow(File).to receive(:exist?).with(dir_path).and_return(false) }
147
+
148
+ it "writes a fatal message and exits 1" do
149
+ expect(knife.ui).to receive(:fatal).with("Directory #{dir_path} does not exist. Please create and retry.")
150
+ expect { knife.run }.to raise_error(SystemExit)
151
+ end
152
+ end
153
+
154
+ context "when the directory is not writable" do
155
+ before { allow(File).to receive(:writable?).with(dir_path).and_return(false) }
156
+
157
+ it "writes a fatal message and exits 1" do
158
+ expect(knife.ui).to receive(:fatal).with("Directory #{dir_path} is not writable. Please check the permissions.")
159
+ expect { knife.run }.to raise_error(SystemExit)
160
+ end
161
+ end
162
+
163
+ context "when the directory is a file" do
164
+ before { allow(File).to receive(:directory?).with(dir_path).and_return(false) }
165
+
166
+ it "writes a fatal message and exits 1" do
167
+ expect(knife.ui).to receive(:fatal).with("#{dir_path} exists, but is not a directory. Please update your file path (--file #{file_path}) or re-create #{dir_path} as a directory.")
168
+ expect { knife.run }.to raise_error(SystemExit)
169
+ end
170
+ end
171
+
172
+ context "when the file does not exist" do
173
+ before do
174
+ allow(File).to receive(:exist?).with(file_path).and_return(false)
175
+ end
176
+
177
+ it "does not log a fatal message and does not raise exception" do
178
+ expect(knife.ui).not_to receive(:fatal)
179
+ expect { knife.run }.not_to raise_error
180
+ end
181
+ end
182
+
183
+ context "when the file exists and is not writable" do
184
+ before do
185
+ allow(File).to receive(:exist?).with(file_path).and_return(true)
186
+ allow(File).to receive(:writable?).with(file_path).and_return(false)
187
+ end
188
+
189
+ it "writes a fatal message and exits 1" do
190
+ expect(knife.ui).to receive(:fatal).with("File #{file_path} is not writable. Please check the permissions.")
191
+ expect { knife.run }.to raise_error(SystemExit)
192
+ end
193
+ end
133
194
  end
134
195
 
135
196
  describe "with -p or --public-key" do
@@ -166,39 +227,6 @@ describe Chef::Knife::ClientCreate do
166
227
  expect(client.validator).to be_truthy
167
228
  end
168
229
  end
169
-
170
- describe "with -f or --file when dir or file is not writable or does not exists" do
171
- let(:dir_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "knife", "temp_dir")) }
172
- let(:file_path) { File.expand_path(File.join(dir_path, "tmp.pem")) }
173
-
174
- it "when the directory does not exists" do
175
- knife.config[:file] = "example/client1.pem"
176
- expect(knife.ui).to receive(:fatal).with("Directory example does not exist.")
177
- expect { knife.run }.to raise_error(SystemExit)
178
- end
179
-
180
- it "when the directory not writable" do
181
- knife.config[:file] = file_path
182
- File.chmod(777, dir_path)
183
- expect(knife.ui).to receive(:fatal).with("Directory #{dir_path} is not writable. Check permissions.")
184
- expect { knife.run }.to raise_error(SystemExit)
185
- end
186
-
187
- it "when the file does not exists" do
188
- path = "#{dir_path}/client1.pem"
189
- knife.config[:file] = path
190
- File.chmod(0755, dir_path)
191
- expect(knife.ui).to receive(:fatal).with("File #{path} does not exist.")
192
- expect { knife.run }.to raise_error(SystemExit)
193
- end
194
-
195
- it "when the file is not writable" do
196
- knife.config[:file] = file_path
197
- File.chmod(777, file_path)
198
- expect(knife.ui).to receive(:fatal).with("File #{file_path} is not writable. Check permissions.")
199
- expect { knife.run }.to raise_error(SystemExit)
200
- end
201
- end
202
230
  end
203
231
  end
204
232
  end
@@ -60,11 +60,12 @@ describe Chef::Knife::CookbookUpload do
60
60
  before(:each) do
61
61
  allow(Chef::CookbookLoader).to receive(:new).and_return(cookbook_loader)
62
62
  allow(Chef::CookbookLoader).to receive(:copy_to_tmp_dir_from_array).and_yield(cookbook_loader)
63
+ allow(Chef::CookbookVersion).to receive(:list).and_return({})
64
+ allow(Chef::CookbookVersion).to receive(:list_all_versions).and_return({})
63
65
  end
64
66
 
65
67
  describe "with --concurrency" do
66
68
  it "should upload cookbooks with predefined concurrency" do
67
- allow(Chef::CookbookVersion).to receive(:list_all_versions).and_return({})
68
69
  knife.config[:concurrency] = 3
69
70
  test_cookbook = Chef::CookbookVersion.new("test_cookbook", "/tmp/blah")
70
71
  allow(cookbook_loader).to receive(:each).and_yield("test_cookbook", test_cookbook)
@@ -79,7 +80,6 @@ describe Chef::Knife::CookbookUpload do
79
80
  describe "run" do
80
81
  before(:each) do
81
82
  allow(Chef::CookbookUploader).to receive_messages(new: cookbook_uploader)
82
- allow(Chef::CookbookVersion).to receive(:list_all_versions).and_return({})
83
83
  end
84
84
 
85
85
  it "should print usage and exit when a cookbook name is not provided" do
@@ -214,48 +214,110 @@ describe Chef::Knife::CookbookUpload do
214
214
  end
215
215
  end
216
216
 
217
- describe "when specifying a cookbook name with missing dependencies" do
218
- let(:cookbook_dependency) { Chef::CookbookVersion.new("dependency", "/tmp/blah") }
217
+ context "when chef_dependencies config is disabled" do
218
+ before do
219
+ knife.config[:check_dependencies] = false
220
+ end
219
221
 
220
- before(:each) do
221
- cookbook.metadata.depends("dependency")
222
- allow(cookbook_loader).to receive(:[]) do |ckbk|
223
- { "test_cookbook" => cookbook,
224
- "dependency" => cookbook_dependency }[ckbk]
222
+ describe "when specifying a cookbook name with missing dependencies" do
223
+ let(:cookbook_dependency) { Chef::CookbookVersion.new("dependency", "/tmp/blah") }
224
+
225
+ before(:each) do
226
+ cookbook.metadata.depends("dependency")
227
+ allow(cookbook_loader).to receive(:[]) do |ckbk|
228
+ { "test_cookbook" => cookbook,
229
+ "dependency" => cookbook_dependency }[ckbk]
230
+ end
231
+ allow(knife).to receive(:cookbook_names).and_return(%w{cookbook_dependency test_cookbook})
232
+ @stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new
233
+ knife.ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {})
225
234
  end
226
- allow(knife).to receive(:cookbook_names).and_return(%w{cookbook_dependency test_cookbook})
227
- @stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new
228
- knife.ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {})
229
- end
230
235
 
231
- it "should exit and not upload the cookbook" do
232
- expect(cookbook_loader).to receive(:[]).once.with("test_cookbook")
233
- expect(cookbook_uploader).not_to receive(:upload_cookbooks)
234
- expect { knife.run }.to raise_error(SystemExit)
236
+ it "should not fetch all cookbooks from Chef Infra Server" do
237
+ expect(Chef::CookbookVersion).not_to receive(:list_all_versions)
238
+ knife.run
239
+ end
240
+
241
+ it "should upload the cookbook" do
242
+ expect(cookbook_loader).to receive(:[]).once.with("test_cookbook")
243
+ expect(cookbook_uploader).to receive(:upload_cookbooks)
244
+ knife.run
245
+ end
246
+
247
+ it "should not output a message for a single missing dependency" do
248
+ knife.run
249
+ expect(@stderr.string).not_to include("Cookbook test_cookbook depends on cookbooks which are not currently")
250
+ expect(@stderr.string).not_to include("being uploaded and cannot be found on the server.")
251
+ expect(@stderr.string).not_to include("The missing cookbook(s) are: 'dependency' version '>= 0.0.0'")
252
+ end
253
+
254
+ it "should not output a message for a multiple missing dependencies which are concatenated" do
255
+ cookbook_dependency2 = Chef::CookbookVersion.new("dependency2")
256
+ cookbook.metadata.depends("dependency2")
257
+ allow(cookbook_loader).to receive(:[]) do |ckbk|
258
+ { "test_cookbook" => cookbook,
259
+ "dependency" => cookbook_dependency,
260
+ "dependency2" => cookbook_dependency2 }[ckbk]
261
+ end
262
+ allow(knife).to receive(:cookbook_names).and_return(%w{dependency dependency2 test_cookbook})
263
+ knife.run
264
+ expect(@stderr.string).not_to include("Cookbook test_cookbook depends on cookbooks which are not currently")
265
+ expect(@stderr.string).not_to include("being uploaded and cannot be found on the server.")
266
+ expect(@stderr.string).not_to include("The missing cookbook(s) are:")
267
+ expect(@stderr.string).not_to include("'dependency' version '>= 0.0.0'")
268
+ expect(@stderr.string).not_to include("'dependency2' version '>= 0.0.0'")
269
+ end
235
270
  end
271
+ end
236
272
 
237
- it "should output a message for a single missing dependency" do
238
- expect { knife.run }.to raise_error(SystemExit)
239
- expect(@stderr.string).to include("Cookbook test_cookbook depends on cookbooks which are not currently")
240
- expect(@stderr.string).to include("being uploaded and cannot be found on the server.")
241
- expect(@stderr.string).to include("The missing cookbook(s) are: 'dependency' version '>= 0.0.0'")
273
+ context "when chef_dependencies config is enabled" do
274
+ before do
275
+ knife.config[:check_dependencies] = true
242
276
  end
243
277
 
244
- it "should output a message for a multiple missing dependencies which are concatenated" do
245
- cookbook_dependency2 = Chef::CookbookVersion.new("dependency2")
246
- cookbook.metadata.depends("dependency2")
247
- allow(cookbook_loader).to receive(:[]) do |ckbk|
248
- { "test_cookbook" => cookbook,
249
- "dependency" => cookbook_dependency,
250
- "dependency2" => cookbook_dependency2 }[ckbk]
278
+ describe "when specifying a cookbook name with missing dependencies" do
279
+ let(:cookbook_dependency) { Chef::CookbookVersion.new("dependency", "/tmp/blah") }
280
+
281
+ before(:each) do
282
+ cookbook.metadata.depends("dependency")
283
+ allow(cookbook_loader).to receive(:[]) do |ckbk|
284
+ { "test_cookbook" => cookbook,
285
+ "dependency" => cookbook_dependency }[ckbk]
286
+ end
287
+ allow(knife).to receive(:cookbook_names).and_return(%w{cookbook_dependency test_cookbook})
288
+ @stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new
289
+ knife.ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {})
290
+ end
291
+
292
+ it "should exit and not upload the cookbook" do
293
+ expect(cookbook_loader).to receive(:[]).once.with("test_cookbook")
294
+ expect(cookbook_uploader).not_to receive(:upload_cookbooks)
295
+ expect { knife.run }.to raise_error(SystemExit)
296
+ end
297
+
298
+ it "should output a message for a single missing dependency" do
299
+ expect { knife.run }.to raise_error(SystemExit)
300
+ expect(@stderr.string).to include("Cookbook test_cookbook depends on cookbooks which are not currently")
301
+ expect(@stderr.string).to include("being uploaded and cannot be found on the server.")
302
+ expect(@stderr.string).to include("The missing cookbook(s) are: 'dependency' version '>= 0.0.0'")
303
+ end
304
+
305
+ it "should output a message for a multiple missing dependencies which are concatenated" do
306
+ cookbook_dependency2 = Chef::CookbookVersion.new("dependency2")
307
+ cookbook.metadata.depends("dependency2")
308
+ allow(cookbook_loader).to receive(:[]) do |ckbk|
309
+ { "test_cookbook" => cookbook,
310
+ "dependency" => cookbook_dependency,
311
+ "dependency2" => cookbook_dependency2 }[ckbk]
312
+ end
313
+ allow(knife).to receive(:cookbook_names).and_return(%w{dependency dependency2 test_cookbook})
314
+ expect { knife.run }.to raise_error(SystemExit)
315
+ expect(@stderr.string).to include("Cookbook test_cookbook depends on cookbooks which are not currently")
316
+ expect(@stderr.string).to include("being uploaded and cannot be found on the server.")
317
+ expect(@stderr.string).to include("The missing cookbook(s) are:")
318
+ expect(@stderr.string).to include("'dependency' version '>= 0.0.0'")
319
+ expect(@stderr.string).to include("'dependency2' version '>= 0.0.0'")
251
320
  end
252
- allow(knife).to receive(:cookbook_names).and_return(%w{dependency dependency2 test_cookbook})
253
- expect { knife.run }.to raise_error(SystemExit)
254
- expect(@stderr.string).to include("Cookbook test_cookbook depends on cookbooks which are not currently")
255
- expect(@stderr.string).to include("being uploaded and cannot be found on the server.")
256
- expect(@stderr.string).to include("The missing cookbook(s) are:")
257
- expect(@stderr.string).to include("'dependency' version '>= 0.0.0'")
258
- expect(@stderr.string).to include("'dependency2' version '>= 0.0.0'")
259
321
  end
260
322
  end
261
323
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife
3
3
  version: !ruby/object:Gem::Version
4
- version: 17.4.18
4
+ version: 17.4.46
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Jacob
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-02 00:00:00.000000000 Z
11
+ date: 2021-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef-config
@@ -256,6 +256,20 @@ dependencies:
256
256
  - - "~>"
257
257
  - !ruby/object:Gem::Version
258
258
  version: '1.1'
259
+ - !ruby/object:Gem::Dependency
260
+ name: x25519
261
+ requirement: !ruby/object:Gem::Requirement
262
+ requirements:
263
+ - - ">="
264
+ - !ruby/object:Gem::Version
265
+ version: 1.0.9
266
+ type: :runtime
267
+ prerelease: false
268
+ version_requirements: !ruby/object:Gem::Requirement
269
+ requirements:
270
+ - - ">="
271
+ - !ruby/object:Gem::Version
272
+ version: 1.0.9
259
273
  - !ruby/object:Gem::Dependency
260
274
  name: highline
261
275
  requirement: !ruby/object:Gem::Requirement
@@ -1130,7 +1144,7 @@ licenses:
1130
1144
  - Apache-2.0
1131
1145
  metadata:
1132
1146
  bug_tracker_uri: https://github.com/chef/chef/issues
1133
- changelog_uri: https://github.com/chef/chef/blob/master/CHANGELOG.md
1147
+ changelog_uri: https://github.com/chef/chef/blob/main/CHANGELOG.md
1134
1148
  documentation_uri: https://docs.chef.io/
1135
1149
  homepage_uri: https://www.chef.io
1136
1150
  mailing_list_uri: https://discourse.chef.io/