Docker provider networking support updates
Use `mask` option for defining subnet on network configuration. Allow options to be passed through using hash scoping and docker_network and docker_connect prefixes. Enable public networks. Allow configuration to define pre-existing networks by name.
This commit is contained in:
parent
1224622387
commit
a645ce3c25
|
@ -244,6 +244,7 @@ module VagrantPlugins
|
|||
b2.use PrepareNFSValidIds
|
||||
b2.use SyncedFolderCleanup
|
||||
b2.use PrepareNFSSettings
|
||||
b2.use PrepareNetworks
|
||||
b2.use Login
|
||||
b2.use Build
|
||||
|
||||
|
@ -266,7 +267,7 @@ module VagrantPlugins
|
|||
end
|
||||
end
|
||||
|
||||
b2.use Network
|
||||
b2.use ConnectNetworks
|
||||
b2.use Start
|
||||
b2.use WaitForRunning
|
||||
|
||||
|
@ -294,6 +295,7 @@ module VagrantPlugins
|
|||
action_root = Pathname.new(File.expand_path("../action", __FILE__))
|
||||
autoload :Build, action_root.join("build")
|
||||
autoload :CompareSyncedFolders, action_root.join("compare_synced_folders")
|
||||
autoload :ConnectNetworks, action_root.join("connect_networks")
|
||||
autoload :Create, action_root.join("create")
|
||||
autoload :Destroy, action_root.join("destroy")
|
||||
autoload :DestroyBuildImage, action_root.join("destroy_build_image")
|
||||
|
@ -311,13 +313,13 @@ module VagrantPlugins
|
|||
autoload :IsBuild, action_root.join("is_build")
|
||||
autoload :IsHostMachineCreated, action_root.join("is_host_machine_created")
|
||||
autoload :Login, action_root.join("login")
|
||||
autoload :Pull, action_root.join("pull")
|
||||
autoload :PrepareSSH, action_root.join("prepare_ssh")
|
||||
autoload :Stop, action_root.join("stop")
|
||||
autoload :Network, action_root.join("network")
|
||||
autoload :PrepareNetworks, action_root.join("prepare_networks")
|
||||
autoload :PrepareNFSValidIds, action_root.join("prepare_nfs_valid_ids")
|
||||
autoload :PrepareNFSSettings, action_root.join("prepare_nfs_settings")
|
||||
autoload :PrepareSSH, action_root.join("prepare_ssh")
|
||||
autoload :Pull, action_root.join("pull")
|
||||
autoload :Start, action_root.join("start")
|
||||
autoload :Stop, action_root.join("stop")
|
||||
autoload :WaitForRunning, action_root.join("wait_for_running")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
require 'ipaddr'
|
||||
require 'log4r'
|
||||
|
||||
module VagrantPlugins
|
||||
module DockerProvider
|
||||
module Action
|
||||
class ConnectNetworks
|
||||
|
||||
include Vagrant::Util::ScopedHashOverride
|
||||
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new('vagrant::plugins::docker::connectnetworks')
|
||||
end
|
||||
|
||||
# Generate CLI arguments for creating the docker network.
|
||||
#
|
||||
# @param [Hash] options Options from the network config
|
||||
# @returns[Array<String> Network create arguments
|
||||
def generate_connect_cli_arguments(options)
|
||||
options.map do |key, value|
|
||||
# If value is false, option is not set
|
||||
next if value == false
|
||||
# If value is true, consider feature flag with no value
|
||||
opt = value == true ? [] : [value]
|
||||
opt.unshift("--#{key.to_s.tr("_", "-")}")
|
||||
end.flatten.compact
|
||||
end
|
||||
|
||||
# Execute the action
|
||||
def call(env)
|
||||
# If we are using a host VM, then don't worry about it
|
||||
machine = env[:machine]
|
||||
if machine.provider.host_vm?
|
||||
@logger.debug("Not setting up networks because docker host_vm is in use")
|
||||
return @app.call(env)
|
||||
end
|
||||
|
||||
env[:ui].info(I18n.t("docker_provider.network_connect"))
|
||||
|
||||
connections = env[:docker_connects] || {}
|
||||
|
||||
machine.config.vm.networks.each_with_index do |args, idx|
|
||||
type, options = args
|
||||
next if type != :private_network && type != :public_network
|
||||
|
||||
network_options = scoped_hash_override(options, :docker_connect)
|
||||
network_options.delete_if{|k,_| options.key?(k)}
|
||||
network_name = connections[idx]
|
||||
|
||||
if !network_name
|
||||
raise Errors::NetworkNameMissing,
|
||||
index: idx,
|
||||
container: machine.name
|
||||
end
|
||||
|
||||
@logger.debug("Connecting network #{network_name} to container guest #{machine.name}")
|
||||
if options[:ip]
|
||||
if IPAddr.new(options[:ip]).ipv4?
|
||||
network_options[:ip] = options[:ip]
|
||||
else
|
||||
network_options[:ip6] = options[:ip]
|
||||
end
|
||||
end
|
||||
network_options[:alias] = options[:alias] if options[:alias]
|
||||
connect_opts = generate_connect_cli_arguments(network_options)
|
||||
machine.provider.driver.connect_network(network_name, machine.id, connect_opts)
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -18,22 +18,22 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
machine.config.vm.networks.each do |type, options|
|
||||
# We only handle private networks
|
||||
next if type != :private_network
|
||||
next if type != :private_network && type != :public_network
|
||||
|
||||
if options[:subnet]
|
||||
network_name = "vagrant_network_#{options[:subnet]}"
|
||||
else
|
||||
network_name = "vagrant_network"
|
||||
end
|
||||
machine.env.lock("docker-network-destroy", retry: true) do
|
||||
vagrant_networks = machine.provider.driver.list_network_names.find_all do |n|
|
||||
n.start_with?("vagrant_network")
|
||||
end
|
||||
|
||||
# Only cleans up networks defined by Vagrant
|
||||
if machine.provider.driver.existing_network?(network_name) &&
|
||||
!machine.provider.driver.network_used?(network_name)
|
||||
env[:ui].info(I18n.t("docker_provider.network_destroy", network_name: network_name))
|
||||
machine.provider.driver.rm_network(network_name)
|
||||
else
|
||||
@logger.debug("Network #{network_name} not found")
|
||||
vagrant_networks.each do |network_name|
|
||||
if machine.provider.driver.existing_named_network?(network_name) &&
|
||||
!machine.provider.driver.network_used?(network_name)
|
||||
env[:ui].info(I18n.t("docker_provider.network_destroy", network_name: network_name))
|
||||
machine.provider.driver.rm_network(network_name)
|
||||
else
|
||||
@logger.debug("Network #{network_name} not found or in use")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
require 'log4r'
|
||||
|
||||
module VagrantPlugins
|
||||
module DockerProvider
|
||||
module Action
|
||||
class Network
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new('vagrant::plugins::docker::network')
|
||||
end
|
||||
|
||||
# @param[Hash] options - options from the network config
|
||||
# @returns[Array] cli_opts - an array of strings used for the network commnad
|
||||
def generate_create_cli_arguments(options)
|
||||
cli_opts = []
|
||||
ignored_options = ["ip", "protocol", "id", "alias"].map(&:freeze).freeze
|
||||
|
||||
# Splits the networking options to generate the proper CLI flags for docker
|
||||
options.each do |opt, value|
|
||||
opt = opt.to_s
|
||||
if (opt == "type" && value == "dhcp") || ignored_options.include?(opt)
|
||||
# `docker network create` doesn't care about these options
|
||||
next
|
||||
else
|
||||
cli_opts.concat(["--#{opt}=#{value.to_s}"])
|
||||
end
|
||||
end
|
||||
|
||||
return cli_opts
|
||||
end
|
||||
|
||||
# @param[Hash] options - options from the network config
|
||||
# @returns[Array] cli_opts - an array of strings used for the network commnad
|
||||
def generate_connect_cli_arguments(options)
|
||||
cli_opts = []
|
||||
|
||||
if options[:ip]
|
||||
cli_opts = ["--ip", options[:ip]]
|
||||
elsif options[:ip6]
|
||||
cli_opts = ["--ip6", options[:ip6]]
|
||||
end
|
||||
|
||||
if options[:alias]
|
||||
cli_opts.concat(["--alias=#{options[:alias]}"])
|
||||
end
|
||||
|
||||
return cli_opts
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# If we are using a host VM, then don't worry about it
|
||||
machine = env[:machine]
|
||||
if machine.provider.host_vm?
|
||||
@logger.debug("Not setting up networks because docker host_vm is in use")
|
||||
return @app.call(env)
|
||||
end
|
||||
|
||||
env[:ui].info(I18n.t("docker_provider.network_configure"))
|
||||
|
||||
machine.config.vm.networks.each do |type, options|
|
||||
# We only handle private networks
|
||||
next if type != :private_network
|
||||
|
||||
cli_opts = generate_create_cli_arguments(options)
|
||||
|
||||
if options[:subnet]
|
||||
existing_network = machine.provider.driver.subnet_defined?(options[:subnet])
|
||||
if !existing_network
|
||||
network_name = "vagrant_network_#{options[:subnet]}"
|
||||
else
|
||||
env[:ui].warn(I18n.t("docker_provider.subnet_exists",
|
||||
network_name: existing_network,
|
||||
subnet: options[:subnet]))
|
||||
network_name = existing_network
|
||||
end
|
||||
elsif options[:type] == "dhcp"
|
||||
network_name = "vagrant_network"
|
||||
else
|
||||
raise Errors::NetworkInvalidOption, container: machine.name
|
||||
end
|
||||
container_id = machine.id
|
||||
|
||||
machine.env.lock("docker-network-create", retry: true) do
|
||||
if !machine.provider.driver.existing_network?(network_name)
|
||||
@logger.debug("Creating network #{network_name}")
|
||||
machine.provider.driver.create_network(network_name, cli_opts)
|
||||
else
|
||||
@logger.debug("Network #{network_name} already created")
|
||||
end
|
||||
end
|
||||
|
||||
@logger.debug("Connecting network #{network_name} to container guest #{machine.name}")
|
||||
connect_opts = generate_connect_cli_arguments(options)
|
||||
machine.provider.driver.connect_network(network_name, container_id, connect_opts)
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,335 @@
|
|||
require 'ipaddr'
|
||||
require 'log4r'
|
||||
|
||||
module VagrantPlugins
|
||||
module DockerProvider
|
||||
module Action
|
||||
class PrepareNetworks
|
||||
|
||||
include Vagrant::Util::ScopedHashOverride
|
||||
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new('vagrant::plugins::docker::preparenetworks')
|
||||
end
|
||||
|
||||
# Generate CLI arguments for creating the docker network.
|
||||
#
|
||||
# @param [Hash] options Options from the network config
|
||||
# @returns[Array<String> Network create arguments
|
||||
def generate_create_cli_arguments(options)
|
||||
options.map do |key, value|
|
||||
# If value is false, option is not set
|
||||
next if value == false
|
||||
# If value is true, consider feature flag with no value
|
||||
opt = value == true ? [] : [value]
|
||||
opt.unshift("--#{key.to_s.tr("_", "-")}")
|
||||
end.flatten.compact
|
||||
end
|
||||
|
||||
# @return [Array<Socket::Ifaddr>] interface list
|
||||
def list_interfaces
|
||||
Socket.getifaddrs.find_all do |i|
|
||||
i.addr.ip? && !i.addr.ipv4_loopback? &&
|
||||
!i.addr.ipv6_loopback? && !i.addr.ipv6_linklocal?
|
||||
end
|
||||
end
|
||||
|
||||
# Validates that a network name exists. If it does not
|
||||
# exist, an exception is raised.
|
||||
#
|
||||
# @param [String] name Name of existing network
|
||||
# @return [Boolean]
|
||||
def validate_network_name!(name)
|
||||
if !env[:machine].provider.driver.existing_named_network?(network_name)
|
||||
raise Errors::NetworkNameUndefined,
|
||||
network_name: name
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
# Validates that the provided options are compatible with a
|
||||
# pre-existing network. Raises exceptions on invalid configurations
|
||||
#
|
||||
# @param [String] network_name Name of the network
|
||||
# @param [Hash] root_options Root networking options
|
||||
# @param [Hash] network_options Docker scoped networking options
|
||||
# @param [Driver] driver Docker driver
|
||||
# @return [Boolean]
|
||||
def validate_network_configuration!(network_name, root_options, network_options, driver)
|
||||
if root_options[:ip] &&
|
||||
driver.network_containing_address(root_options[:ip]) != network_name
|
||||
raise Errors::NetworkAddressInvalid,
|
||||
address: root_options[:ip],
|
||||
network_name: network_name
|
||||
end
|
||||
if network_options[:subnet] &&
|
||||
driver.network_containing_address(network_options[:subnet]) != network_name
|
||||
raise Errors::NetworkSubnetInvalid,
|
||||
subnet: network_options[:subnet],
|
||||
network_name: network_name
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
# Generate configuration for private network
|
||||
#
|
||||
# @param [Hash] root_options Root networking options
|
||||
# @param [Hash] net_options Docker scoped networking options
|
||||
# @param [Hash] env Local call env
|
||||
# @return [String, Hash] Network name and updated network_options
|
||||
def process_private_network(root_options, network_options, env)
|
||||
if root_options[:name] && validate_network_name!(root_options[:name])
|
||||
network_name = root_options[:name]
|
||||
end
|
||||
|
||||
if root_options[:type].to_s == "dhcp"
|
||||
network_name = "vagrant_network" if !network_name
|
||||
return [network_name, network_options]
|
||||
end
|
||||
|
||||
if !root_options[:ip]
|
||||
raise Errors::NetworkIPAddressRequired
|
||||
end
|
||||
|
||||
# Validate the IP address
|
||||
addr = IPAddr.new(root_options[:ip])
|
||||
|
||||
# If address is ipv6, enable ipv6 support
|
||||
network_options[:ipv6] = addr.ipv6?
|
||||
|
||||
# If no mask is provided, attempt to locate any existing
|
||||
# network which contains the assigned IP address
|
||||
if !root_options[:mask] && !network_name
|
||||
network_name = env[:machine].provider.driver.
|
||||
network_containing_address(root_options[:ip])
|
||||
# When no existing network is found, we are creating
|
||||
# a new network. Since no mask was provided, default
|
||||
# to /24 for ipv4 and /64 for ipv6
|
||||
if !network_name
|
||||
root_options[:mask] = addr.ipv4? ? 24 : 64
|
||||
end
|
||||
end
|
||||
|
||||
# With no network name, process options to find or determine
|
||||
# name for new network
|
||||
if !network_name
|
||||
subnet = IPAddr.new("#{root_options[:ip]}/#{root_options[:mask]}")
|
||||
network = "#{subnet}/#{root_options[:mask]}"
|
||||
network_options[:subnet] = network
|
||||
existing_network = env[:machine].provider.driver.
|
||||
network_defined?(network)
|
||||
if !existing_network
|
||||
network_name = "vagrant_network_#{network}"
|
||||
else
|
||||
if !existing_network.to_s.start_with?("vagrant_network")
|
||||
env[:ui].warn(I18n.t("docker_provider.subnet_exists",
|
||||
network_name: existing_network,
|
||||
subnet: network))
|
||||
end
|
||||
network_name = existing_network
|
||||
end
|
||||
end
|
||||
|
||||
[network_name, network_options]
|
||||
end
|
||||
|
||||
# Generate configuration for public network
|
||||
#
|
||||
# @param [Hash] root_options Root networking options
|
||||
# @param [Hash] net_options Docker scoped networking options
|
||||
# @param [Hash] env Local call env
|
||||
# @return [String, Hash] Network name and updated network_options
|
||||
def process_public_network(root_options, net_options, env)
|
||||
if root_options[:name] && validate_network_name!(root_options[:name])
|
||||
network_name = root_options[:name]
|
||||
end
|
||||
if !network_name
|
||||
valid_interfaces = list_interfaces
|
||||
if valid_interfaces.empty?
|
||||
raise Errors::NetworkNoInterfaces
|
||||
elsif valid_interfaces.size == 1
|
||||
bridge_interface = valid_interfaces.first
|
||||
elsif i = valid_interfaces.detect{|i| Array(root_options[:bridge]).include?(i.name) }
|
||||
bridge_interface = i
|
||||
end
|
||||
if !bridge_interface
|
||||
env[:ui].info(I18n.t("vagrant.actions.vm.bridged_networking.available"),
|
||||
prefix: false)
|
||||
valid_interfaces.each_with_index do |int, i|
|
||||
env[:ui].info("#{i + 1}) #{int.name}", prefix: false)
|
||||
end
|
||||
env[:ui].info(I18n.t(
|
||||
"vagrant.actions.vm.bridged_networking.choice_help") + "\n",
|
||||
prefix: false
|
||||
)
|
||||
end
|
||||
while !bridge_interface
|
||||
choice = env[:ui].ask(
|
||||
I18n.t("vagrant.actions.vm.bridged_networking.select_interface") + " ",
|
||||
prefix: false)
|
||||
bridge_interface = valid_interfaces[choice.to_i - 1]
|
||||
end
|
||||
base_opts = Vagrant::Util::HashWithIndifferentAccess.new
|
||||
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}"
|
||||
subnet_addr = IPAddr.new(base_opts[:subnet])
|
||||
base_opts[:driver] = "macvlan"
|
||||
base_opts[:gateway] = subnet_addr.succ.to_s
|
||||
base_opts[:ipv6] = subnet_addr.ipv6?
|
||||
network_options = base_opts.merge(net_options)
|
||||
|
||||
# Check if network already exists for this subnet
|
||||
network_name = env[:machine].provider.driver.
|
||||
network_containing_address(network_options[:gateway])
|
||||
if !network_name
|
||||
network_name = "vagrant_network_public_#{bridge_interface.name}"
|
||||
end
|
||||
|
||||
# If the network doesn't already exist, gather available address range
|
||||
# within subnet which docker can provide addressing
|
||||
if !env[:machine].provider.driver.existing_named_network?(network_name)
|
||||
if !net_options[:gateway]
|
||||
network_options[:gateway] = request_public_gateway(
|
||||
network_options, bridge_interface.name, env)
|
||||
end
|
||||
network_options[:ip_range] = request_public_iprange(
|
||||
network_options, bridge_interface.name, env)
|
||||
end
|
||||
end
|
||||
[network_name, network_options]
|
||||
end
|
||||
|
||||
# Request the gateway address for the public network
|
||||
#
|
||||
# @param [Hash] network_options Docker scoped networking options
|
||||
# @param [String] interface The bridge interface used
|
||||
# @param [Hash] env Local call env
|
||||
# @return [String] Gateway address
|
||||
def request_public_gateway(network_options, interface, env)
|
||||
subnet = IPAddr.new(network_options[:subnet])
|
||||
gateway = nil
|
||||
while !gateway
|
||||
gateway = env[:ui].ask(I18n.t(
|
||||
"docker_provider.network_bridge_gateway_request",
|
||||
interface: interface,
|
||||
default_gateway: network_options[:gateway]) + " ",
|
||||
prefix: false
|
||||
).strip
|
||||
if gateway.empty?
|
||||
gateway = network_options[:gateway]
|
||||
end
|
||||
begin
|
||||
gateway = IPAddr.new(gateway)
|
||||
if !subnet.include?(gateway)
|
||||
env[:ui].warn(I18n.t("docker_provider.network_bridge_gateway_outofbounds",
|
||||
gateway: gateway,
|
||||
subnet: network_options[:subnet]) + "\n", prefix: false)
|
||||
end
|
||||
rescue IPAddr::InvalidAddressError
|
||||
env[:ui].warn(I18n.t("docker_provider.network_bridge_gateway_invalid",
|
||||
gateway: gateway) + "\n", prefix: false)
|
||||
gateway = nil
|
||||
end
|
||||
end
|
||||
gateway.to_s
|
||||
end
|
||||
|
||||
# Request the IP range allowed for use by docker when creating a new
|
||||
# public network
|
||||
#
|
||||
# @param [Hash] network_options Docker scoped networking options
|
||||
# @param [String] interface The bridge interface used
|
||||
# @param [Hash] env Local call env
|
||||
# @return [String] Address range
|
||||
def request_public_iprange(network_options, interface, env)
|
||||
return network_options[:ip_range] if network_options[:ip_range]
|
||||
subnet = IPAddr.new(network_options[:subnet])
|
||||
env[:ui].info(I18n.t(
|
||||
"docker_provider.network_bridge_iprange_info") + "\n",
|
||||
prefix: false
|
||||
)
|
||||
range = nil
|
||||
while !range
|
||||
range = env[:ui].ask(I18n.t(
|
||||
"docker_provider.network_bridge_iprange_request",
|
||||
interface: interface,
|
||||
default_range: network_options[:subnet]) + " ",
|
||||
prefix: false
|
||||
).strip
|
||||
if range.empty?
|
||||
range = network_options[:subnet]
|
||||
end
|
||||
begin
|
||||
range = IPAddr.new(range)
|
||||
if !subnet.include?(range)
|
||||
env[:ui].warn(I18n.t(
|
||||
"docker_provider.network_bridge_iprange_outofbounds",
|
||||
subnet: network_options[:subnet],
|
||||
range: "#{range}/#{range.prefix}"
|
||||
) + "\n", prefix: false)
|
||||
range = nil
|
||||
end
|
||||
rescue IPAddr::InvalidAddressError
|
||||
env[:ui].warn(I18n.t(
|
||||
"docker_provider.network_bridge_iprange_invalid",
|
||||
range: range) + "\n", prefix: false)
|
||||
range = nil
|
||||
end
|
||||
end
|
||||
"#{range}/#{range.prefix}"
|
||||
end
|
||||
|
||||
# Execute the action
|
||||
def call(env)
|
||||
# If we are using a host VM, then don't worry about it
|
||||
machine = env[:machine]
|
||||
if machine.provider.host_vm?
|
||||
@logger.debug("Not setting up networks because docker host_vm is in use")
|
||||
return @app.call(env)
|
||||
end
|
||||
|
||||
connections = {}
|
||||
machine.env.lock("docker-network-create", retry: true) do
|
||||
env[:ui].info(I18n.t("docker_provider.network_create"))
|
||||
machine.config.vm.networks.each_with_index do |net_info, net_idx|
|
||||
type, options = net_info
|
||||
network_options = scoped_hash_override(options, :docker_network)
|
||||
network_options.delete_if{|k,_| options.key?(k)}
|
||||
|
||||
case type
|
||||
when :public_network
|
||||
network_name, network_options = process_public_network(
|
||||
options, network_options, env)
|
||||
when :private_network
|
||||
network_name, network_options = process_private_network(
|
||||
options, network_options, env)
|
||||
else
|
||||
next # unsupported type so ignore
|
||||
end
|
||||
|
||||
if !network_name
|
||||
raise Errors::NetworkInvalidOption, container: machine.name
|
||||
end
|
||||
|
||||
if !machine.provider.driver.existing_named_network?(network_name)
|
||||
@logger.debug("Creating network #{network_name}")
|
||||
cli_opts = generate_create_cli_arguments(network_options)
|
||||
machine.provider.driver.create_network(network_name, cli_opts)
|
||||
else
|
||||
@logger.debug("Network #{network_name} already created")
|
||||
validate_network_configuration!(network_name, options, network_options, machine.provider.driver)
|
||||
end
|
||||
connections[net_idx] = network_name
|
||||
end
|
||||
end
|
||||
|
||||
env[:docker_connects] = connections
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -157,14 +157,20 @@ module VagrantPlugins
|
|||
raise if !e.to_s.include?("No such image")
|
||||
end
|
||||
|
||||
# Inspect the provided container
|
||||
#
|
||||
# @param [String] cid ID or name of container
|
||||
# @return [Hash]
|
||||
def inspect_container(cid)
|
||||
JSON.parse(execute('docker', 'inspect', cid)).first
|
||||
end
|
||||
|
||||
# @return [Array<String>] list of all container IDs
|
||||
def all_containers
|
||||
execute('docker', 'ps', '-a', '-q', '--no-trunc').to_s.split
|
||||
end
|
||||
|
||||
# @return [String] IP address of the docker bridge
|
||||
def docker_bridge_ip
|
||||
output = execute('/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'docker0')
|
||||
if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
|
||||
|
@ -206,12 +212,18 @@ module VagrantPlugins
|
|||
command = ['docker', 'network', 'inspect'] + Array(network)
|
||||
command = command.push(*opts)
|
||||
output = execute(*command)
|
||||
output
|
||||
begin
|
||||
JSON.load(output)
|
||||
rescue JSON::ParseError
|
||||
@logger.warn("Failed to parse network inspection of network: #{network}")
|
||||
@logger.debug("Failed network output content: `#{output.inspect}`")
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# @param [Array] opts - An array of flags used for listing networks
|
||||
def list_network(opts=nil)
|
||||
command = ['docker', 'network', 'ls'].push(*opts)
|
||||
# @param [String] opts - Flags used for listing networks
|
||||
def list_network(*opts)
|
||||
command = ['docker', 'network', 'ls', *opts]
|
||||
output = execute(*command)
|
||||
output
|
||||
end
|
||||
|
@ -226,12 +238,11 @@ module VagrantPlugins
|
|||
output
|
||||
end
|
||||
|
||||
# TODO: Note...cli can optionally take a list of networks to delete.
|
||||
# We might need this later, but for now our helper takes 1 network at a time
|
||||
# Delete network(s)
|
||||
#
|
||||
# @param [String] network - name of network to remove
|
||||
def rm_network(network)
|
||||
command = ['docker', 'network', 'rm', network]
|
||||
def rm_network(*network)
|
||||
command = ['docker', 'network', 'rm', *network]
|
||||
output = execute(*command)
|
||||
output
|
||||
end
|
||||
|
@ -246,34 +257,55 @@ module VagrantPlugins
|
|||
# ######################
|
||||
|
||||
# @param [String] subnet_string - Subnet to look for
|
||||
def subnet_defined?(subnet_string)
|
||||
all_networks = list_network(["--format={{.Name}}"])
|
||||
all_networks = all_networks.split("\n")
|
||||
def network_defined?(subnet_string)
|
||||
all_networks = list_network_names
|
||||
|
||||
results = inspect_network(all_networks)
|
||||
begin
|
||||
networks_info = JSON.parse(results)
|
||||
networks_info.each do |network|
|
||||
config = network["IPAM"]["Config"]
|
||||
if (config.size > 0 &&
|
||||
config.first["Subnet"] == subnet_string)
|
||||
@logger.debug("Found existing network #{network["Name"]} already configured with #{subnet_string}")
|
||||
return network["Name"]
|
||||
end
|
||||
network_info = inspect_network(all_networks)
|
||||
network_info.each do |network|
|
||||
config = network["IPAM"]["Config"]
|
||||
if (config.size > 0 &&
|
||||
config.first["Subnet"] == subnet_string)
|
||||
@logger.debug("Found existing network #{network["Name"]} already configured with #{subnet_string}")
|
||||
return network["Name"]
|
||||
end
|
||||
rescue JSON::ParserError => e
|
||||
@logger.warn("Could not properly parse response from `docker network inspect #{all_networks.join(" ")}`")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
# Looks to see if a docker network has already been defined
|
||||
# Locate network which contains given address
|
||||
#
|
||||
# @param [String] network - name of network to look for
|
||||
def existing_network?(network)
|
||||
result = list_network(["--format={{.Name}}"])
|
||||
#TODO: we should be more explicit here if we can
|
||||
result.match?(/#{network}/)
|
||||
# @param [String] address IP address
|
||||
# @return [String] network name
|
||||
def network_containing_address(address)
|
||||
names = list_network_names
|
||||
networks = inspect_network(names)
|
||||
return if !networks
|
||||
networks.each do |net|
|
||||
next if !net["IPAM"]
|
||||
config = net["IPAM"]["Config"]
|
||||
next if !config || config.size < 1
|
||||
config.each do |opts|
|
||||
subnet = IPAddr.new(opts["Subnet"])
|
||||
if subnet.include?(address)
|
||||
return net["Name"]
|
||||
end
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# Looks to see if a docker network has already been defined
|
||||
# with the given name
|
||||
#
|
||||
# @param [String] network_name - name of network to look for
|
||||
def existing_named_network?(network_name)
|
||||
result = list_network_names
|
||||
result.any?{|net_name| net_name == network_name}
|
||||
end
|
||||
|
||||
# @return [Array<String>] list of all docker networks
|
||||
def list_network_names
|
||||
list_network("--format={{.Name}}").split("\n").map(&:strip)
|
||||
end
|
||||
|
||||
# Returns true or false if network is in use or not.
|
||||
|
@ -283,13 +315,8 @@ module VagrantPlugins
|
|||
# @return [Bool,nil]
|
||||
def network_used?(network)
|
||||
result = inspect_network(network)
|
||||
begin
|
||||
result = JSON.parse(result)
|
||||
return result.first["Containers"].size > 0
|
||||
rescue JSON::ParserError => e
|
||||
@logger.warn("Could not properly parse response from `docker network inspect #{network}`")
|
||||
return nil
|
||||
end
|
||||
return nil if !result
|
||||
return result.first["Containers"].size > 0
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -49,6 +49,14 @@ module VagrantPlugins
|
|||
error_key(:network_invalid_option)
|
||||
end
|
||||
|
||||
class NetworkNameMissing < DockerError
|
||||
error_key(:network_name_missing)
|
||||
end
|
||||
|
||||
class NetworkNameUndefined < DockerError
|
||||
error_key(:network_name_undefined)
|
||||
end
|
||||
|
||||
class PackageNotSupported < DockerError
|
||||
error_key(:package_not_supported)
|
||||
end
|
||||
|
|
|
@ -2148,6 +2148,8 @@ en:
|
|||
choice_help: |-
|
||||
When choosing an interface, it is usually the one that is
|
||||
being used to connect to the internet.
|
||||
select_interface: |-
|
||||
Which interface should the network bridge to?
|
||||
specific_not_found: |-
|
||||
Specific bridge '%{bridge}' not found. You may be asked to specify
|
||||
which network to bridge to.
|
||||
|
|
|
@ -45,8 +45,33 @@ en:
|
|||
This container requires a host VM, and the state of that VM
|
||||
is unknown. Run `vagrant up` to verify that the container and
|
||||
its host VM is running, then try again.
|
||||
network_configure: |-
|
||||
Configuring and enabling network interfaces...
|
||||
network_bridge_gateway_invalid: |-
|
||||
The provided gateway IP address is invalid (%{gateway}). Please
|
||||
provide a valid IP address.
|
||||
network_bridge_gateway_outofbounds: |-
|
||||
The provided gateway IP (%{gateway}) is not within the defined
|
||||
subnet (%{subnet}). Please provide an IP address within the
|
||||
defined subnet.
|
||||
network_bridge_gateway_request: |-
|
||||
Gateway IP address for %{interface} interface [%{default_gateway}]:
|
||||
network_bridge_iprange_info: |-
|
||||
When an explicit address is not provided to a container attached
|
||||
to this bridged network, docker will supply an address to the
|
||||
container. This is independent of the local DHCP service that
|
||||
may be available on the network.
|
||||
network_bridge_iprange_invalid: |-
|
||||
The provided IP address range is invalid (%{range}). Please
|
||||
provide a valid range.
|
||||
network_bridge_iprange_outofbounds: |-
|
||||
The provided IP address range (%{range}) is not within the
|
||||
defined subnet (%{subnet}). Please provide an address range
|
||||
within the defined subnet.
|
||||
network_bridge_iprange_request: |-
|
||||
Available address range for assignment on %{interface} interface [%{default_range}]:
|
||||
network_create: |-
|
||||
Creating and configuring docker networks...
|
||||
network_connect: |-
|
||||
Enabling network interfaces...
|
||||
network_destroy: |-
|
||||
Removing network %{network_name} ...
|
||||
not_created_skip: |-
|
||||
|
@ -204,9 +229,33 @@ en:
|
|||
is functional and properly configured.
|
||||
|
||||
Host VM ID: %{id}
|
||||
network_address_invalid: |-
|
||||
The configured network address is not valid within the configured
|
||||
subnet of the defined network. Please update the network settings
|
||||
and try again.
|
||||
|
||||
Configured address: %{address}
|
||||
Network name: %{network_name}
|
||||
network_invalid_option: |-
|
||||
Invalid option given for docker network for guest "%{container}". Must specify either
|
||||
a `subnet` or use `type: "dhcp"`.
|
||||
network_name_missing: |-
|
||||
The Docker provider is unable to connect the container to the
|
||||
defined network due to a missing network name. Please validate
|
||||
your configuration and try again.
|
||||
|
||||
Container: %{container}
|
||||
Network Number: %{index}
|
||||
network_name_undefined: |-
|
||||
The Docker provider was unable to configure networking using the
|
||||
provided network name `%{network_name}`. Please ensure the network
|
||||
name is correct and exists, then try again.
|
||||
network_subnet_invalid: |-
|
||||
The configured network subnet is not valid for the defined network.
|
||||
Please update the network settings and try again.
|
||||
|
||||
Configured subnet: %{subnet}
|
||||
Network name: %{network_name}
|
||||
package_not_supported: |-
|
||||
The "package" command is not supported with the Docker provider.
|
||||
If you'd like to commit or push your Docker container, please SSH
|
||||
|
|
Loading…
Reference in New Issue