summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/chef/provider/ifconfig/debian.rb27
-rw-r--r--spec/unit/provider/ifconfig/debian_spec.rb275
2 files changed, 270 insertions, 32 deletions
diff --git a/lib/chef/provider/ifconfig/debian.rb b/lib/chef/provider/ifconfig/debian.rb
index 821f4fe924..7589971143 100644
--- a/lib/chef/provider/ifconfig/debian.rb
+++ b/lib/chef/provider/ifconfig/debian.rb
@@ -24,6 +24,9 @@ class Chef
class Ifconfig
class Debian < Chef::Provider::Ifconfig
+ INTERFACES_FILE = "/etc/network/interfaces"
+ INTERFACES_DOT_D_DIR = "/etc/network/interfaces.d"
+
def initialize(new_resource, run_context)
super(new_resource, run_context)
@config_template = %{
@@ -46,22 +49,30 @@ iface <%= @new_resource.device %> inet static
<% end %>
<% end %>
}
- @config_path = "/etc/network/interfaces.d/ifcfg-#{@new_resource.device}"
+ @config_path = "#{INTERFACES_DOT_D_DIR}/ifcfg-#{@new_resource.device}"
end
def generate_config
- check_interfaces_config
+ enforce_interfaces_dot_d_sanity
super
end
protected
- def check_interfaces_config
- converge_by ('modify configuration file : /etc/network/interfaces') do
- Dir.mkdir('/etc/network/interfaces.d') unless ::File.directory?('/etc/network/interfaces.d')
- conf = Chef::Util::FileEdit.new('/etc/network/interfaces')
- conf.insert_line_if_no_match('^\s*source\s+/etc/network/interfaces[.]d/[*]\s*$', 'source /etc/network/interfaces.d/*')
- conf.write_file
+ def enforce_interfaces_dot_d_sanity
+ # create /etc/network/interfaces.d via dir resource (to get reporting, etc)
+ dir = Chef::Resource::Directory.new(INTERFACES_DOT_D_DIR, run_context)
+ dir.run_action(:create)
+ new_resource.updated_by_last_action(true) if dir.updated_by_last_action?
+ # roll our own file_edit resource, this will not get reported until we have a file_edit resource
+ interfaces_dot_d_for_regexp = INTERFACES_DOT_D_DIR.gsub(/\./, '\.') # escape dots for the regexp
+ regexp = %r{^\s*source\s+#{interfaces_dot_d_for_regexp}/\*\s*$}
+ unless ::File.exists?(INTERFACES_FILE) && regexp.match(IO.read(INTERFACES_FILE))
+ converge_by("modifying #{INTERFACES_FILE} to source #{INTERFACES_DOT_D_DIR}") do
+ conf = Chef::Util::FileEdit.new(INTERFACES_FILE)
+ conf.insert_line_if_no_match(regexp, "source #{INTERFACES_DOT_D_DIR}/*")
+ conf.write_file
+ end
end
end
diff --git a/spec/unit/provider/ifconfig/debian_spec.rb b/spec/unit/provider/ifconfig/debian_spec.rb
index c2e2d1bfd1..c6a37fdd5b 100644
--- a/spec/unit/provider/ifconfig/debian_spec.rb
+++ b/spec/unit/provider/ifconfig/debian_spec.rb
@@ -53,38 +53,264 @@ describe Chef::Provider::Ifconfig::Debian do
let(:config_filename_ifcfg) { "/etc/network/interfaces.d/ifcfg-#{new_resource.device}" }
- describe "generate_config for action_add" do
+ describe "generate_config" do
- let(:config_file_ifaces) { StringIO.new }
+ context "when writing a file" do
+ let(:config_file_ifcfg) { StringIO.new }
- let(:config_file_ifcfg) { StringIO.new }
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
- before do
- expect(FileUtils).to receive(:cp)
- expect(File).to receive(:open).with(config_filename_ifaces).and_return(StringIO.new)
- expect(File).to receive(:open).with(config_filename_ifaces, "w").and_yield(config_file_ifaces)
- expect(File).to receive(:new).with(config_filename_ifcfg, "w").and_return(config_file_ifcfg)
- expect(File).to receive(:exist?).with(config_filename_ifaces).and_return(true)
- end
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
+
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
+
+ before do
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
+ expect(File).to receive(:new).with(config_filename_ifcfg, "w").and_return(config_file_ifcfg)
+ end
+
+ it "should write a network-script" do
+ provider.run_action(:add)
+ expect(config_file_ifcfg.string).to match(/^iface eth0 inet static\s*$/)
+ expect(config_file_ifcfg.string).to match(/^\s+address 10\.0\.0\.1\s*$/)
+ expect(config_file_ifcfg.string).to match(/^\s+netmask 255\.255\.254\.0\s*$/)
+ end
+
+ context "when the interface_dot_d directory does not exist" do
+ before do
+ FileUtils.rmdir tempdir_path
+ expect(File.exists?(tempdir_path)).to be_false
+ end
+
+ it "should create the /etc/network/interfaces.d directory" do
+ provider.run_action(:add)
+ expect(File.exists?(tempdir_path)).to be_true
+ expect(File.directory?(tempdir_path)).to be_true
+ end
- it "should create network-scripts directory" do
- expect(File).to receive(:directory?).with(File.dirname(config_filename_ifcfg)).and_return(false)
- expect(Dir).to receive(:mkdir).with(File.dirname(config_filename_ifcfg))
- provider.run_action(:add)
+ it "should mark the resource as updated" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
+
+ context "when the interface_dot_d directory exists" do
+ before do
+ expect(File.exists?(tempdir_path)).to be_true
+ end
+
+ it "should still mark the resource as updated (we still write a file to it)" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
end
- it "should write configure network-scripts directory" do
- expect(File).to receive(:directory?).with(File.dirname(config_filename_ifcfg)).and_return(true)
- provider.run_action(:add)
- expect(config_file_ifaces.string).to match(/^\s*source\s+\/etc\/network\/interfaces[.]d\/[*]\s*$/)
+ context "when the file is up-to-date" do
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
+
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
+
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
+
+ before do
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
+ config_file_ifcfg = StringIO.new(<<-EOF
+iface eth0 inet static
+ address 10.0.0.1
+ netmask 255.255.254.0
+EOF
+ )
+ expect(File).to receive(:new).with(config_filename_ifcfg, "w").and_return(config_file_ifcfg)
+ expect(File.exists?(tempdir_path)).to be_true # since the file exists, the enclosing dir must also exist
+ end
+
+ context "when the /etc/network/interfaces file has the source line" do
+ let(:expected_string) do
+ <<-EOF
+a line
+source #{tempdir_path}/*
+another line
+EOF
+ end
+
+ before do
+ tempfile.write(expected_string)
+ tempfile.close
+ end
+
+ it "should preserve all the contents" do
+ provider.run_action(:add)
+ expect(IO.read(tempfile.path)).to eq(expected_string)
+ end
+
+ it "should not mark the resource as updated" do
+ provider.run_action(:add)
+ pending "superclass ifconfig provider is not idempotent"
+ expect(new_resource.updated_by_last_action?).to be_false
+ end
+ end
+
+ context "when the /etc/network/interfaces file does not have the source line" do
+ let(:expected_string) do
+ <<-EOF
+a line
+another line
+source #{tempdir_path}/*
+EOF
+ end
+
+ before do
+ tempfile.write("a line\nanother line\n")
+ tempfile.close
+ end
+
+ it "should preserve the original contents and add the source line" do
+ provider.run_action(:add)
+ expect(IO.read(tempfile.path)).to eq(expected_string)
+ end
+
+ it "should mark the resource as updated" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
end
- it "should write a network-script" do
- expect(File).to receive(:directory?).with(File.dirname(config_filename_ifcfg)).and_return(true)
- provider.run_action(:add)
- expect(config_file_ifcfg.string).to match(/^iface eth0 inet static\s*$/)
- expect(config_file_ifcfg.string).to match(/^\s+address 10\.0\.0\.1\s*$/)
- expect(config_file_ifcfg.string).to match(/^\s+netmask 255\.255\.254\.0\s*$/)
+ describe "when running under why run" do
+
+ before do
+ Chef::Config[:why_run] = true
+ end
+
+ after do
+ Chef::Config[:why_run] = false
+ end
+
+ context "when writing a file" do
+ let(:config_file_ifcfg) { StringIO.new }
+
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
+
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
+
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
+
+ before do
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
+ expect(File).not_to receive(:new).with(config_filename_ifcfg, "w")
+ end
+
+ it "should write a network-script" do
+ provider.run_action(:add)
+ expect(config_file_ifcfg.string).not_to match(/^iface eth0 inet static\s*$/)
+ expect(config_file_ifcfg.string).not_to match(/^\s+address 10\.0\.0\.1\s*$/)
+ expect(config_file_ifcfg.string).not_to match(/^\s+netmask 255\.255\.254\.0\s*$/)
+ end
+
+ context "when the interface_dot_d directory does not exist" do
+ before do
+ FileUtils.rmdir tempdir_path
+ expect(File.exists?(tempdir_path)).to be_false
+ end
+
+ it "should not create the /etc/network/interfaces.d directory" do
+ provider.run_action(:add)
+ expect(File.exists?(tempdir_path)).not_to be_true
+ end
+
+ it "should mark the resource as updated" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
+
+ context "when the interface_dot_d directory exists" do
+ before do
+ expect(File.exists?(tempdir_path)).to be_true
+ end
+
+ it "should still mark the resource as updated (we still write a file to it)" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
+ end
+
+ context "when the file is up-to-date" do
+ let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") }
+
+ let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") }
+
+ let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" }
+
+ before do
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path)
+ stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path)
+ config_file_ifcfg = StringIO.new(<<-EOF
+iface eth0 inet static
+ address 10.0.0.1
+ netmask 255.255.254.0
+ EOF
+ )
+ expect(File).not_to receive(:new).with(config_filename_ifcfg, "w")
+ expect(File.exists?(tempdir_path)).to be_true # since the file exists, the enclosing dir must also exist
+ end
+
+ context "when the /etc/network/interfaces file has the source line" do
+ let(:expected_string) do
+ <<-EOF
+a line
+source #{tempdir_path}/*
+another line
+ EOF
+ end
+
+ before do
+ tempfile.write(expected_string)
+ tempfile.close
+ end
+
+ it "should preserve all the contents" do
+ provider.run_action(:add)
+ expect(IO.read(tempfile.path)).to eq(expected_string)
+ end
+
+ it "should not mark the resource as updated" do
+ provider.run_action(:add)
+ pending "superclass ifconfig provider is not idempotent"
+ expect(new_resource.updated_by_last_action?).to be_false
+ end
+ end
+
+ context "when the /etc/network/interfaces file does not have the source line" do
+ let(:expected_string) do
+ <<-EOF
+a line
+another line
+source #{tempdir_path}/*
+ EOF
+ end
+
+ before do
+ tempfile.write("a line\nanother line\n")
+ tempfile.close
+ end
+
+ it "should preserve the original contents and not add the source line" do
+ provider.run_action(:add)
+ expect(IO.read(tempfile.path)).to eq("a line\nanother line\n")
+ end
+
+ it "should mark the resource as updated" do
+ provider.run_action(:add)
+ expect(new_resource.updated_by_last_action?).to be_true
+ end
+ end
+ end
end
end
@@ -98,4 +324,5 @@ describe Chef::Provider::Ifconfig::Debian do
provider.run_action(:delete)
end
end
+
end