diff --git a/plugins/guests/alpine/cap/change_host_name.rb b/plugins/guests/alpine/cap/change_host_name.rb index a08086998..e139caaa4 100644 --- a/plugins/guests/alpine/cap/change_host_name.rb +++ b/plugins/guests/alpine/cap/change_host_name.rb @@ -1,7 +1,11 @@ +require 'vagrant/util/guest_hosts' + module VagrantPlugins module GuestAlpine module Cap class ChangeHostName + include Vagrant::Util::GuestHosts::Linux + def self.change_host_name(machine, name) new(machine, name).change! end @@ -14,10 +18,10 @@ module VagrantPlugins end def change! + update_etc_hosts return unless should_change? update_etc_hostname - update_etc_hosts refresh_hostname_service update_mailname renew_dhcp @@ -48,17 +52,12 @@ module VagrantPlugins # 127.0.0.1 localhost # 127.0.1.1 host.fqdn.com host.fqdn host def update_etc_hosts - if machine.communicate.test("grep '#{current_hostname}' /etc/hosts") - # Current hostname entry is in /etc/hosts - ip_address = '([0-9]{1,3}\.){3}[0-9]{1,3}' - search = "^(#{ip_address})\\s+#{Regexp.escape(current_hostname)}(\\s.*)?$" - replace = "\\1 #{new_hostname} #{short_hostname}" - expression = ['s', search, replace, 'g'].join('@') - - machine.communicate.sudo("sed -ri '#{expression}' /etc/hosts") + comm = machine.communicate + network_with_hostname = machine.config.vm.networks.map {|_, c| c if c[:hostname] }.compact[0] + if network_with_hostname + replace_host(comm, new_hostname, network_with_hostname[:ip]) else - # Current hostname entry isn't in /etc/hosts, just append it - machine.communicate.sudo("echo '127.0.1.1 #{new_hostname} #{short_hostname}' >>/etc/hosts") + add_hostname_to_loopback_interface(comm, new_hostname) end end diff --git a/test/unit/plugins/guests/alpine/cap/change_host_name_test.rb b/test/unit/plugins/guests/alpine/cap/change_host_name_test.rb index 3f159ad75..65a32fadc 100644 --- a/test/unit/plugins/guests/alpine/cap/change_host_name_test.rb +++ b/test/unit/plugins/guests/alpine/cap/change_host_name_test.rb @@ -1,127 +1,70 @@ require_relative "../../../../base" describe 'VagrantPlugins::GuestAlpine::Cap::ChangeHostname' do - let(:described_class) do - VagrantPlugins::GuestAlpine::Plugin.components.guest_capabilities[:alpine].get(:change_host_name) - end - let(:machine) { double('machine') } - let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } - let(:old_hostname) { 'oldhostname.olddomain.tld' } + let(:described_class) do + VagrantPlugins::GuestAlpine::Plugin.components.guest_capabilities[:alpine].get(:change_host_name) + end + let(:machine) { double('machine') } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:old_hostname) { 'oldhostname.olddomain.tld' } + let(:networks) {[ + [:forwarded_port, {:guest=>22, :host=>2222, :host_ip=>"127.0.0.1", :id=>"ssh", :auto_correct=>true, :protocol=>"tcp"}] + ]} - before do - allow(machine).to receive(:communicate).and_return(communicator) - communicator.stub_command('hostname -f', stdout: old_hostname) + before do + allow(machine).to receive(:communicate).and_return(communicator) + communicator.stub_command('hostname -f', stdout: old_hostname) + allow(machine).to receive_message_chain(:config, :vm, :networks).and_return(networks) + end + + after do + communicator.verify_expectations! + end + + describe '.change_host_name' do + it 'updates /etc/hostname on the machine' do + communicator.expect_command("echo 'newhostname' > /etc/hostname") + described_class.change_host_name(machine, 'newhostname.newdomain.tld') end - after do - communicator.verify_expectations! + it 'only tries to update /etc/hosts when the provided hostname is not different' do + described_class.change_host_name(machine, 'oldhostname.olddomain.tld') + expect(communicator.received_commands[0]).to eq('hostname -f') + expect(communicator.received_commands.length).to eq(2) end - describe '.change_host_name' do - it 'updates /etc/hostname on the machine' do - communicator.expect_command("echo 'newhostname' > /etc/hostname") + it 'refreshes the hostname service with the hostname command' do + communicator.expect_command('hostname -F /etc/hostname') + described_class.change_host_name(machine, 'newhostname.newdomain.tld') + end + + it 'renews dhcp on the system with the new hostname' do + communicator.expect_command('ifdown -a; ifup -a; ifup eth0') + described_class.change_host_name(machine, 'newhostname.newdomain.tld') + end + + describe 'flipping out the old hostname in /etc/hosts' do + context "minimal network config" do + it "sets the hostname" do + described_class.change_host_name(machine, 'newhostname.newdomain.tld') + add_to_loopback_cmd = communicator.received_commands.find { |c| c =~ /127.0.\$\{i\}.1/ } + expect(add_to_loopback_cmd).to_not eq(nil) + end + end + + context "multiple networks configured with hostname" do + let(:networks) {[ + [:forwarded_port, {:guest=>22, :host=>2222, :host_ip=>"127.0.0.1", :id=>"ssh", :auto_correct=>true, :protocol=>"tcp"}], + [:public_network, {:ip=>"192.168.0.1", :hostname=>true, :protocol=>"tcp", :id=>"93a4ad88-0774-4127-a161-ceb715ff372f"}], + [:public_network, {:ip=>"192.168.0.2", :protocol=>"tcp", :id=>"5aebe848-7d85-4425-8911-c2003d924120"}] + ]} + + it "sets the hostname" do described_class.change_host_name(machine, 'newhostname.newdomain.tld') + add_to_loopback_cmd = communicator.received_commands.find { |c| c =~ /sed -i '\/newhostname.newdomain.tld\/d'/ } + expect(add_to_loopback_cmd).to_not eq(nil) end - - it 'does nothing when the provided hostname is not different' do - described_class.change_host_name(machine, 'oldhostname.olddomain.tld') - expect(communicator.received_commands).to eq(['hostname -f']) - end - - it 'refreshes the hostname service with the hostname command' do - communicator.expect_command('hostname -F /etc/hostname') - described_class.change_host_name(machine, 'newhostname.newdomain.tld') - end - - it 'renews dhcp on the system with the new hostname' do - communicator.expect_command('ifdown -a; ifup -a; ifup eth0') - described_class.change_host_name(machine, 'newhostname.newdomain.tld') - 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] } - - let(:grep_command) { "grep '#{old_hostname}' /etc/hosts" } - - before do - communicator.stub_command(grep_command, exit_code: 0) - end - - 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) - - expect(modified_etc_hosts).to eq <<-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) - - expect(modified_etc_hosts).to eq <<-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 - - it "appends 127.0.1.1 if it isn't there" do - communicator.stub_command(grep_command, exit_code: 1) - described_class.change_host_name(machine, 'newhostname.newdomain.tld') - - sed = communicator.received_commands.find { |cmd| cmd =~ /^sed/ } - expect(sed).to be_nil - - echo = communicator.received_commands.find { |cmd| cmd =~ /^echo/ } - expect(echo).to_not be_nil - 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) - - expect(modified_etc_hosts).to eq <<-RESULT.gsub(/^ */, '') - 127.0.0.1 localhost - 127.0.1.1 newhostname.newdomain.tld newhostname - RESULT - end - end - end + end end + end end