knife 18.8.68 → 19.0.102

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +30 -9
  3. data/LICENSE +201 -201
  4. data/Rakefile +23 -0
  5. data/knife.gemspec +9 -15
  6. data/lib/chef/application/knife.rb +3 -5
  7. data/lib/chef/chef_fs/knife.rb +1 -1
  8. data/lib/chef/knife/acl_base.rb +1 -1
  9. data/lib/chef/knife/acl_bulk_add.rb +0 -1
  10. data/lib/chef/knife/acl_bulk_remove.rb +0 -1
  11. data/lib/chef/knife/bootstrap/templates/chef-full.erb +24 -5
  12. data/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb +121 -4
  13. data/lib/chef/knife/bootstrap.rb +25 -29
  14. data/lib/chef/knife/config_get.rb +1 -1
  15. data/lib/chef/knife/config_get_profile.rb +1 -1
  16. data/lib/chef/knife/config_list_profiles.rb +1 -1
  17. data/lib/chef/knife/config_show.rb +1 -1
  18. data/lib/chef/knife/config_use_profile.rb +3 -3
  19. data/lib/chef/knife/configure_client.rb +1 -1
  20. data/lib/chef/knife/cookbook_bulk_delete.rb +1 -1
  21. data/lib/chef/knife/cookbook_delete.rb +1 -1
  22. data/lib/chef/knife/cookbook_download.rb +1 -1
  23. data/lib/chef/knife/cookbook_upload.rb +1 -1
  24. data/lib/chef/knife/core/bootstrap_context.rb +36 -6
  25. data/lib/chef/knife/core/cookbook_scm_repo.rb +2 -2
  26. data/lib/chef/knife/core/cookbook_site_streaming_uploader.rb +1 -6
  27. data/lib/chef/knife/core/gem_glob_loader.rb +4 -4
  28. data/lib/chef/knife/core/object_loader.rb +1 -1
  29. data/lib/chef/knife/core/status_presenter.rb +1 -1
  30. data/lib/chef/knife/core/subcommand_loader.rb +1 -1
  31. data/lib/chef/knife/core/text_formatter.rb +0 -1
  32. data/lib/chef/knife/core/windows_bootstrap_context.rb +103 -37
  33. data/lib/chef/knife/data_bag_secret_options.rb +1 -1
  34. data/lib/chef/knife/download.rb +1 -1
  35. data/lib/chef/knife/environment_compare.rb +2 -3
  36. data/lib/chef/knife/key_create.rb +1 -5
  37. data/lib/chef/knife/key_edit.rb +1 -5
  38. data/lib/chef/knife/license.rb +6 -2
  39. data/lib/chef/knife/list.rb +1 -1
  40. data/lib/chef/knife/node_edit.rb +1 -1
  41. data/lib/chef/knife/raw.rb +1 -2
  42. data/lib/chef/knife/recipe_list.rb +1 -1
  43. data/lib/chef/knife/search.rb +2 -2
  44. data/lib/chef/knife/ssh.rb +5 -5
  45. data/lib/chef/knife/ssl_fetch.rb +1 -1
  46. data/lib/chef/knife/supermarket_share.rb +3 -2
  47. data/lib/chef/knife/upload.rb +1 -1
  48. data/lib/chef/knife/user_create.rb +11 -21
  49. data/lib/chef/knife/user_list.rb +3 -3
  50. data/lib/chef/knife/version.rb +1 -3
  51. data/lib/chef/knife/xargs.rb +0 -1
  52. data/lib/chef/knife.rb +4 -1
  53. data/lib/chef/utils/licensing_config.rb +0 -1
  54. data/lib/chef/utils/licensing_handler.rb +18 -28
  55. data/spec/data/apt/chef-integration-test-1.0/debian/rules +0 -0
  56. data/spec/data/apt/chef-integration-test-1.1/debian/rules +0 -0
  57. data/spec/data/apt/chef-integration-test2-1.0/debian/rules +0 -0
  58. data/spec/data/cookbooks/openldap/templates/default/all_windows_line_endings.erb +4 -4
  59. data/spec/data/cookbooks/openldap/templates/default/some_windows_line_endings.erb +2 -2
  60. data/spec/functional/cookbook_delete_spec.rb +4 -4
  61. data/spec/functional/version_spec.rb +2 -3
  62. data/spec/integration/config_use_spec.rb +8 -8
  63. data/spec/integration/cookbook_api_ipv6_spec.rb +2 -2
  64. data/spec/integration/node_environment_set_spec.rb +1 -1
  65. data/spec/integration/node_run_list_set_spec.rb +1 -1
  66. data/spec/knife_spec_helper.rb +0 -5
  67. data/spec/support/platforms/prof/gc.rb +1 -1
  68. data/spec/support/shared/integration/integration_helper.rb +1 -1
  69. data/spec/tiny_server.rb +2 -2
  70. data/spec/unit/knife/bootstrap/templates/windows_presigned_url_spec.rb +183 -0
  71. data/spec/unit/knife/bootstrap_chef19_spec.rb +122 -0
  72. data/spec/unit/knife/bootstrap_spec.rb +121 -7
  73. data/spec/unit/knife/configure_spec.rb +3 -3
  74. data/spec/unit/knife/cookbook_list_spec.rb +1 -1
  75. data/spec/unit/knife/core/bootstrap_context_spec.rb +137 -0
  76. data/spec/unit/knife/core/windows_bootstrap_context_chef19_spec.rb +82 -0
  77. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +51 -64
  78. data/spec/unit/knife/license_spec.rb +1 -1
  79. data/spec/unit/knife/search_spec.rb +3 -3
  80. data/spec/unit/knife/supermarket_install_spec.rb +4 -5
  81. data/spec/unit/knife/utils/licensing_handler_chef19_spec.rb +144 -0
  82. metadata +61 -100
  83. data/spec/data/gems/chef-integration-test-0.1.0.gem +0 -0
  84. data/spec/data/nodes/default.rb +0 -15
  85. data/spec/data/nodes/test.example.com.rb +0 -17
  86. data/spec/data/nodes/test.rb +0 -15
  87. data/spec/unit/utils/licensing_handler_spec.rb +0 -140
@@ -157,9 +157,9 @@ describe Chef::Knife::Configure do
157
157
  expect(::File).to receive(:open).with("/home/you/.chef/credentials", "w").and_yield config_file
158
158
  @knife.config[:repository] = "/home/you/chef-repo"
159
159
  @knife.run
160
- expect(config_file.string).to match(/^client_name\s+=\s+'#{Etc.getlogin}'$/)
161
- expect(config_file.string).to match(%r{^client_key\s+=\s+'/home/you/.chef/#{Etc.getlogin}.pem'$})
162
- expect(config_file.string).to match(/^chef_server_url\s+=\s+'#{default_server_url}'$/)
160
+ expect(config_file.string).to match(/^client_name\s+=\s+'#{Regexp.escape(Etc.getlogin)}'$/)
161
+ expect(config_file.string).to match(/^client_key\s+=\s+'#{Regexp.escape("/home/you/.chef/#{Etc.getlogin}.pem")}'$/)
162
+ expect(config_file.string).to match(/^chef_server_url\s+=\s+'#{Regexp.escape(default_server_url)}'$/)
163
163
  end
164
164
 
165
165
  it "creates a new client when given the --initial option" do
@@ -59,7 +59,7 @@ describe Chef::Knife::CookbookList do
59
59
  allow(@rest_mock).to receive(:get).and_return(@cookbook_data)
60
60
  @knife.run
61
61
  @cookbook_names.each do |item|
62
- pattern = /#{Regexp.escape(@cookbook_data[item]['versions'].first['url'])}/
62
+ pattern = /#{Regexp.escape(@cookbook_data[item]["versions"].first["url"])}/
63
63
  expect(@stdout.string).to match pattern
64
64
  end
65
65
  end
@@ -284,4 +284,141 @@ describe Chef::Knife::Core::BootstrapContext do
284
284
  end
285
285
  end
286
286
  end
287
+
288
+ describe "when using disable_license_activation" do
289
+ let(:config) { { license_id: "123", color: false } }
290
+ # it "by default it should return license" do
291
+ # expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json --no-color"
292
+ # end
293
+
294
+ it "doesn't return the chef-license-key in the start_chef command if it set" do
295
+ config[:disable_license_activation] = true
296
+ expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json --no-color"
297
+ end
298
+ end
299
+
300
+ describe "#chef_ice?" do
301
+ context "when product_to_install is chef-ice" do
302
+ let(:config) { { bootstrap_version: "19.0.0" } }
303
+
304
+ it "returns true for Chef Infra 19" do
305
+ expect(bootstrap_context.chef_ice?).to be true
306
+ end
307
+ end
308
+
309
+ context "when product_to_install is chef" do
310
+ let(:config) { { bootstrap_version: "18.0.0" } }
311
+
312
+ it "returns false for Chef Infra 18" do
313
+ expect(bootstrap_context.chef_ice?).to be false
314
+ end
315
+ end
316
+
317
+ context "when bootstrap_product is explicitly set to chef-ice" do
318
+ let(:config) { { bootstrap_product: "chef-ice" } }
319
+
320
+ it "returns true" do
321
+ expect(bootstrap_context.chef_ice?).to be true
322
+ end
323
+ end
324
+
325
+ context "when bootstrap_product is explicitly set to chef" do
326
+ let(:config) { { bootstrap_product: "chef" } }
327
+
328
+ it "returns false" do
329
+ expect(bootstrap_context.chef_ice?).to be false
330
+ end
331
+ end
332
+ end
333
+
334
+ describe "#product_to_install" do
335
+ context "when bootstrap_product is explicitly set" do
336
+ let(:config) { { bootstrap_product: "chef-ice" } }
337
+
338
+ it "returns the explicitly set product" do
339
+ expect(bootstrap_context.product_to_install).to eq "chef-ice"
340
+ end
341
+ end
342
+
343
+ context "when bootstrap_version is latest" do
344
+ let(:config) { { channel: "unstable" } }
345
+
346
+ it "returns chef-ice for latest version" do
347
+ expect(bootstrap_context.product_to_install).to eq "chef-ice"
348
+ end
349
+ end
350
+
351
+ context "when bootstrap_version starts with 19" do
352
+ let(:config) { { bootstrap_version: "19.0.0" } }
353
+
354
+ it "returns chef-ice for version 19.x.x" do
355
+ expect(bootstrap_context.product_to_install).to eq "chef-ice"
356
+ end
357
+ end
358
+
359
+ context "when bootstrap_version is older than 19" do
360
+ let(:config) { { bootstrap_version: "18.4.0" } }
361
+
362
+ it "returns chef for older versions" do
363
+ expect(bootstrap_context.product_to_install).to eq "chef"
364
+ end
365
+ end
366
+
367
+ context "when no version is specified and channel is stable" do
368
+ let(:config) { { channel: "stable" } }
369
+
370
+ it "returns chef-ice if current Chef version is 19+" do
371
+ # Assuming Chef::VERSION starts with "19"
372
+ if Chef::VERSION.split(".").first.to_i >= 19
373
+ expect(bootstrap_context.product_to_install).to eq "chef-ice"
374
+ else
375
+ expect(bootstrap_context.product_to_install).to eq "chef"
376
+ end
377
+ end
378
+ end
379
+ end
380
+
381
+ describe "#start_chef with licensing" do
382
+ context "when license is required and license_key is provided" do
383
+ let(:config) { { bootstrap_version: "19.0.0", license_key: "test-license-key" } }
384
+
385
+ it "includes license environment variable in start_chef command" do
386
+ expected = 'CHEF_LICENSE_KEY="test-license-key" chef-client -j /etc/chef/first-boot.json'
387
+ expect(bootstrap_context.start_chef).to eq expected
388
+ end
389
+ end
390
+
391
+ context "when license is required and license_id is provided" do
392
+ let(:config) { { bootstrap_version: "19.0.0", license_id: "test-license-id" } }
393
+
394
+ it "includes license environment variable with license_id in start_chef command" do
395
+ expected = 'CHEF_LICENSE_KEY="test-license-id" chef-client -j /etc/chef/first-boot.json'
396
+ expect(bootstrap_context.start_chef).to eq expected
397
+ end
398
+ end
399
+
400
+ context "when license is required but no license key/id provided" do
401
+ let(:config) { { bootstrap_version: "19.0.0" } }
402
+
403
+ it "does not include license environment variable" do
404
+ expected = "chef-client -j /etc/chef/first-boot.json"
405
+ expect(bootstrap_context.start_chef).to eq expected
406
+ end
407
+ end
408
+
409
+ context "when license is not required" do
410
+ let(:config) { { bootstrap_version: "18.0.0", license_key: "test-license-key" } }
411
+
412
+ it "does not include license environment variable even if license key provided" do
413
+ expected = "chef-client -j /etc/chef/first-boot.json"
414
+ expect(bootstrap_context.start_chef).to eq expected
415
+ end
416
+ end
417
+ end
418
+
419
+ describe "#detect_package_manager" do
420
+ it "returns empty string for base implementation" do
421
+ expect(bootstrap_context.detect_package_manager).to eq ""
422
+ end
423
+ end
287
424
  end
@@ -0,0 +1,82 @@
1
+ #
2
+ # Copyright:: Copyright (c) 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 "knife_spec_helper"
19
+ require "chef/knife/core/windows_bootstrap_context"
20
+
21
+ describe Chef::Knife::Core::WindowsBootstrapContext do
22
+ let(:config) { { foo: :bar, color: true } }
23
+ let(:run_list) { Chef::RunList.new("recipe[tmux]", "role[base]") }
24
+ let(:chef_config) do
25
+ {
26
+ config_log_level: "info",
27
+ config_log_location: "/tmp/log",
28
+ validation_key: File.join(CHEF_SPEC_DATA, "ssl", "private_key.pem"),
29
+ chef_server_url: "http://chef.example.com:4444",
30
+ validation_client_name: "chef-validator-testing",
31
+ }
32
+ end
33
+
34
+ let(:secret) { nil }
35
+
36
+ subject(:bootstrap_context) { described_class.new(config, run_list, chef_config, secret) }
37
+
38
+ describe "Chef Infra 19 licensing support" do
39
+
40
+ describe "install_command with licensing" do
41
+ let(:executor_quote) { '"' }
42
+
43
+ context "when license is required and license_key is provided" do
44
+ let(:config) { { bootstrap_version: "19.0.0", license_key: "test-license-key" } }
45
+
46
+ it "includes license environment variable in install command" do
47
+ command = bootstrap_context.send(:install_command, executor_quote)
48
+ expect(command).to include("set CHEF_LICENSE_KEY=test-license-key")
49
+ expect(command).to include("msiexec /qn")
50
+ end
51
+ end
52
+
53
+ context "when license is required and license_id is provided" do
54
+ let(:config) { { bootstrap_version: "19.0.0", license_id: "test-license-id" } }
55
+
56
+ it "includes license environment variable with license_id in install command" do
57
+ command = bootstrap_context.send(:install_command, executor_quote)
58
+ expect(command).to include("set CHEF_LICENSE_KEY=test-license-id")
59
+ end
60
+ end
61
+
62
+ context "when license is required but no license key/id provided" do
63
+ let(:config) { { bootstrap_version: "19.0.0" } }
64
+
65
+ it "does not include license environment variable" do
66
+ command = bootstrap_context.send(:install_command, executor_quote)
67
+ expect(command).not_to include("CHEF_LICENSE_KEY")
68
+ expect(command).to include("msiexec /qn")
69
+ end
70
+ end
71
+
72
+ context "when license is not required" do
73
+ let(:config) { { bootstrap_version: "18.0.0", license_key: "test-license-key" } }
74
+
75
+ it "does not include license environment variable even if license key provided" do
76
+ command = bootstrap_context.send(:install_command, executor_quote)
77
+ expect(command).not_to include("CHEF_LICENSE_KEY")
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -186,39 +186,70 @@ describe Chef::Knife::Core::WindowsBootstrapContext do
186
186
  end
187
187
 
188
188
  describe "#start_chef" do
189
- it "returns the expected string" do
189
+ # These tests are for Chef 18 behavior, so we need to specify version 18
190
+ let(:config) { { bootstrap_version: "18" } }
191
+
192
+ it "returns the expected string with default values" do
193
+ expect(bootstrap_context.start_chef).to eq(
194
+ <<~EOH
195
+ SET "PATH=%SYSTEM32%;%SystemRoot%;%SYSTEM32%\\Wbem;%SYSTEM32%\\WindowsPowerShell\\v1.0\\;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin;C:\\hab\\bin;%PATH%"
196
+ chef-client -c C:\\chef\\client.rb -j C:\\chef\\first-boot.json
197
+ EOH
198
+ )
199
+ end
200
+
201
+ it "returns the expected string with license_id" do
202
+ config[:license_id] = "123"
203
+
204
+ expect(bootstrap_context.start_chef).to eq(
205
+ <<~EOH
206
+ SET "PATH=%SYSTEM32%;%SystemRoot%;%SYSTEM32%\\Wbem;%SYSTEM32%\\WindowsPowerShell\\v1.0\\;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin;C:\\hab\\bin;%PATH%"
207
+ chef-client -c C:\\chef\\client.rb -j C:\\chef\\first-boot.json
208
+ EOH
209
+ )
210
+ end
211
+
212
+ it "exclude the license key argument when disable_license_activation is true" do
213
+ config[:disable_license_activation] = true
214
+ config[:license_id] = "123"
215
+
190
216
  expect(bootstrap_context.start_chef).to eq(
191
217
  <<~EOH
192
- SET "PATH=%SYSTEM32%;%SystemRoot%;%SYSTEM32%\\Wbem;%SYSTEM32%\\WindowsPowerShell\\v1.0\\;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin;%PATH%"
218
+ SET "PATH=%SYSTEM32%;%SystemRoot%;%SYSTEM32%\\Wbem;%SYSTEM32%\\WindowsPowerShell\\v1.0\\;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin;C:\\hab\\bin;%PATH%"
193
219
  chef-client -c C:\\chef\\client.rb -j C:\\chef\\first-boot.json
194
220
  EOH
195
221
  )
196
222
  end
197
223
  end
198
224
 
225
+ describe "#start_chef with bootstrap_url and chef-ice" do
226
+ let(:config) { { bootstrap_url: "https://example.com/install.ps1" } }
227
+
228
+ before do
229
+ allow(bootstrap_context).to receive(:chef_ice?).and_return(true)
230
+ end
231
+
232
+ it "includes HAB_LICENSE and uses hab pkg exec" do
233
+ expect(bootstrap_context.start_chef).to eq(
234
+ <<~EOH
235
+ SET "PATH=%SYSTEM32%;%SystemRoot%;%SYSTEM32%\\Wbem;%SYSTEM32%\\WindowsPowerShell\\v1.0\\;C:\\hab\\bin;%PATH%"
236
+ SET "HAB_LICENSE=accept-no-persist"
237
+ hab pkg exec chef/chef-infra-client chef-client -c C:\\chef\\client.rb -j C:\\chef\\first-boot.json
238
+ EOH
239
+ )
240
+ end
241
+ end
242
+
199
243
  describe "msi_url" do
200
244
  context "when msi_url config option is not set" do
201
245
  let(:config) { { channel: "stable" } }
202
- before do
203
- expect(bootstrap_context).to receive(:version_to_install).and_return("something")
204
- end
205
246
 
206
- it "returns a chef.io msi url with minimal url parameters" do
207
- reference_url = "https://omnitruck.chef.io/chef/download?p=windows&channel=stable&v=something"
208
- expect(bootstrap_context.msi_url).to eq(reference_url)
247
+ it "returns nil when no msi_url is provided" do
248
+ expect(bootstrap_context.msi_url).to be_nil
209
249
  end
210
250
 
211
- it "returns a chef.io msi url with provided url parameters substituted" do
212
- reference_url = "https://omnitruck.chef.io/chef/download?p=windows&channel=stable&pv=machine&m=arch&DownloadContext=ctx&v=something"
213
- expect(bootstrap_context.msi_url("machine", "arch", "ctx")).to eq(reference_url)
214
- end
215
-
216
- context "when a channel is provided in config" do
217
- let(:config) { { channel: "current" } }
218
- it "returns a chef.io msi url with the requested channel" do
219
- reference_url = "https://omnitruck.chef.io/chef/download?p=windows&channel=current&v=something"
220
- expect(bootstrap_context.msi_url).to eq(reference_url)
221
- end
251
+ it "returns nil even with parameters when no msi_url is provided" do
252
+ expect(bootstrap_context.msi_url).to be_nil
222
253
  end
223
254
  end
224
255
 
@@ -231,51 +262,7 @@ describe Chef::Knife::Core::WindowsBootstrapContext do
231
262
  end
232
263
 
233
264
  it "doesn't introduce any unnecessary query parameters if provided by the template" do
234
- expect(bootstrap_context.msi_url("machine", "arch", "ctx")).to eq(custom_url)
235
- end
236
- end
237
-
238
- context "with licensing changes" do
239
- let(:config) { { channel: "stable", bootstrap_version: "latest" } }
240
-
241
- it "should return the old omnitruck api in case license not present" do
242
- nil_license_obj = Chef::Utils::LicensingHandler.new(nil, nil)
243
- expect(Chef::Utils::LicensingHandler).to receive(:validate!).and_return(nil_license_obj)
244
- b = Chef::Knife::Bootstrap.new([])
245
- b.send(:fetch_license)
246
- config.merge!(b.config)
247
-
248
- expect(bootstrap_context.msi_url).to eq("https://omnitruck.chef.io/chef/download?p=windows&channel=stable&v=latest")
249
- end
250
-
251
- it "should return the correct msi_url for trial license" do
252
- license_obj_trial = Chef::Utils::LicensingHandler.new("key-trial-123", "trial")
253
- expect(Chef::Utils::LicensingHandler).to receive(:validate!).and_return(license_obj_trial)
254
-
255
- b = Chef::Knife::Bootstrap.new([])
256
- b.send(:fetch_license)
257
- config.merge!(b.config)
258
- expect(bootstrap_context.msi_url).to eq("https://chefdownload-trial.chef.io/stable/chef/download?license_id=key-trial-123&p=windows&v=latest")
259
- end
260
-
261
- it "should return the correct msi_url for commercial license" do
262
- license_obj_commercial = Chef::Utils::LicensingHandler.new("key-commercial-123", "commercial")
263
- expect(Chef::Utils::LicensingHandler).to receive(:validate!).and_return(license_obj_commercial)
264
-
265
- b = Chef::Knife::Bootstrap.new([])
266
- b.send(:fetch_license)
267
- config.merge!(b.config)
268
- expect(bootstrap_context.msi_url).to eq("https://chefdownload-commerical.chef.io/stable/chef/download?license_id=key-commercial-123&p=windows&v=latest")
269
- end
270
-
271
- it "should return the omnitruck url in case of airgapped env" do
272
- # In case of connection issues with the licensing service, the library will raise this exception
273
- expect(ChefLicensing::LicenseKeyFetcher).to receive(:fetch).and_raise(ChefLicensing::RestfulClientConnectionError)
274
-
275
- b = Chef::Knife::Bootstrap.new([])
276
- b.send(:fetch_license)
277
- config.merge!(b.config)
278
- expect(bootstrap_context.msi_url).to eq("https://omnitruck.chef.io/chef/download?p=windows&channel=stable&v=latest")
265
+ expect(bootstrap_context.msi_url).to eq(custom_url)
279
266
  end
280
267
  end
281
268
  end
@@ -92,4 +92,4 @@ describe Chef::Knife::License do
92
92
  end
93
93
  end
94
94
  end
95
- end
95
+ end
@@ -59,14 +59,14 @@ describe Chef::Knife::Search do
59
59
  knife.config[:query] = ":"
60
60
 
61
61
  expect { knife.run }.to raise_error(SystemExit)
62
- expect(stderr.string).to match /Please specify query as an argument or an option via -q, not both/im
62
+ expect(stderr.string).to match(/Please specify query as an argument or an option via -q, not both/im)
63
63
  end
64
64
 
65
65
  it "should fail if no query passed" do
66
66
  knife.name_args = []
67
67
 
68
68
  expect { knife.run }.to raise_error(SystemExit)
69
- expect(stderr.string).to match /No query specified/im
69
+ expect(stderr.string).to match(/No query specified/im)
70
70
  end
71
71
  end
72
72
 
@@ -145,4 +145,4 @@ describe Chef::Knife::Search do
145
145
  end
146
146
  end
147
147
  end
148
- end
148
+ end
@@ -28,8 +28,8 @@ describe Chef::Knife::SupermarketInstall do
28
28
  let(:archive) { double(Mixlib::Archive, extract: true) }
29
29
  let(:repo) do
30
30
  double(sanity_check: true, reset_to_default_state: true,
31
- prepare_to_import: true, finalize_updates_to: true,
32
- merge_updates_from: true)
31
+ prepare_to_import: true, finalize_updates_to: true,
32
+ merge_updates_from: true)
33
33
  end
34
34
  let(:install_path) do
35
35
  if ChefUtils.windows?
@@ -137,7 +137,6 @@ describe Chef::Knife::SupermarketInstall do
137
137
  knife.name_args = ["getting-started"]
138
138
  knife.config[:use_current_branch] = true
139
139
  knife.config[:no_deps] = true
140
- upstream_file = File.join(install_path, "getting-started.tar.gz")
141
140
  expect(repo).not_to receive(:prepare_to_import)
142
141
  expect(repo).not_to receive(:reset_to_default_state)
143
142
  knife.run
@@ -192,11 +191,11 @@ describe Chef::Knife::SupermarketInstall do
192
191
  end
193
192
 
194
193
  it "rasies an error if it finds no metadata file" do
195
- expect { knife.preferred_metadata }.to raise_error { |error|
194
+ expect { knife.preferred_metadata }.to(raise_error { |error|
196
195
  expect(error).to be_a(Chef::Exceptions::MetadataNotFound)
197
196
  expect(error.cookbook_name).to eq("post-punk-kitchen")
198
197
  expect(error.install_path).to eq(install_path)
199
- }
198
+ })
200
199
  end
201
200
 
202
201
  end
@@ -0,0 +1,144 @@
1
+ #
2
+ # Copyright:: Copyright (c) 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 "knife_spec_helper"
19
+ require "chef/utils/licensing_handler"
20
+
21
+ describe Chef::Utils::LicensingHandler do
22
+ let(:license_key) { "test-license-key" }
23
+ let(:license_type) { "trial" }
24
+ let(:handler) { described_class.new(license_key, license_type) }
25
+
26
+ describe "#initialize" do
27
+ it "sets license_key and license_type" do
28
+ expect(handler.license_key).to eq(license_key)
29
+ expect(handler.license_type).to eq(license_type)
30
+ end
31
+ end
32
+
33
+ describe "#omnitruck_url" do
34
+ context "with trial license" do
35
+ let(:license_type) { "trial" }
36
+
37
+ it "returns chefdownload-trial.chef.io URL with license_id parameter" do
38
+ expected_url = "https://chefdownload-trial.chef.io/%s?license_id=#{license_key}"
39
+ expect(handler.omnitruck_url).to eq(expected_url)
40
+ end
41
+ end
42
+
43
+ context "with free license" do
44
+ let(:license_type) { "free" }
45
+
46
+ it "returns chefdownload-trial.chef.io URL with license_id parameter" do
47
+ expected_url = "https://chefdownload-trial.chef.io/%s?license_id=#{license_key}"
48
+ expect(handler.omnitruck_url).to eq(expected_url)
49
+ end
50
+ end
51
+
52
+ context "with commercial license" do
53
+ let(:license_type) { "commercial" }
54
+
55
+ it "returns commercial download URL with license_id parameter" do
56
+ expected_url = "https://chefdownload-commercial.chef.io/%s?license_id=#{license_key}"
57
+ expect(handler.omnitruck_url).to eq(expected_url)
58
+ end
59
+ end
60
+ end
61
+
62
+ describe "#install_sh_url" do
63
+ it "returns formatted URL for install.sh" do
64
+ expected_url = handler.omnitruck_url.gsub("%s", "install.sh")
65
+ expect(handler.install_sh_url).to eq(expected_url)
66
+ end
67
+ end
68
+
69
+ describe "DOWNLOAD_URLS constant" do
70
+ it "has correct URLs for each license type" do
71
+ expect(described_class::DOWNLOAD_URLS["free"]).to eq("https://chefdownload-trial.chef.io")
72
+ expect(described_class::DOWNLOAD_URLS["trial"]).to eq("https://chefdownload-trial.chef.io")
73
+ expect(described_class::DOWNLOAD_URLS["commercial"]).to eq("https://chefdownload-commercial.chef.io")
74
+ end
75
+ end
76
+
77
+ describe ".validate!" do
78
+ let(:license_metadata) { double("license_metadata", id: "test-id", license_type: "trial") }
79
+ let(:licenses_metadata) { double("licenses_metadata", last: license_metadata) }
80
+
81
+ before do
82
+ allow(ChefLicensing).to receive(:fetch_and_persist).and_return(["test-license"])
83
+ allow(ChefLicensing::Api::Describe).to receive(:list).and_return(licenses_metadata)
84
+ end
85
+
86
+ it "validates license and returns handler instance" do
87
+ result = described_class.validate!
88
+
89
+ expect(result).to be_a(described_class)
90
+ expect(result.license_key).to eq("test-id")
91
+ expect(result.license_type).to eq("trial")
92
+ end
93
+
94
+ it "fetches licenses from licensing service" do
95
+ expect(ChefLicensing).to receive(:fetch_and_persist).and_return(["test-license"])
96
+
97
+ described_class.validate!
98
+ end
99
+
100
+ it "describes licenses with license keys" do
101
+ expect(ChefLicensing::Api::Describe).to receive(:list).with(
102
+ hash_including(license_keys: ["test-license"])
103
+ )
104
+
105
+ described_class.validate!
106
+ end
107
+ end
108
+
109
+ describe ".check_software_entitlement!" do
110
+ let(:ui) { double("ui") }
111
+
112
+ context "when entitlement check succeeds" do
113
+ it "calls ChefLicensing.check_software_entitlement!" do
114
+ expect(ChefLicensing).to receive(:check_software_entitlement!)
115
+
116
+ described_class.check_software_entitlement!(ui)
117
+ end
118
+ end
119
+
120
+ context "when software is not entitled" do
121
+ before do
122
+ allow(ChefLicensing).to receive(:check_software_entitlement!).and_raise(ChefLicensing::SoftwareNotEntitled)
123
+ end
124
+
125
+ it "displays error message and exits" do
126
+ expect(ui).to receive(:error).with("License is not entitled to use Workstation.")
127
+ expect { described_class.check_software_entitlement!(ui) }.to raise_error(SystemExit)
128
+ end
129
+ end
130
+
131
+ context "when licensing error occurs" do
132
+ let(:licensing_error) { ChefLicensing::Error.new("Test error") }
133
+
134
+ before do
135
+ allow(ChefLicensing).to receive(:check_software_entitlement!).and_raise(licensing_error)
136
+ end
137
+
138
+ it "displays error message and exits" do
139
+ expect(ui).to receive(:error).with("Test error")
140
+ expect { described_class.check_software_entitlement!(ui) }.to raise_error(SystemExit)
141
+ end
142
+ end
143
+ end
144
+ end