guests/arch: Configure networks in one command

This commit updates the procedure for configuring arch networks to occur
in a single command. Previously, each network was configured
independently. If, for some reason, one of the networks destroyed the
SSH connection, the box would be irrecoverable. This commit does not
alleviate that behavior, but attempts to mitigate it by running all
network-related configuration commands in a single communicator (SSH)
session.

The new procedure looks like this:

1. Upload a temp file to /tmp/vagrant-network-id... for each interface
  on the guest.
2. Compile a commands array (of bash) to execute after all network
  configurations have been uploaded.
3. Concatenate all the commands together in a single communicator
  session.

This was tested against `terrywant/archlinux` using the following Vagrantfile:

```ruby
Vagrant.configure(2) do |config|
  config.vm.box = "terrywang/archlinux"
  config.vm.hostname = "banana-ramama.example.com"

  config.vm.network "private_network", type: "dhcp"

  config.vm.network "private_network", ip: "33.33.33.10"

  config.vm.provision "file", source: "Vagrantfile", destination: "/tmp/vf"
  config.vm.provision "shell", inline: "echo hi"
end
```
This commit is contained in:
Seth Vargo 2016-05-30 22:11:48 -04:00
parent 41d61120a5
commit d77ad5c941
No known key found for this signature in database
GPG Key ID: 905A90C2949E8787
3 changed files with 62 additions and 15 deletions

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
require "tempfile"
require_relative "../../../../lib/vagrant/util/template_renderer"
@ -10,32 +9,45 @@ module VagrantPlugins
include Vagrant::Util
def self.configure_networks(machine, networks)
tempfiles = []
comm = machine.communicate
commands = []
interfaces = []
machine.communicate.sudo("ip -o -0 addr | grep -v LOOPBACK | awk '{print $2}' | sed 's/://'") do |_, result|
interfaces = result.split("\n")
# The result will be something like:
# eth0\nenp0s8\nenp0s9
comm.sudo("ip -o -0 addr | grep -v LOOPBACK | awk '{print $2}' | sed 's/://'") do |_, stdout|
interfaces = stdout.split("\n")
end
networks.each.with_index do |network, i|
network[:device] = interfaces[network[:interface]]
entry = TemplateRenderer.render("guests/arch/network_#{network[:type]}",
options: network)
options: network,
)
remote_path = "/tmp/vagrant-network-#{Time.now.to_i}-#{i}"
remote_path = "/tmp/vagrant-network-#{network[:device]}-#{Time.now.to_i}-#{i}"
Tempfile.open("vagrant-arch-configure-networks") do |f|
f.binmode
f.write(entry)
f.fsync
f.close
machine.communicate.upload(f.path, remote_path)
comm.upload(f.path, remote_path)
end
machine.communicate.sudo("mv #{remote_path} /etc/netctl/#{network[:device]}")
machine.communicate.sudo("ip link set #{network[:device]} down && netctl restart #{network[:device]} && netctl enable #{network[:device]}")
commands << <<-EOH.gsub(/^ {14}/, '')
# Configure #{network[:device]}
mv '#{remote_path}' '/etc/netctl/#{network[:device]}'
ip link set '#{network[:device]}' down
netctl restart '#{network[:device]}'
netctl enable '#{network[:device]}'
EOH
end
# Run all the network modification commands in one communicator call.
comm.sudo(commands.join("\n"))
end
end
end

View File

@ -9,16 +9,16 @@ describe "VagrantPlugins::GuestArch::Cap::ConfigureNetworks" do
end
let(:machine) { double("machine") }
let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
before do
allow(machine).to receive(:communicate).and_return(communicator)
communicator.stub_command("ip -o -0 addr | grep -v LOOPBACK | awk '{print $2}' | sed 's/://'",
allow(machine).to receive(:communicate).and_return(comm)
comm.stub_command("ip -o -0 addr | grep -v LOOPBACK | awk '{print $2}' | sed 's/://'",
stdout: "eth1\neth2")
end
after do
communicator.verify_expectations!
comm.verify_expectations!
end
describe ".configure_networks" do
@ -40,9 +40,16 @@ describe "VagrantPlugins::GuestArch::Cap::ConfigureNetworks" do
end
it "creates and starts the networks" do
communicator.expect_command("ip link set eth1 down && netctl restart eth1 && netctl enable eth1")
communicator.expect_command("ip link set eth2 down && netctl restart eth2 && netctl enable eth2")
described_class.configure_networks(machine, [network_1, network_2])
expect(comm.received_commands[1]).to match(/mv (.+) '\/etc\/netctl\/eth1'/)
expect(comm.received_commands[1]).to match(/ip link set 'eth1' down/)
expect(comm.received_commands[1]).to match(/netctl restart 'eth1'/)
expect(comm.received_commands[1]).to match(/netctl enable 'eth1'/)
expect(comm.received_commands[1]).to match(/mv (.+) '\/etc\/netctl\/eth2'/)
expect(comm.received_commands[1]).to match(/ip link set 'eth2' down/)
expect(comm.received_commands[1]).to match(/netctl restart 'eth2'/)
expect(comm.received_commands[1]).to match(/netctl enable 'eth2'/)
end
end
end

View File

@ -0,0 +1,28 @@
require_relative "../../../../base"
describe "VagrantPlugins::GuestCoreOS::Cap::ChangeHostName" do
let(:described_class) do
VagrantPlugins::GuestCoreOS::Plugin
.components
.guest_capabilities[:coreos]
.get(:docker_daemon_running)
end
let(:machine) { double("machine") }
let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
before do
allow(machine).to receive(:communicate).and_return(comm)
end
after do
comm.verify_expectations!
end
describe ".docker_daemon_running" do
it "checks /run/docker/sock" do
described_class.docker_daemon_running(machine)
expect(comm.received_commands[0]).to eq("test -S /run/docker.sock")
end
end
end