chef 11.10.4-x86-mingw32 → 11.12.0.alpha.1-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +6 -6
  3. data/README.md +1 -1
  4. data/lib/chef/api_client.rb +1 -3
  5. data/lib/chef/application.rb +2 -1
  6. data/lib/chef/application/client.rb +11 -1
  7. data/lib/chef/client.rb +24 -9
  8. data/lib/chef/cookbook/syntax_check.rb +107 -6
  9. data/lib/chef/dsl/reboot_pending.rb +61 -0
  10. data/lib/chef/exceptions.rb +12 -1
  11. data/lib/chef/formatters/error_descriptor.rb +1 -1
  12. data/lib/chef/http/remote_request_id.rb +46 -0
  13. data/lib/chef/knife/bootstrap.rb +1 -1
  14. data/lib/chef/knife/bootstrap/README.md +12 -0
  15. data/lib/chef/knife/bootstrap/chef-full.erb +3 -0
  16. data/lib/chef/knife/client_create.rb +6 -0
  17. data/lib/chef/knife/client_delete.rb +15 -1
  18. data/lib/chef/knife/raw.rb +1 -0
  19. data/lib/chef/node.rb +1 -1
  20. data/lib/chef/node/attribute_collections.rb +8 -1
  21. data/lib/chef/node/immutable_collections.rb +8 -1
  22. data/lib/chef/provider/deploy.rb +1 -1
  23. data/lib/chef/provider/group.rb +1 -1
  24. data/lib/chef/provider/ifconfig/debian.rb +19 -8
  25. data/lib/chef/provider/ohai.rb +6 -5
  26. data/lib/chef/provider/service/macosx.rb +68 -14
  27. data/lib/chef/recipe.rb +2 -0
  28. data/lib/chef/request_id.rb +37 -0
  29. data/lib/chef/resource.rb +2 -0
  30. data/lib/chef/resource_reporter.rb +7 -4
  31. data/lib/chef/rest.rb +5 -1
  32. data/lib/chef/run_status.rb +4 -1
  33. data/lib/chef/server_api.rb +3 -1
  34. data/lib/chef/version.rb +2 -2
  35. data/spec/functional/dsl/reboot_pending_spec.rb +118 -0
  36. data/spec/functional/resource/base.rb +1 -3
  37. data/spec/functional/resource/deploy_revision_spec.rb +192 -1
  38. data/spec/functional/resource/git_spec.rb +1 -1
  39. data/spec/functional/resource/ohai_spec.rb +65 -0
  40. data/spec/functional/resource/registry_spec.rb +4 -5
  41. data/spec/integration/client/client_spec.rb +14 -0
  42. data/spec/spec_helper.rb +1 -2
  43. data/spec/support/shared/functional/windows_script.rb +1 -2
  44. data/spec/unit/api_client_spec.rb +46 -0
  45. data/spec/unit/client_spec.rb +345 -229
  46. data/spec/unit/cookbook/syntax_check_spec.rb +0 -1
  47. data/spec/unit/dsl/reboot_pending_spec.rb +100 -0
  48. data/spec/unit/knife/client_create_spec.rb +29 -1
  49. data/spec/unit/knife/client_delete_spec.rb +44 -1
  50. data/spec/unit/knife_spec.rb +55 -0
  51. data/spec/unit/node/attribute_spec.rb +7 -0
  52. data/spec/unit/node/immutable_collections_spec.rb +5 -1
  53. data/spec/unit/provider/group_spec.rb +5 -0
  54. data/spec/unit/provider/ifconfig/debian_spec.rb +251 -24
  55. data/spec/unit/provider/ohai_spec.rb +2 -3
  56. data/spec/unit/provider/service/macosx_spec.rb +29 -11
  57. data/spec/unit/resource_reporter_spec.rb +1 -1
  58. data/spec/unit/rest_spec.rb +38 -13
  59. metadata +151 -216
@@ -28,7 +28,6 @@ describe Chef::Cookbook::SyntaxCheck do
28
28
  Chef::Log.logger = Logger.new(StringIO.new)
29
29
  Chef::Log.level = :warn # suppress "Syntax OK" messages
30
30
 
31
-
32
31
  @attr_files = %w{default.rb smokey.rb}.map { |f| File.join(cookbook_path, 'attributes', f) }
33
32
  @defn_files = %w{client.rb server.rb}.map { |f| File.join(cookbook_path, 'definitions', f)}
34
33
  @recipes = %w{default.rb gigantor.rb one.rb}.map { |f| File.join(cookbook_path, 'recipes', f) }
@@ -0,0 +1,100 @@
1
+ #
2
+ # Author:: Bryan McLellan <btm@loftninjas.org>
3
+ # Copyright:: Copyright (c) 2014 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 "chef/dsl/reboot_pending"
20
+ require "spec_helper"
21
+
22
+ describe Chef::DSL::RebootPending do
23
+ describe "reboot_pending?" do
24
+ describe "in isoloation" do
25
+ let(:recipe) { Object.new.extend(Chef::DSL::RebootPending) }
26
+
27
+ before do
28
+ recipe.stub(:platform?).and_return(false)
29
+ end
30
+
31
+ context "platform is windows" do
32
+ before do
33
+ recipe.stub(:platform?).with('windows').and_return(true)
34
+ recipe.stub(:registry_key_exists?).and_return(false)
35
+ recipe.stub(:registry_value_exists?).and_return(false)
36
+ end
37
+
38
+ it 'should return true if "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations" exists' do
39
+ recipe.stub(:registry_value_exists?).with('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => 'PendingFileRenameOperations' }).and_return(true)
40
+ expect(recipe.reboot_pending?).to be_true
41
+ end
42
+
43
+ it 'should return true if "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" exists' do
44
+ recipe.stub(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired').and_return(true)
45
+ expect(recipe.reboot_pending?).to be_true
46
+ end
47
+
48
+ it 'should return true if key "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired" exists' do
49
+ recipe.stub(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired').and_return(true)
50
+ expect(recipe.reboot_pending?).to be_true
51
+ end
52
+
53
+ it 'should return true if value "HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile" contains specific data' do
54
+ recipe.stub(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return(true)
55
+ recipe.stub(:registry_get_values).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return(
56
+ [{:name => "Flags", :type => :dword, :data => 3}])
57
+ expect(recipe.reboot_pending?).to be_true
58
+ end
59
+ end
60
+
61
+ context "platform is ubuntu" do
62
+ before do
63
+ recipe.stub(:platform?).with('ubuntu').and_return(true)
64
+ end
65
+
66
+ it 'should return true if /var/run/reboot-required exists' do
67
+ File.stub(:exists?).with('/var/run/reboot-required').and_return(true)
68
+ expect(recipe.reboot_pending?).to be_true
69
+ end
70
+
71
+ it 'should return false if /var/run/reboot-required does not exist' do
72
+ File.stub(:exists?).with('/var/run/reboot-required').and_return(false)
73
+ expect(recipe.reboot_pending?).to be_false
74
+ end
75
+ end
76
+
77
+ context "platform is not supported" do
78
+ it 'should raise an exception' do
79
+ recipe.stub_chain(:node, :[]).with(:platform).and_return('msdos')
80
+ expect { recipe.reboot_pending? }.to raise_error(Chef::Exceptions::UnsupportedPlatform)
81
+ end
82
+ end
83
+ end # describe in isolation
84
+
85
+ describe "in a recipe" do
86
+ it "responds to reboot_pending?" do
87
+ # Chef::Recipe.new(cookbook_name, recipe_name, run_context(node, cookbook_collection, events))
88
+ recipe = Chef::Recipe.new(nil,nil,Chef::RunContext.new(Chef::Node.new, {}, nil))
89
+ expect(recipe).to respond_to(:reboot_pending?)
90
+ end
91
+ end # describe in a recipe
92
+
93
+ describe "in a resource" do
94
+ it "responds to reboot_pending?" do
95
+ resource = Chef::Resource::new("Crackerjack::Timing", nil)
96
+ expect(resource).to respond_to(:reboot_pending?)
97
+ end
98
+ end # describe in a resource
99
+ end
100
+ end
@@ -25,7 +25,9 @@ describe Chef::Knife::ClientCreate do
25
25
  Chef::Config[:node_name] = "webmonkey.example.com"
26
26
  @knife = Chef::Knife::ClientCreate.new
27
27
  @knife.config = {
28
- :file => nil
28
+ :file => nil,
29
+ :admin => false,
30
+ :validator => false
29
31
  }
30
32
  @knife.name_args = [ "adam" ]
31
33
  @client = Chef::ApiClient.new
@@ -49,6 +51,16 @@ describe Chef::Knife::ClientCreate do
49
51
  @knife.run
50
52
  end
51
53
 
54
+ it "by default it is not an admin" do
55
+ @client.should_receive(:admin).with(false)
56
+ @knife.run
57
+ end
58
+
59
+ it "by default it is not a validator" do
60
+ @client.should_receive(:validator).with(false)
61
+ @knife.run
62
+ end
63
+
52
64
  it "should allow you to edit the data" do
53
65
  @knife.should_receive(:edit_data).with(@client)
54
66
  @knife.run
@@ -70,5 +82,21 @@ describe Chef::Knife::ClientCreate do
70
82
  end
71
83
  end
72
84
 
85
+ describe "with -a or --admin" do
86
+ it "should create an admin client" do
87
+ @knife.config[:admin] = true
88
+ @client.should_receive(:admin).with(true)
89
+ @knife.run
90
+ end
91
+ end
92
+
93
+ describe "with --validator" do
94
+ it "should create an validator client" do
95
+ @knife.config[:validator] = true
96
+ @client.should_receive(:validator).with(true)
97
+ @knife.run
98
+ end
99
+ end
100
+
73
101
  end
74
102
  end
@@ -21,12 +21,16 @@ require 'spec_helper'
21
21
  describe Chef::Knife::ClientDelete do
22
22
  before(:each) do
23
23
  @knife = Chef::Knife::ClientDelete.new
24
+ # defaults
25
+ @knife.config = {
26
+ :force => false
27
+ }
24
28
  @knife.name_args = [ 'adam' ]
25
29
  end
26
30
 
27
31
  describe 'run' do
28
32
  it 'should delete the client' do
29
- @knife.should_receive(:delete_object).with(Chef::ApiClient, 'adam')
33
+ @knife.should_receive(:delete_object).with(Chef::ApiClient, 'adam', 'client')
30
34
  @knife.run
31
35
  end
32
36
 
@@ -37,4 +41,43 @@ describe Chef::Knife::ClientDelete do
37
41
  lambda { @knife.run }.should raise_error(SystemExit)
38
42
  end
39
43
  end
44
+
45
+ describe 'with a validator' do
46
+ before(:each) do
47
+ Chef::Knife::UI.stub(:confirm).and_return(true)
48
+ @knife.stub(:confirm).and_return(true)
49
+ @client = Chef::ApiClient.new
50
+ Chef::ApiClient.should_receive(:load).and_return(@client)
51
+ end
52
+
53
+ it 'should delete non-validator client if --force is not set' do
54
+ @knife.config[:force] = false
55
+ @client.should_receive(:destroy).and_return(@client)
56
+ @knife.should_receive(:msg)
57
+
58
+ @knife.run
59
+ end
60
+
61
+ it 'should delete non-validator client if --force is set' do
62
+ @knife.config[:force] = true
63
+ @client.should_receive(:destroy).and_return(@client)
64
+ @knife.should_receive(:msg)
65
+
66
+ @knife.run
67
+ end
68
+
69
+ it 'should not delete validator client if --force is not set' do
70
+ @client.validator(true)
71
+ @knife.ui.should_receive(:fatal)
72
+ lambda { @knife.run}.should raise_error(SystemExit)
73
+ end
74
+
75
+ it 'should delete validator client if --force is set' do
76
+ @knife.config[:force] = true
77
+ @client.should_receive(:destroy).and_return(@client)
78
+ @knife.should_receive(:msg)
79
+
80
+ @knife.run
81
+ end
82
+ end
40
83
  end
@@ -22,6 +22,7 @@ module KnifeSpecs
22
22
  end
23
23
 
24
24
  require 'spec_helper'
25
+ require 'uri'
25
26
 
26
27
  describe Chef::Knife do
27
28
  before(:each) do
@@ -141,6 +142,60 @@ describe Chef::Knife do
141
142
 
142
143
  end
143
144
 
145
+ describe "the headers include X-Remote-Request-Id" do
146
+
147
+ let(:headers) {{"Accept"=>"application/json",
148
+ "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
149
+ 'X-Chef-Version' => Chef::VERSION,
150
+ "Host"=>"api.opscode.piab:443",
151
+ "X-REMOTE-REQUEST-ID"=>request_id}}
152
+
153
+ let(:request_id) {"1234"}
154
+
155
+ let(:request_mock) { {} }
156
+
157
+ let(:rest) do
158
+ Net::HTTP.stub(:new).and_return(http_client)
159
+ Chef::RequestID.instance.stub(:request_id).and_return(request_id)
160
+ Chef::Config.stub(:chef_server_url).and_return("https://api.opscode.piab")
161
+ command = Chef::Knife.run(%w{test yourself})
162
+ rest = command.noauth_rest
163
+ rest
164
+ end
165
+
166
+ let!(:http_client) do
167
+ http_client = Net::HTTP.new(url.host, url.port)
168
+ http_client.stub(:request).and_yield(http_response).and_return(http_response)
169
+ http_client
170
+ end
171
+
172
+ let(:url) { URI.parse("https://api.opscode.piab") }
173
+
174
+ let(:http_response) do
175
+ http_response = Net::HTTPSuccess.new("1.1", "200", "successful rest req")
176
+ http_response.stub(:read_body)
177
+ http_response.stub(:body).and_return(body)
178
+ http_response["Content-Length"] = body.bytesize.to_s
179
+ http_response
180
+ end
181
+
182
+ let(:body) { "ninja" }
183
+
184
+ before(:each) do
185
+ Chef::Config[:chef_server_url] = "https://api.opscode.piab"
186
+ if KnifeSpecs.const_defined?(:TestYourself)
187
+ KnifeSpecs.send :remove_const, :TestYourself
188
+ end
189
+ Kernel.load(File.join(CHEF_SPEC_DATA, 'knife_subcommand', 'test_yourself.rb'))
190
+ Chef::Knife.subcommands.each { |name, klass| Chef::Knife.subcommands.delete(name) unless klass.kind_of?(Class) }
191
+ end
192
+
193
+ it "confirms that the headers include X-Remote-Request-Id" do
194
+ Net::HTTP::Get.should_receive(:new).with("/monkey", headers).and_return(request_mock)
195
+ rest.get_rest("monkey")
196
+ end
197
+ end
198
+
144
199
  describe "when running a command" do
145
200
  before(:each) do
146
201
  if KnifeSpecs.const_defined?(:TestYourself)
@@ -488,6 +488,13 @@ describe Chef::Node::Attribute do
488
488
  end
489
489
  end
490
490
 
491
+ describe "dup" do
492
+ it "array can be duped even if some elements can't" do
493
+ @attributes.default[:foo] = %w[foo bar baz] + Array(1..3) + [nil, true, false, [ "el", 0, nil ] ]
494
+ @attributes.default[:foo].dup
495
+ end
496
+ end
497
+
491
498
  describe "has_key?" do
492
499
  it "should return true if an attribute exists" do
493
500
  @attributes.has_key?("music").should == true
@@ -86,7 +86,7 @@ end
86
86
  describe Chef::Node::ImmutableArray do
87
87
 
88
88
  before do
89
- @immutable_array = Chef::Node::ImmutableArray.new(%w[foo bar baz])
89
+ @immutable_array = Chef::Node::ImmutableArray.new(%w[foo bar baz] + Array(1..3) + [nil, true, false, [ "el", 0, nil ] ])
90
90
  end
91
91
 
92
92
  ##
@@ -130,6 +130,10 @@ describe Chef::Node::ImmutableArray do
130
130
  end
131
131
  end
132
132
 
133
+ it "can be duped even if some elements can't" do
134
+ @immutable_array.dup
135
+ end
136
+
133
137
  it "returns a mutable version of itself when duped" do
134
138
  mutable = @immutable_array.dup
135
139
  mutable[0] = :value
@@ -96,6 +96,11 @@ describe Chef::Provider::User do
96
96
  @provider.compare_group.should be_false
97
97
  end
98
98
 
99
+ it "should coerce an integer to a string for comparison" do
100
+ @current_resource.stub!(:gid).and_return("500")
101
+ @provider.compare_group.should be_false
102
+ end
103
+
99
104
  it "should return false if append is true and the group member(s) already exists" do
100
105
  @current_resource.members << "extra_user"
101
106
  @new_resource.stub(:append).and_return(true)
@@ -53,38 +53,264 @@ describe Chef::Provider::Ifconfig::Debian do
53
53
 
54
54
  let(:config_filename_ifcfg) { "/etc/network/interfaces.d/ifcfg-#{new_resource.device}" }
55
55
 
56
- describe "generate_config for action_add" do
56
+ describe "generate_config" do
57
57
 
58
- let(:config_file_ifaces) { StringIO.new }
58
+ context "when writing a file" do
59
+ let(:config_file_ifcfg) { StringIO.new }
59
60
 
60
- let(:config_file_ifcfg) { StringIO.new }
61
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
61
62
 
62
- before do
63
- expect(FileUtils).to receive(:cp)
64
- expect(File).to receive(:open).with(config_filename_ifaces).and_return(StringIO.new)
65
- expect(File).to receive(:open).with(config_filename_ifaces, "w").and_yield(config_file_ifaces)
66
- expect(File).to receive(:new).with(config_filename_ifcfg, "w").and_return(config_file_ifcfg)
67
- expect(File).to receive(:exist?).with(config_filename_ifaces).and_return(true)
68
- end
63
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
64
+
65
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
66
+
67
+ before do
68
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
69
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
70
+ expect(File).to receive(:new).with(config_filename_ifcfg, "w").and_return(config_file_ifcfg)
71
+ end
72
+
73
+ it "should write a network-script" do
74
+ provider.run_action(:add)
75
+ expect(config_file_ifcfg.string).to match(/^iface eth0 inet static\s*$/)
76
+ expect(config_file_ifcfg.string).to match(/^\s+address 10\.0\.0\.1\s*$/)
77
+ expect(config_file_ifcfg.string).to match(/^\s+netmask 255\.255\.254\.0\s*$/)
78
+ end
79
+
80
+ context "when the interface_dot_d directory does not exist" do
81
+ before do
82
+ FileUtils.rmdir tempdir_path
83
+ expect(File.exists?(tempdir_path)).to be_false
84
+ end
85
+
86
+ it "should create the /etc/network/interfaces.d directory" do
87
+ provider.run_action(:add)
88
+ expect(File.exists?(tempdir_path)).to be_true
89
+ expect(File.directory?(tempdir_path)).to be_true
90
+ end
69
91
 
70
- it "should create network-scripts directory" do
71
- expect(File).to receive(:directory?).with(File.dirname(config_filename_ifcfg)).and_return(false)
72
- expect(Dir).to receive(:mkdir).with(File.dirname(config_filename_ifcfg))
73
- provider.run_action(:add)
92
+ it "should mark the resource as updated" do
93
+ provider.run_action(:add)
94
+ expect(new_resource.updated_by_last_action?).to be_true
95
+ end
96
+ end
97
+
98
+ context "when the interface_dot_d directory exists" do
99
+ before do
100
+ expect(File.exists?(tempdir_path)).to be_true
101
+ end
102
+
103
+ it "should still mark the resource as updated (we still write a file to it)" do
104
+ provider.run_action(:add)
105
+ expect(new_resource.updated_by_last_action?).to be_true
106
+ end
107
+ end
74
108
  end
75
109
 
76
- it "should write configure network-scripts directory" do
77
- expect(File).to receive(:directory?).with(File.dirname(config_filename_ifcfg)).and_return(true)
78
- provider.run_action(:add)
79
- expect(config_file_ifaces.string).to match(/^\s*source\s+\/etc\/network\/interfaces[.]d\/[*]\s*$/)
110
+ context "when the file is up-to-date" do
111
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
112
+
113
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
114
+
115
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
116
+
117
+ before do
118
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
119
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
120
+ config_file_ifcfg = StringIO.new(<<-EOF
121
+ iface eth0 inet static
122
+ address 10.0.0.1
123
+ netmask 255.255.254.0
124
+ EOF
125
+ )
126
+ expect(File).to receive(:new).with(config_filename_ifcfg, "w").and_return(config_file_ifcfg)
127
+ expect(File.exists?(tempdir_path)).to be_true # since the file exists, the enclosing dir must also exist
128
+ end
129
+
130
+ context "when the /etc/network/interfaces file has the source line" do
131
+ let(:expected_string) do
132
+ <<-EOF
133
+ a line
134
+ source #{tempdir_path}/*
135
+ another line
136
+ EOF
137
+ end
138
+
139
+ before do
140
+ tempfile.write(expected_string)
141
+ tempfile.close
142
+ end
143
+
144
+ it "should preserve all the contents" do
145
+ provider.run_action(:add)
146
+ expect(IO.read(tempfile.path)).to eq(expected_string)
147
+ end
148
+
149
+ it "should not mark the resource as updated" do
150
+ provider.run_action(:add)
151
+ pending "superclass ifconfig provider is not idempotent"
152
+ expect(new_resource.updated_by_last_action?).to be_false
153
+ end
154
+ end
155
+
156
+ context "when the /etc/network/interfaces file does not have the source line" do
157
+ let(:expected_string) do
158
+ <<-EOF
159
+ a line
160
+ another line
161
+ source #{tempdir_path}/*
162
+ EOF
163
+ end
164
+
165
+ before do
166
+ tempfile.write("a line\nanother line\n")
167
+ tempfile.close
168
+ end
169
+
170
+ it "should preserve the original contents and add the source line" do
171
+ provider.run_action(:add)
172
+ expect(IO.read(tempfile.path)).to eq(expected_string)
173
+ end
174
+
175
+ it "should mark the resource as updated" do
176
+ provider.run_action(:add)
177
+ expect(new_resource.updated_by_last_action?).to be_true
178
+ end
179
+ end
80
180
  end
81
181
 
82
- it "should write a network-script" do
83
- expect(File).to receive(:directory?).with(File.dirname(config_filename_ifcfg)).and_return(true)
84
- provider.run_action(:add)
85
- expect(config_file_ifcfg.string).to match(/^iface eth0 inet static\s*$/)
86
- expect(config_file_ifcfg.string).to match(/^\s+address 10\.0\.0\.1\s*$/)
87
- expect(config_file_ifcfg.string).to match(/^\s+netmask 255\.255\.254\.0\s*$/)
182
+ describe "when running under why run" do
183
+
184
+ before do
185
+ Chef::Config[:why_run] = true
186
+ end
187
+
188
+ after do
189
+ Chef::Config[:why_run] = false
190
+ end
191
+
192
+ context "when writing a file" do
193
+ let(:config_file_ifcfg) { StringIO.new }
194
+
195
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
196
+
197
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
198
+
199
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
200
+
201
+ before do
202
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
203
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
204
+ expect(File).not_to receive(:new).with(config_filename_ifcfg, "w")
205
+ end
206
+
207
+ it "should write a network-script" do
208
+ provider.run_action(:add)
209
+ expect(config_file_ifcfg.string).not_to match(/^iface eth0 inet static\s*$/)
210
+ expect(config_file_ifcfg.string).not_to match(/^\s+address 10\.0\.0\.1\s*$/)
211
+ expect(config_file_ifcfg.string).not_to match(/^\s+netmask 255\.255\.254\.0\s*$/)
212
+ end
213
+
214
+ context "when the interface_dot_d directory does not exist" do
215
+ before do
216
+ FileUtils.rmdir tempdir_path
217
+ expect(File.exists?(tempdir_path)).to be_false
218
+ end
219
+
220
+ it "should not create the /etc/network/interfaces.d directory" do
221
+ provider.run_action(:add)
222
+ expect(File.exists?(tempdir_path)).not_to be_true
223
+ end
224
+
225
+ it "should mark the resource as updated" do
226
+ provider.run_action(:add)
227
+ expect(new_resource.updated_by_last_action?).to be_true
228
+ end
229
+ end
230
+
231
+ context "when the interface_dot_d directory exists" do
232
+ before do
233
+ expect(File.exists?(tempdir_path)).to be_true
234
+ end
235
+
236
+ it "should still mark the resource as updated (we still write a file to it)" do
237
+ provider.run_action(:add)
238
+ expect(new_resource.updated_by_last_action?).to be_true
239
+ end
240
+ end
241
+ end
242
+
243
+ context "when the file is up-to-date" do
244
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
245
+
246
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
247
+
248
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
249
+
250
+ before do
251
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
252
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
253
+ config_file_ifcfg = StringIO.new(<<-EOF
254
+ iface eth0 inet static
255
+ address 10.0.0.1
256
+ netmask 255.255.254.0
257
+ EOF
258
+ )
259
+ expect(File).not_to receive(:new).with(config_filename_ifcfg, "w")
260
+ expect(File.exists?(tempdir_path)).to be_true # since the file exists, the enclosing dir must also exist
261
+ end
262
+
263
+ context "when the /etc/network/interfaces file has the source line" do
264
+ let(:expected_string) do
265
+ <<-EOF
266
+ a line
267
+ source #{tempdir_path}/*
268
+ another line
269
+ EOF
270
+ end
271
+
272
+ before do
273
+ tempfile.write(expected_string)
274
+ tempfile.close
275
+ end
276
+
277
+ it "should preserve all the contents" do
278
+ provider.run_action(:add)
279
+ expect(IO.read(tempfile.path)).to eq(expected_string)
280
+ end
281
+
282
+ it "should not mark the resource as updated" do
283
+ provider.run_action(:add)
284
+ pending "superclass ifconfig provider is not idempotent"
285
+ expect(new_resource.updated_by_last_action?).to be_false
286
+ end
287
+ end
288
+
289
+ context "when the /etc/network/interfaces file does not have the source line" do
290
+ let(:expected_string) do
291
+ <<-EOF
292
+ a line
293
+ another line
294
+ source #{tempdir_path}/*
295
+ EOF
296
+ end
297
+
298
+ before do
299
+ tempfile.write("a line\nanother line\n")
300
+ tempfile.close
301
+ end
302
+
303
+ it "should preserve the original contents and not add the source line" do
304
+ provider.run_action(:add)
305
+ expect(IO.read(tempfile.path)).to eq("a line\nanother line\n")
306
+ end
307
+
308
+ it "should mark the resource as updated" do
309
+ provider.run_action(:add)
310
+ expect(new_resource.updated_by_last_action?).to be_true
311
+ end
312
+ end
313
+ end
88
314
  end
89
315
  end
90
316
 
@@ -98,4 +324,5 @@ describe Chef::Provider::Ifconfig::Debian do
98
324
  provider.run_action(:delete)
99
325
  end
100
326
  end
327
+
101
328
  end