chef 13.5.3 → 13.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/chef-shell +2 -0
  4. data/distro/powershell/chef/chef.psm1 +23 -2
  5. data/lib/chef/application/client.rb +1 -1
  6. data/lib/chef/chef_fs/config.rb +1 -1
  7. data/lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb +5 -0
  8. data/lib/chef/client.rb +3 -0
  9. data/lib/chef/cookbook/synchronizer.rb +2 -4
  10. data/lib/chef/data_bag.rb +4 -0
  11. data/lib/chef/deprecated.rb +10 -0
  12. data/lib/chef/knife/client_delete.rb +1 -1
  13. data/lib/chef/knife/node_delete.rb +1 -1
  14. data/lib/chef/knife/node_run_list_add.rb +1 -1
  15. data/lib/chef/knife/node_run_list_remove.rb +1 -1
  16. data/lib/chef/knife/role_env_run_list_add.rb +1 -1
  17. data/lib/chef/knife/role_run_list_add.rb +1 -1
  18. data/lib/chef/mixin/user_context.rb +1 -1
  19. data/lib/chef/platform/rebooter.rb +7 -3
  20. data/lib/chef/provider/dsc_script.rb +5 -1
  21. data/lib/chef/provider/package.rb +35 -4
  22. data/lib/chef/provider/package/apt.rb +7 -0
  23. data/lib/chef/provider/package/chocolatey.rb +24 -8
  24. data/lib/chef/provider/package/dnf.rb +14 -5
  25. data/lib/chef/provider/package/dnf/dnf_helper.py +10 -0
  26. data/lib/chef/provider/package/dnf/python_helper.rb +15 -0
  27. data/lib/chef/provider/package/rpm.rb +5 -0
  28. data/lib/chef/provider/package/windows.rb +15 -0
  29. data/lib/chef/provider/package/yum.rb +4 -20
  30. data/lib/chef/provider/package/zypper.rb +5 -1
  31. data/lib/chef/provider/route.rb +1 -1
  32. data/lib/chef/provider/windows_task.rb +19 -4
  33. data/lib/chef/provider/zypper_repository.rb +100 -12
  34. data/lib/chef/resource/deploy.rb +6 -0
  35. data/lib/chef/resource/dnf_package.rb +9 -2
  36. data/lib/chef/resource/windows_task.rb +12 -7
  37. data/lib/chef/resource/zypper_package.rb +1 -0
  38. data/lib/chef/resource/zypper_repository.rb +1 -0
  39. data/lib/chef/util/dsc/lcm_output_parser.rb +57 -2
  40. data/lib/chef/util/dsc/local_configuration_manager.rb +16 -16
  41. data/lib/chef/version.rb +1 -1
  42. data/spec/functional/rebooter_spec.rb +22 -11
  43. data/spec/functional/resource/windows_task_spec.rb +25 -0
  44. data/spec/unit/chef_fs/data_handler/data_bag_item_data_handler.rb +79 -0
  45. data/spec/unit/client_spec.rb +11 -0
  46. data/spec/unit/cookbook/synchronizer_spec.rb +30 -0
  47. data/spec/unit/knife/data_bag_create_spec.rb +8 -0
  48. data/spec/unit/mixin/user_context_spec.rb +1 -2
  49. data/spec/unit/provider/package/chocolatey_spec.rb +3 -1
  50. data/spec/unit/provider/package/rubygems_spec.rb +5 -0
  51. data/spec/unit/provider/package/zypper_spec.rb +8 -0
  52. data/spec/unit/provider/remote_file/network_file_spec.rb +1 -1
  53. data/spec/unit/provider/route_spec.rb +2 -0
  54. data/spec/unit/provider/windows_task_spec.rb +28 -0
  55. data/spec/unit/provider/zypper_repository_spec.rb +124 -0
  56. data/spec/unit/provider_resolver_spec.rb +4 -1
  57. data/spec/unit/resource/windows_task_spec.rb +8 -2
  58. data/spec/unit/resource/zypper_repository_spec.rb +11 -9
  59. data/spec/unit/util/dsc/lcm_output_parser_spec.rb +102 -18
  60. data/spec/unit/util/dsc/local_configuration_manager_spec.rb +2 -1
  61. metadata +6 -4
@@ -39,6 +39,7 @@ class Chef
39
39
  property :mode, default: "0644"
40
40
  property :refresh_cache, [true, false], default: true
41
41
  property :source, String, regex: /.*/
42
+ property :cookbook, String
42
43
  property :gpgautoimportkeys, [true, false], default: true
43
44
 
44
45
  default_action :create
@@ -28,7 +28,7 @@ class Chef
28
28
  # Parses the output from LCM and returns a list of Chef::Util::DSC::ResourceInfo objects
29
29
  # that describe how the resources affected the system
30
30
  #
31
- # Example:
31
+ # Example for WhatIfParser:
32
32
  # parse <<-EOF
33
33
  # What if: [Machine]: LCM: [Start Set ]
34
34
  # What if: [Machine]: LCM: [Start Resource ] [[File]FileToNotBeThere]
@@ -53,7 +53,62 @@ class Chef
53
53
  # )
54
54
  # ]
55
55
  #
56
- def self.parse(lcm_output)
56
+ # Example for TestDSCParser:
57
+ # parse <<-EOF
58
+ # InDesiredState : False
59
+ # ResourcesInDesiredState :
60
+ # ResourcesNotInDesiredState: {[Environment]texteditor}
61
+ # ReturnValue : 0
62
+ # PSComputerName : .
63
+ # EOF
64
+ #
65
+ # would return
66
+ #
67
+ # [
68
+ # Chef::Util::DSC::ResourceInfo.new(
69
+ # '{[Environment]texteditor}',
70
+ # true,
71
+ # [
72
+ # ]
73
+ # )
74
+ # ]
75
+ #
76
+
77
+ def self.parse(lcm_output, test_dsc_configuration)
78
+ test_dsc_configuration ? test_dsc_parser(lcm_output) : what_if_parser(lcm_output)
79
+ end
80
+
81
+ def self.test_dsc_parser(lcm_output)
82
+ lcm_output ||= ""
83
+ current_resource = Hash.new
84
+
85
+ resources = []
86
+ lcm_output.lines.each do |line|
87
+ op_action , op_value = line.strip.split(":")
88
+ op_action&.strip!
89
+ case op_action
90
+ when "InDesiredState"
91
+ current_resource[:skipped] = op_value.strip == "True" ? true : false
92
+ when "ResourcesInDesiredState"
93
+ current_resource[:name] = op_value.strip if op_value
94
+ when "ResourcesNotInDesiredState"
95
+ current_resource[:name] = op_value.strip if op_value
96
+ when "ReturnValue"
97
+ current_resource[:context] = nil
98
+ end
99
+ end
100
+ if current_resource[:name]
101
+ resources.push(current_resource)
102
+ end
103
+
104
+ if resources.length > 0
105
+ build_resource_info(resources)
106
+ else
107
+ raise Chef::Exceptions::LCMParser, "Could not parse:\n#{lcm_output}"
108
+ end
109
+ end
110
+
111
+ def self.what_if_parser(lcm_output)
57
112
  lcm_output ||= ""
58
113
  current_resource = Hash.new
59
114
 
@@ -29,7 +29,7 @@ class Chef::Util::DSC
29
29
 
30
30
  def test_configuration(configuration_document, shellout_flags)
31
31
  status = run_configuration_cmdlet(configuration_document, false, shellout_flags)
32
- log_what_if_exception(status.stderr) unless status.succeeded?
32
+ log_dsc_exception(status.stderr) unless status.succeeded?
33
33
  configuration_update_required?(status.return_value)
34
34
  end
35
35
 
@@ -77,7 +77,7 @@ class Chef::Util::DSC
77
77
  ps4_base_command
78
78
  else
79
79
  if ps_version_gte_5?
80
- "#{common_command_prefix} Test-DscConfiguration -path #{@configuration_path}"
80
+ "#{common_command_prefix} Test-DscConfiguration -path #{@configuration_path} | format-list"
81
81
  else
82
82
  ps4_base_command + " -whatif; if (! $?) { exit 1 }"
83
83
  end
@@ -88,31 +88,31 @@ class Chef::Util::DSC
88
88
  Chef::Platform.supported_powershell_version?(@node, 5)
89
89
  end
90
90
 
91
- def log_what_if_exception(what_if_exception_output)
92
- if whatif_not_supported?(what_if_exception_output)
91
+ def log_dsc_exception(dsc_exception_output)
92
+ if whatif_not_supported?(dsc_exception_output)
93
93
  # LCM returns an error if any of the resources do not support the opptional What-If
94
94
  Chef::Log.warn("Received error while testing configuration due to resource not supporting 'WhatIf'")
95
- elsif dsc_module_import_failure?(what_if_exception_output)
96
- Chef::Log.warn("Received error while testing configuration due to a module for an imported resource possibly not being fully installed:\n#{what_if_exception_output.gsub(/\s+/, ' ')}")
95
+ elsif dsc_module_import_failure?(dsc_exception_output)
96
+ Chef::Log.warn("Received error while testing configuration due to a module for an imported resource possibly not being fully installed:\n#{dsc_exception_output.gsub(/\s+/, ' ')}")
97
97
  else
98
- Chef::Log.warn("Received error while testing configuration:\n#{what_if_exception_output.gsub(/\s+/, ' ')}")
98
+ Chef::Log.warn("Received error while testing configuration:\n#{dsc_exception_output.gsub(/\s+/, ' ')}")
99
99
  end
100
100
  end
101
101
 
102
- def whatif_not_supported?(what_if_exception_output)
103
- !! (what_if_exception_output.gsub(/[\r\n]+/, "").gsub(/\s+/, " ") =~ /A parameter cannot be found that matches parameter name 'Whatif'/i)
102
+ def whatif_not_supported?(dsc_exception_output)
103
+ !! (dsc_exception_output.gsub(/[\r\n]+/, "").gsub(/\s+/, " ") =~ /A parameter cannot be found that matches parameter name 'Whatif'/i)
104
104
  end
105
105
 
106
- def dsc_module_import_failure?(what_if_output)
107
- !! (what_if_output =~ /\sCimException/ &&
108
- what_if_output =~ /ProviderOperationExecutionFailure/ &&
109
- what_if_output =~ /\smodule\s+is\s+installed/)
106
+ def dsc_module_import_failure?(command_output)
107
+ !! (command_output =~ /\sCimException/ &&
108
+ command_output =~ /ProviderOperationExecutionFailure/ &&
109
+ command_output =~ /\smodule\s+is\s+installed/)
110
110
  end
111
111
 
112
- def configuration_update_required?(what_if_output)
113
- Chef::Log.debug("DSC: DSC returned the following '-whatif' output from test operation:\n#{what_if_output}")
112
+ def configuration_update_required?(command_output)
113
+ Chef::Log.debug("DSC: DSC returned the following '-whatif' output from test operation:\n#{command_output}")
114
114
  begin
115
- Parser.parse(what_if_output)
115
+ Parser.parse(command_output, ps_version_gte_5?)
116
116
  rescue Chef::Exceptions::LCMParser => e
117
117
  Chef::Log.warn("Could not parse LCM output: #{e}")
118
118
  [Chef::Util::DSC::ResourceInfo.new("Unknown DSC Resources", true, ["Unknown changes because LCM output was not parsable."])]
@@ -23,7 +23,7 @@ require "chef/version_string"
23
23
 
24
24
  class Chef
25
25
  CHEF_ROOT = File.expand_path("../..", __FILE__)
26
- VERSION = Chef::VersionString.new("13.5.3")
26
+ VERSION = Chef::VersionString.new("13.6.0")
27
27
  end
28
28
 
29
29
  #
@@ -35,8 +35,9 @@ describe Chef::Platform::Rebooter do
35
35
  resource
36
36
  end
37
37
 
38
+ let(:node) { Chef::Node.new }
39
+
38
40
  let(:run_context) do
39
- node = Chef::Node.new
40
41
  events = Chef::EventDispatch::Dispatcher.new
41
42
  Chef::RunContext.new(node, {}, events)
42
43
  end
@@ -44,7 +45,8 @@ describe Chef::Platform::Rebooter do
44
45
  let(:expected) do
45
46
  {
46
47
  :windows => "#{ENV['SYSTEMROOT']}/System32/shutdown.exe /r /t 300 /c \"rebooter spec test\"",
47
- :linux => 'shutdown -r +5 "rebooter spec test"',
48
+ :linux => 'shutdown -r +5 "rebooter spec test" &',
49
+ :solaris => 'shutdown -i6 -g5 -y "rebooter spec test" &',
48
50
  }
49
51
  end
50
52
 
@@ -69,8 +71,9 @@ describe Chef::Platform::Rebooter do
69
71
  end
70
72
 
71
73
  shared_context "test a reboot method" do
72
- def test_rebooter_method(method_sym, is_windows, expected_reboot_str)
74
+ def test_rebooter_method(method_sym, is_windows, is_solaris, expected_reboot_str)
73
75
  allow(ChefConfig).to receive(:windows?).and_return(is_windows)
76
+ node.automatic["os"] = node.automatic["platform"] = node.automatic["platform_family"] = "solaris2" if is_solaris
74
77
  expect(rebooter).to receive(:shell_out!).once.with(expected_reboot_str)
75
78
  expect(rebooter).to receive(:raise).with(Chef::Exceptions::Reboot)
76
79
  expect(rebooter).to receive(method_sym).once.and_call_original
@@ -81,24 +84,32 @@ describe Chef::Platform::Rebooter do
81
84
  describe "when using #reboot_if_needed!" do
82
85
  include_context "test a reboot method"
83
86
 
84
- it "should produce the correct string on Windows", :windows_only do
85
- test_rebooter_method(:reboot_if_needed!, true, expected[:windows])
87
+ it "should produce the correct string on Windows" do
88
+ test_rebooter_method(:reboot_if_needed!, true, false, expected[:windows])
89
+ end
90
+
91
+ it "should produce a SysV-like shutdown on solaris" do
92
+ test_rebooter_method(:reboot_if_needed!, false, true, expected[:solaris])
86
93
  end
87
94
 
88
- it "should produce the correct (Linux-specific) string on non-Windows" do
89
- test_rebooter_method(:reboot_if_needed!, false, expected[:linux])
95
+ it "should produce a BSD-like shutdown by default" do
96
+ test_rebooter_method(:reboot_if_needed!, false, false, expected[:linux])
90
97
  end
91
98
  end
92
99
 
93
100
  describe "when using #reboot!" do
94
101
  include_context "test a reboot method"
95
102
 
96
- it "should produce the correct string on Windows", :windows_only do
97
- test_rebooter_method(:reboot!, true, expected[:windows])
103
+ it "should produce the correct string on Windows" do
104
+ test_rebooter_method(:reboot!, true, false, expected[:windows])
105
+ end
106
+
107
+ it "should produce a SysV-like shutdown on solaris" do
108
+ test_rebooter_method(:reboot!, false, true, expected[:solaris])
98
109
  end
99
110
 
100
- it "should produce the correct (Linux-specific) string on non-Windows" do
101
- test_rebooter_method(:reboot!, false, expected[:linux])
111
+ it "should produce a BSD-like shutdown by default" do
112
+ test_rebooter_method(:reboot!, false, false, expected[:linux])
102
113
  end
103
114
  end
104
115
  end
@@ -163,6 +163,31 @@ describe Chef::Resource::WindowsTask, :windows_only do
163
163
  end
164
164
  end
165
165
 
166
+ context "frequency :none" do
167
+ subject do
168
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
169
+ new_resource.command task_name
170
+ new_resource.run_level :highest
171
+ new_resource.frequency :none
172
+ new_resource.random_delay ""
173
+ new_resource
174
+ end
175
+
176
+ it "creates the scheduled task to run on demand only" do
177
+ subject.run_action(:create)
178
+ task_details = windows_task_provider.send(:load_task_hash, task_name)
179
+
180
+ expect(task_details[:TaskName]).to eq("\\chef-client")
181
+ expect(task_details[:TaskToRun]).to eq("chef-client")
182
+ expect(task_details[:ScheduleType]).to eq("On demand only")
183
+ expect(task_details[:StartTime]).to eq("N/A")
184
+ expect(task_details[:StartDate]).to eq("N/A")
185
+ expect(task_details[:NextRunTime]).to eq("N/A")
186
+ expect(task_details[:none]).to eq(true)
187
+ expect(task_details[:run_level]).to eq("HighestAvailable")
188
+ end
189
+ end
190
+
166
191
  context "frequency :weekly" do
167
192
  subject do
168
193
  new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
@@ -0,0 +1,79 @@
1
+ #
2
+ # Author:: Sandra Tiffin (<sandi.tiffin@gmail.com>)
3
+ # Copyright:: Copyright 2014-2016, Chef Software Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "spec_helper"
20
+ require "lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb"
21
+
22
+ class TestDataBag < Mash
23
+ attr_accessor :name
24
+
25
+ def initialize(bag_name)
26
+ @name = bag_name
27
+ end
28
+ end
29
+
30
+ class TestDataBagItem < Mash
31
+ attr_accessor :name, :parent
32
+
33
+ def path_for_printing
34
+ "/some/path"
35
+ end
36
+
37
+ def initialize(bag_name, item_name)
38
+ @name = "#{item_name}.json"
39
+ @parent = TestDataBag.new(bag_name)
40
+ end
41
+ end
42
+
43
+ describe Chef::ChefFS::DataHandler::DataBagItemDataHandler do
44
+ let(:handler) { described_class.new }
45
+
46
+ describe "#verify_integrity" do
47
+ context "json id does not match data bag item name" do
48
+ let(:entry) { TestDataBagItem.new("luggage", "bag") }
49
+ let(:object) do
50
+ { "raw_data" => { "id" => "duffel" } }
51
+ end
52
+ it "rejects the data bag item name" do
53
+ expect { |b| handler.verify_integrity(object, entry, &b) }.to yield_with_args
54
+ end
55
+ end
56
+
57
+ context "using a reserved word for the data bag name" do
58
+ %w{node role environment client}.each do |reserved_word|
59
+ let(:entry) { TestDataBagItem.new(reserved_word, "bag") }
60
+ let(:object) do
61
+ { "raw_data" => { "id" => "bag" } }
62
+ end
63
+ it "rejects the data bag name '#{reserved_word}'" do
64
+ expect { |b| handler.verify_integrity(object, entry, &b) }.to yield_with_args
65
+ end
66
+ end
67
+ end
68
+
69
+ context "valid data" do
70
+ let(:entry) { TestDataBagItem.new("luggage", "bag") }
71
+ let(:object) do
72
+ { "raw_data" => { "id" => "bag" } }
73
+ end
74
+ it "validates the data bag item" do
75
+ expect(handler.verify_integrity(object, entry)).to be_nil
76
+ end
77
+ end
78
+ end
79
+ end
@@ -44,6 +44,17 @@ describe Chef::Client do
44
44
  end
45
45
  end
46
46
 
47
+ context "when Ohai tells us to fail" do
48
+ it "fails" do
49
+ ohai_system = Ohai::System.new
50
+ module Ohai::Exceptions
51
+ class CriticalPluginFailure < Error; end
52
+ end
53
+ expect(ohai_system).to receive(:all_plugins) { raise Ohai::Exceptions::CriticalPluginFailure }
54
+ expect { client.run_ohai }.to raise_error(SystemExit)
55
+ end
56
+ end
57
+
47
58
  describe "authentication protocol selection" do
48
59
  context "when FIPS is disabled" do
49
60
  before do
@@ -116,9 +116,12 @@ describe Chef::CookbookSynchronizer do
116
116
 
117
117
  let(:no_lazy_load) { true }
118
118
 
119
+ let(:skip_cookbook_sync) { false }
120
+
119
121
  let(:synchronizer) do
120
122
  Chef::Config[:no_lazy_load] = no_lazy_load
121
123
  Chef::Config[:file_cache_path] = "/file-cache"
124
+ Chef::Config[:skip_cookbook_sync] = skip_cookbook_sync
122
125
  Chef::CookbookSynchronizer.new(cookbook_manifest, events)
123
126
  end
124
127
 
@@ -523,5 +526,32 @@ describe Chef::CookbookSynchronizer do
523
526
  end
524
527
  end
525
528
  end
529
+
530
+ context "when Chef::Config[:skip_cookbook_sync] is true" do
531
+ let(:skip_cookbook_sync) { true }
532
+
533
+ it "loads the cookbook files and warns the user that this isn't supported" do
534
+ expect(file_cache).to receive(:load).
535
+ with("cookbooks/cookbook_a/recipes/default.rb", false).
536
+ once.
537
+ and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb")
538
+ expect(file_cache).to receive(:load).
539
+ with("cookbooks/cookbook_a/attributes/default.rb", false).
540
+ once.
541
+ and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb")
542
+ expect(file_cache).to receive(:load).
543
+ with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false).
544
+ once.
545
+ and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb")
546
+ expect(file_cache).to receive(:load).
547
+ with("cookbooks/cookbook_a/files/default/megaman.conf", false).
548
+ once.
549
+ and_return("/file-cache/cookbooks/cookbook_a/files/default/megaman.conf")
550
+ expect(Chef::Log).to receive(:warn).
551
+ with("skipping cookbook synchronization! DO NOT LEAVE THIS ENABLED IN PRODUCTION!!!").
552
+ once
553
+ synchronizer.sync_cookbooks
554
+ end
555
+ end
526
556
  end
527
557
  end
@@ -72,6 +72,14 @@ describe Chef::Knife::DataBagCreate do
72
72
  expect { knife.run }.to exit_with_code(1)
73
73
  end
74
74
 
75
+ it "won't create a data bag with a reserved name for search" do
76
+ %w{node role client environment}.each do |name|
77
+ knife.name_args = [name]
78
+ expect(Chef::DataBag).to receive(:validate_name!).with(knife.name_args[0]).and_raise(Chef::Exceptions::InvalidDataBagName)
79
+ expect { knife.run }.to exit_with_code(1)
80
+ end
81
+ end
82
+
75
83
  context "when given one argument" do
76
84
  before do
77
85
  knife.name_args = [bag_name]
@@ -41,7 +41,6 @@ describe "a class that mixes in user_context" do
41
41
  before do
42
42
  allow(::Chef::Platform).to receive(:windows?).and_return(true)
43
43
  allow(::Chef::Util::Windows::LogonSession).to receive(:new).and_return(logon_session)
44
- allow(instance_with_user_context).to receive(:node).and_return({ "platform_family" => "windows" })
45
44
  end
46
45
 
47
46
  let(:logon_session) { instance_double("::Chef::Util::Windows::LogonSession", :set_user_context => nil, :open => nil, :close => nil) }
@@ -99,7 +98,7 @@ describe "a class that mixes in user_context" do
99
98
 
100
99
  context "when not running on Windows" do
101
100
  before do
102
- allow(instance_with_user_context).to receive(:node).and_return({ "platform_family" => "ubuntu" })
101
+ allow(::Chef::Platform).to receive(:windows?).and_return(false)
103
102
  end
104
103
 
105
104
  it "raises a ::Chef::Exceptions::UnsupportedPlatform exception" do
@@ -59,7 +59,9 @@ Git|2.6.2
59
59
  munin-node|1.6.1.20130823
60
60
  EOF
61
61
  remote_list_obj = double(stdout: remote_list_stdout)
62
- allow(provider).to receive(:shell_out!).with("#{choco_exe} list -r #{package_names.join ' '}#{args}", { :returns => [0], timeout: timeout }).and_return(remote_list_obj)
62
+ package_names.each do |pkg|
63
+ allow(provider).to receive(:shell_out!).with("#{choco_exe} list -r #{pkg}#{args}", { :returns => [0], timeout: timeout }).and_return(remote_list_obj)
64
+ end
63
65
  end
64
66
 
65
67
  describe "#initialize" do