From 62b7e3516904465f31b4a46a9993ebb4d58a9296 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 9 Oct 2019 09:56:59 -0700 Subject: [PATCH 1/3] Fixes #11094: Determine prefix for docker public networks Prior to this commit, the docker action was using the method `prefix` on an IPv4 and IPv6 address. This works fine for ruby versions 2.5 and newer, however the ruby shipped with Vagrant is before 2.5, and therefore the IPv4 and IPv6 classes do not have the prefix method, resulting in an error. This commit fixes that by using a different method of determining the prefix. --- plugins/providers/docker/action/prepare_networks.rb | 7 ++++--- .../providers/docker/action/prepare_networks_test.rb | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/providers/docker/action/prepare_networks.rb b/plugins/providers/docker/action/prepare_networks.rb index 4d1904e85..e52126f47 100644 --- a/plugins/providers/docker/action/prepare_networks.rb +++ b/plugins/providers/docker/action/prepare_networks.rb @@ -191,7 +191,8 @@ module VagrantPlugins base_opts[:opt] = "parent=#{bridge_interface.name}" subnet = IPAddr.new(bridge_interface.addr.ip_address << "/" << bridge_interface.netmask.ip_unpack.first) - base_opts[:subnet] = "#{subnet}/#{subnet.prefix}" + prefix = subnet.ipv4? ? 24 : 64 + base_opts[:subnet] = "#{subnet}/#{prefix}" subnet_addr = IPAddr.new(base_opts[:subnet]) base_opts[:driver] = "macvlan" base_opts[:gateway] = subnet_addr.succ.to_s @@ -282,7 +283,6 @@ module VagrantPlugins begin range = IPAddr.new(range) if !subnet.include?(range) - puts "we in here" env[:ui].warn(I18n.t( "docker_provider.network_bridge_iprange_outofbounds", subnet: network_options[:subnet], @@ -297,7 +297,8 @@ module VagrantPlugins range = nil end end - "#{range}/#{range.prefix}" + prefix = range.ipv4? ? 24 : 64 + "#{range}/#{prefix}" end # Execute the action diff --git a/test/unit/plugins/providers/docker/action/prepare_networks_test.rb b/test/unit/plugins/providers/docker/action/prepare_networks_test.rb index 095ca4335..039669417 100644 --- a/test/unit/plugins/providers/docker/action/prepare_networks_test.rb +++ b/test/unit/plugins/providers/docker/action/prepare_networks_test.rb @@ -305,7 +305,7 @@ describe VagrantPlugins::DockerProvider::Action::PrepareNetworks do describe "#process_public_network" do let(:options) { {:ip=>"172.30.130.2", :subnet=>"172.30.0.0/16", :driver=>"bridge", :id=>"30e017d5-488f-5a2f-a3ke-k8dce8246b60"} } - let(:ipaddr) { double("ipaddr", prefix: 22, succ: "10.1.10.2", ipv6?: false) } + let(:ipaddr) { double("ipaddr", prefix: 22, succ: "10.1.10.2", ipv4?: true, ipv6?: false) } it "raises an error if there are no network interfaces" do expect(subject).to receive(:list_interfaces).and_return([]) @@ -331,7 +331,7 @@ describe VagrantPlugins::DockerProvider::Action::PrepareNetworks do describe "#request_public_gateway" do let(:options) { {:ip=>"172.30.130.2", :subnet=>"172.30.0.0/16", :driver=>"bridge", :id=>"30e017d5-488f-5a2f-a3ke-k8dce8246b60"} } let(:ipaddr) { double("ipaddr", to_s: "172.30.130.2", prefix: 22, succ: "172.30.130.3", - ipv6?: false) } + ipv4?: true, ipv6?: false) } it "requests a gateway" do allow(IPAddr).to receive(:new).and_return(ipaddr) @@ -347,7 +347,7 @@ describe VagrantPlugins::DockerProvider::Action::PrepareNetworks do describe "#request_public_iprange" do let(:options) { {:ip=>"172.30.130.2", :subnet=>"172.30.0.0/16", :driver=>"bridge", :id=>"30e017d5-488f-5a2f-a3ke-k8dce8246b60"} } let(:ipaddr) { double("ipaddr", to_s: "172.30.100.2", prefix: 22, succ: "172.30.100.3", - ipv6?: false) } + ipv4?: true, ipv6?: false) } let(:subnet) { double("ipaddr", to_s: "172.30.130.2", prefix: 22, succ: "172.30.130.3", ipv6?: false) } From 8458a216579ff823c718fcfcb69e01a4ed0ffe63 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 9 Oct 2019 15:40:36 -0700 Subject: [PATCH 2/3] Determine prefix with netmask --- .../providers/docker/action/prepare_networks.rb | 17 +++++++++++------ .../docker/action/prepare_networks_test.rb | 12 ++++++++++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/plugins/providers/docker/action/prepare_networks.rb b/plugins/providers/docker/action/prepare_networks.rb index e52126f47..a61aa82e9 100644 --- a/plugins/providers/docker/action/prepare_networks.rb +++ b/plugins/providers/docker/action/prepare_networks.rb @@ -191,7 +191,8 @@ module VagrantPlugins base_opts[:opt] = "parent=#{bridge_interface.name}" subnet = IPAddr.new(bridge_interface.addr.ip_address << "/" << bridge_interface.netmask.ip_unpack.first) - prefix = subnet.ipv4? ? 24 : 64 + netmask = bridge_interface.netmask.ip_unpack.first + prefix = IPAddr.new("255.255.255.255/#{netmask}").to_i.to_s(2).count("1") base_opts[:subnet] = "#{subnet}/#{prefix}" subnet_addr = IPAddr.new(base_opts[:subnet]) base_opts[:driver] = "macvlan" @@ -214,7 +215,7 @@ module VagrantPlugins network_options, bridge_interface.name, env) end network_options[:ip_range] = request_public_iprange( - network_options, bridge_interface.name, env) + network_options, bridge_interface, env) end end [network_name, network_options] @@ -259,7 +260,7 @@ module VagrantPlugins # public network # # @param [Hash] network_options Docker scoped networking options - # @param [String] interface The bridge interface used + # @param [Socket::Ifaddr] interface The bridge interface used # @param [Hash] env Local call env # @return [String] Address range def request_public_iprange(network_options, interface, env) @@ -273,7 +274,7 @@ module VagrantPlugins while !range range = env[:ui].ask(I18n.t( "docker_provider.network_bridge_iprange_request", - interface: interface, + interface: interface.name, default_range: network_options[:subnet]) + " ", prefix: false ).strip @@ -283,10 +284,12 @@ module VagrantPlugins begin range = IPAddr.new(range) if !subnet.include?(range) + netmask = interface.netmask.ip_unpack.first + prefix = IPAddr.new("255.255.255.255/#{netmask}").to_i.to_s(2).count("1") env[:ui].warn(I18n.t( "docker_provider.network_bridge_iprange_outofbounds", subnet: network_options[:subnet], - range: "#{range}/#{range.prefix}" + range: "#{range}/#{prefix}" ) + "\n", prefix: false) range = nil end @@ -297,7 +300,9 @@ module VagrantPlugins range = nil end end - prefix = range.ipv4? ? 24 : 64 + + netmask = interface.netmask.ip_unpack.first + prefix = IPAddr.new("255.255.255.255/#{netmask}").to_i.to_s(2).count("1") "#{range}/#{prefix}" end diff --git a/test/unit/plugins/providers/docker/action/prepare_networks_test.rb b/test/unit/plugins/providers/docker/action/prepare_networks_test.rb index 039669417..17424b9a2 100644 --- a/test/unit/plugins/providers/docker/action/prepare_networks_test.rb +++ b/test/unit/plugins/providers/docker/action/prepare_networks_test.rb @@ -305,7 +305,8 @@ describe VagrantPlugins::DockerProvider::Action::PrepareNetworks do describe "#process_public_network" do let(:options) { {:ip=>"172.30.130.2", :subnet=>"172.30.0.0/16", :driver=>"bridge", :id=>"30e017d5-488f-5a2f-a3ke-k8dce8246b60"} } - let(:ipaddr) { double("ipaddr", prefix: 22, succ: "10.1.10.2", ipv4?: true, ipv6?: false) } + let(:ipaddr) { double("ipaddr", prefix: 22, succ: "10.1.10.2", ipv4?: true, + ipv6?: false, to_i: 4294967040) } it "raises an error if there are no network interfaces" do expect(subject).to receive(:list_interfaces).and_return([]) @@ -351,13 +352,20 @@ describe VagrantPlugins::DockerProvider::Action::PrepareNetworks do let(:subnet) { double("ipaddr", to_s: "172.30.130.2", prefix: 22, succ: "172.30.130.3", ipv6?: false) } + let(:ipaddr_prefix) { double("ipaddr_prefix", to_s: "255.255.255.255/255.255.255.0", + to_i: 4294967040 ) } + + let(:netmask) { double("netmask", ip_unpack: ["255.255.255.0", 0]) } + let(:interface) { double("interface", name: "bridge", netmask: netmask) } + it "requests a public ip range" do allow(IPAddr).to receive(:new).with(options[:subnet]).and_return(subnet) allow(IPAddr).to receive(:new).with("172.30.130.2").and_return(ipaddr) + allow(IPAddr).to receive(:new).with("255.255.255.255/255.255.255.0").and_return(ipaddr_prefix) allow(subnet).to receive(:include?).and_return(true) allow(machine.ui).to receive(:ask).and_return(options[:ip]) - addr = subject.request_public_iprange(options, "bridge", env) + addr = subject.request_public_iprange(options, interface, env) end end end From e6d47329eebda56babe96c1f7d4734eb8299426d Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 9 Oct 2019 16:07:21 -0700 Subject: [PATCH 3/3] Add note about removing prefix workaround --- plugins/providers/docker/action/prepare_networks.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/providers/docker/action/prepare_networks.rb b/plugins/providers/docker/action/prepare_networks.rb index a61aa82e9..036651d46 100644 --- a/plugins/providers/docker/action/prepare_networks.rb +++ b/plugins/providers/docker/action/prepare_networks.rb @@ -153,6 +153,10 @@ module VagrantPlugins # Generate configuration for public network # + # TODO: When the Vagrant installer upgrades to Ruby 2.5.x, + # remove all instances of the roundabout way of determining a prefix + # and instead just use the built-in `.prefix` method + # # @param [Hash] root_options Root networking options # @param [Hash] net_options Docker scoped networking options # @param [Hash] env Local call env @@ -259,6 +263,10 @@ module VagrantPlugins # Request the IP range allowed for use by docker when creating a new # public network # + # TODO: When the Vagrant installer upgrades to Ruby 2.5.x, + # remove all instances of the roundabout way of determining a prefix + # and instead just use the built-in `.prefix` method + # # @param [Hash] network_options Docker scoped networking options # @param [Socket::Ifaddr] interface The bridge interface used # @param [Hash] env Local call env