From abe0731d2e3d413cd501981a528b0c1e487471ed Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 9 Dec 2013 14:47:28 -0600 Subject: [PATCH] guests/{ubuntu,debian}: fix change_host_name for trailing dots [GH-2610] When `/etc/hosts` contained a FQDN with a trailing dot, the `\b` in the sed expression would not match, since dot is not considered to be a word character. Fix this by regexp-escaping the hostname search, and matching the end of the line on optional space followed by additional characters. Also add some tests that extract the regexp used by sed and verify that it does what we want. These will hopefully serve us in the future if we ever need to test additional edge cases. --- plugins/guests/debian/cap/change_host_name.rb | 4 +- .../debian/cap/change_host_name_test.rb | 3 +- .../shared/debian_like_host_name_examples.rb | 82 +++++++++++++++++-- .../ubuntu/cap/change_host_name_test.rb | 3 +- 4 files changed, 79 insertions(+), 13 deletions(-) diff --git a/plugins/guests/debian/cap/change_host_name.rb b/plugins/guests/debian/cap/change_host_name.rb index 8b563cc21..09ca83d8e 100644 --- a/plugins/guests/debian/cap/change_host_name.rb +++ b/plugins/guests/debian/cap/change_host_name.rb @@ -47,8 +47,8 @@ module VagrantPlugins # 127.0.1.1 host.fqdn.com host.fqdn host def update_etc_hosts ip_address = '([0-9]{1,3}\.){3}[0-9]{1,3}' - search = "^(#{ip_address})\\s+#{current_hostname}\\b.*$" - replace = "\\1\\t#{fqdn} #{short_hostname}" + search = "^(#{ip_address})\\s+#{Regexp.escape(current_hostname)}(\\s.*)?$" + replace = "\\1 #{fqdn} #{short_hostname}" expression = ['s', search, replace, 'g'].join('@') sudo("sed -ri '#{expression}' /etc/hosts") diff --git a/test/unit/plugins/guests/debian/cap/change_host_name_test.rb b/test/unit/plugins/guests/debian/cap/change_host_name_test.rb index 37678b39e..83ba71016 100644 --- a/test/unit/plugins/guests/debian/cap/change_host_name_test.rb +++ b/test/unit/plugins/guests/debian/cap/change_host_name_test.rb @@ -7,10 +7,11 @@ describe "VagrantPlugins::GuestDebian::Cap::ChangeHostName" do end let(:machine) { double("machine") } let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:old_hostname) { 'oldhostname.olddomain.tld' } before do machine.stub(:communicate).and_return(communicator) - communicator.stub_command('hostname -f', stdout: 'oldhostname.olddomain.tld') + communicator.stub_command('hostname -f', stdout: old_hostname) end after do diff --git a/test/unit/plugins/guests/support/shared/debian_like_host_name_examples.rb b/test/unit/plugins/guests/support/shared/debian_like_host_name_examples.rb index a4a945ff4..afa2e63e6 100644 --- a/test/unit/plugins/guests/support/shared/debian_like_host_name_examples.rb +++ b/test/unit/plugins/guests/support/shared/debian_like_host_name_examples.rb @@ -4,15 +4,6 @@ shared_examples "a debian-like host name change" do described_class.change_host_name(machine, 'newhostname.newdomain.tld') end - it "flips out the old hostname in /etc/hosts" do - sed_find = '^(([0-9]{1,3}\.){3}[0-9]{1,3})\s+oldhostname.olddomain.tld\b.*$' - sed_replace = '\1\tnewhostname.newdomain.tld newhostname' - communicator.expect_command( - %Q(sed -ri 's@#{sed_find}@#{sed_replace}@g' /etc/hosts) - ) - described_class.change_host_name(machine, 'newhostname.newdomain.tld') - end - it "updates mailname to prevent problems with the default mailer" do communicator.expect_command(%q(hostname --fqdn > /etc/mailname)) described_class.change_host_name(machine, 'newhostname.newdomain.tld') @@ -22,4 +13,77 @@ shared_examples "a debian-like host name change" do described_class.change_host_name(machine, 'oldhostname.olddomain.tld') communicator.received_commands.should == ['hostname -f'] end + + describe "flipping out the old hostname in /etc/hosts" do + let(:sed_command) do + # Here we run the change_host_name through and extract the recorded sed + # command from the dummy communicator + described_class.change_host_name(machine, 'newhostname.newdomain.tld') + communicator.received_commands.find { |cmd| cmd =~ /^sed/ } + end + + # Now we extract the regexp from that sed command so we can do some + # verification on it + let(:expression) { sed_command.sub(%r{^sed -ri '\(.*\)' /etc/hosts$}, "\1") } + let(:search) { Regexp.new(expression.split('@')[1], Regexp::EXTENDED) } + let(:replace) { expression.split('@')[2] } + + it "works on an simple /etc/hosts file" do + original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 oldhostname.olddomain.tld oldhostname + ETC_HOSTS + + modified_etc_hosts = original_etc_hosts.gsub(search, replace) + + modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 newhostname.newdomain.tld newhostname + RESULT + end + + it "does not modify lines which contain similar hostnames" do + original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 oldhostname.olddomain.tld oldhostname + + # common prefix, but different fqdn + 192.168.12.34 oldhostname.olddomain.tld.different + + # different characters at the dot + 192.168.34.56 oldhostname-olddomain.tld + ETC_HOSTS + + modified_etc_hosts = original_etc_hosts.gsub(search, replace) + + modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 newhostname.newdomain.tld newhostname + + # common prefix, but different fqdn + 192.168.12.34 oldhostname.olddomain.tld.different + + # different characters at the dot + 192.168.34.56 oldhostname-olddomain.tld + RESULT + end + + context "when the old fqdn has a trailing dot" do + let(:old_hostname) { 'oldhostname.withtrailing.dot.' } + + it "modifies /etc/hosts properly" do + original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 oldhostname.withtrailing.dot. oldhostname + ETC_HOSTS + + modified_etc_hosts = original_etc_hosts.gsub(search, replace) + + modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 newhostname.newdomain.tld newhostname + RESULT + end + end + end end diff --git a/test/unit/plugins/guests/ubuntu/cap/change_host_name_test.rb b/test/unit/plugins/guests/ubuntu/cap/change_host_name_test.rb index 1e59bf3c4..2cc631e3d 100644 --- a/test/unit/plugins/guests/ubuntu/cap/change_host_name_test.rb +++ b/test/unit/plugins/guests/ubuntu/cap/change_host_name_test.rb @@ -7,10 +7,11 @@ describe "VagrantPlugins::GuestUbuntu::Cap::ChangeHostName" do end let(:machine) { double("machine") } let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:old_hostname) {'oldhostname.olddomain.tld' } before do machine.stub(:communicate).and_return(communicator) - communicator.stub_command('hostname -f', stdout: 'oldhostname.olddomain.tld') + communicator.stub_command('hostname -f', stdout: old_hostname) end after do