diff --git a/plugins/providers/docker/action/destroy_network.rb b/plugins/providers/docker/action/destroy_network.rb index b056751ff..2cbe560a5 100644 --- a/plugins/providers/docker/action/destroy_network.rb +++ b/plugins/providers/docker/action/destroy_network.rb @@ -24,6 +24,7 @@ module VagrantPlugins # If network is defined in machines config as isolated single container network, then delete # If it's custom and or default, check if other containers are using it, and if not, delete network_name = "#{env[:root_path].basename.to_s}_network_#{machine.name}" + if machine.provider.driver.existing_network?(network_name) && !machine.provider.driver.network_used?(network_name) env[:ui].info("Removing network #{network_name}") diff --git a/plugins/providers/docker/action/network.rb b/plugins/providers/docker/action/network.rb index af98eed4a..ed6abc7f5 100644 --- a/plugins/providers/docker/action/network.rb +++ b/plugins/providers/docker/action/network.rb @@ -9,6 +9,55 @@ module VagrantPlugins @logger = Log4r::Logger.new('vagrant::plugins::docker::network') end + # TODO: This is not ideal, but not enitrely sure a good way + # around this without either having separate config options (which is worse for the user) + # or forcing them to care about options for each separate docker network command (which is also + # not great for the user). Basically a user wants to say: + # + # config.vm.network :private_network, + # + # We could limit the options to be name spaced simply `docker__option` like + # + # config.vm.network :private_network, docker__ip: "ip here" + # + # But then we will need this method to split out each option for creating and connecting networks + # + # Alternatively we could do + # + # - `docker__create__option` + # - `docker__connect__option` + # - ...etc... + # + # config.vm.network :private_network, docker__connect__ip: "ip here", + # docker__create__subnet: "subnet" + # + # But this will force users to care about what options are for which commands, + # but maybe they will have to care about it anyway, and it isn't that big of + # a deal for the extra namespacing? + # + # Extra namespacing puts more effort on the user side, but would allow us + # to be 'smarter' about how we set options in code by seeing essnetially + # what command the flag/option is intended for, rather than us trying to keep + # track of the commands/flags manually in this function. + # + # @param[Hash] options - options from the network config + # @returns[Hash] cli_opts - an array of strings used for the network commnad + def parse_cli_arguments(options) + cli_opts = {create: [], connect: []} + + # make this a function that generates the proper flags + if options[:type] != "dhcp" + if options[:docker__subnet] + cli_opts[:create].concat(["--subnet", options[:docker__subnet]]) + end + + if options[:docker__ip] + cli_opts[:connect].concat(["--ip", options[:docker__ip]]) + end + end + return cli_opts + end + def call(env) # If we are using a host VM, then don't worry about it machine = env[:machine] @@ -23,36 +72,20 @@ module VagrantPlugins # We only handle private and public networks next if type != :private_network && type != :public_network - # Look at docker provider config for network options: - # - If not defined, create simple "folder environment" multi-network - # that all containers with this default option are attached to - # - If provider config option is defined, create the requested network - # and attach that container to it - connect_cli_opts = [] - create_cli_opts = [] - - # make this a function that generates the proper flags - if options[:type] != "dhcp" - if options[:subnet] - create_cli_opts.concat(["--subnet", options[:subnet]]) - end - - if options[:ip] - connect_cli_opts.concat(["--ip", options[:ip]]) - end - end + cli_opts = parse_cli_arguments(options) network_name = "#{env[:root_path].basename.to_s}_network_#{machine.name}" container_id = machine.id - # TODO: Need to check if network already exists and not error + if !machine.provider.driver.existing_network?(network_name) @logger.debug("Creating network #{network_name}") - machine.provider.driver.create_network(network_name, create_cli_opts) + machine.provider.driver.create_network(network_name, cli_opts[:create]) else @logger.debug("Network #{network_name} already created") end + @logger.debug("Connecting network #{network_name} to container guest #{machine.name}") - machine.provider.driver.connect_network(network_name, container_id, connect_cli_opts) + machine.provider.driver.connect_network(network_name, container_id, cli_opts[:connect]) end @app.call(env) diff --git a/plugins/providers/docker/driver.rb b/plugins/providers/docker/driver.rb index 31d2d9953..cbc17c96c 100644 --- a/plugins/providers/docker/driver.rb +++ b/plugins/providers/docker/driver.rb @@ -177,74 +177,86 @@ module VagrantPlugins end end - # @param[String] network - name of network to connect conatiner to - # @param[String] cid - container id - # @param[Array] opts - An array of flags used for listing networks + # @param [String] network - name of network to connect conatiner to + # @param [String] cid - container id + # @param [Array] opts - An array of flags used for listing networks def connect_network(network, cid, *opts) command = ['docker', 'network', 'connect', network, cid].concat(*opts) output = execute(*command) output end - # @param[String] network - name of network to create - # @param[Array] opts - An array of flags used for listing networks + # @param [String] network - name of network to create + # @param [Array] opts - An array of flags used for listing networks def create_network(network, *opts) command = ['docker', 'network', 'create', network].concat(*opts) output = execute(*command) output end - # @param[String] network - name of network to disconnect container from - # @param[String] cid - container id - # @param[Array] opts - An array of flags used for listing networks + # @param [String] network - name of network to disconnect container from + # @param [String] cid - container id def disconnect_network(network, cid, *opts) - command = ['docker', 'network', 'disconnect', network, cid].concat(*opts) + command = ['docker', 'network', 'disconnect', network, cid, "--force"] output = execute(*command) output end - # @param[Array] networks - list of networks to inspect - # @param[Array] opts - An array of flags used for listing networks + # @param [Array] networks - list of networks to inspect + # @param [Array] opts - An array of flags used for listing networks def inspect_network(network, *opts) command = ['docker', 'network', 'inspect', network].concat(*opts) output = execute(*command) output end - # @param[Array] opts - An array of flags used for listing networks + # @param [Array] opts - An array of flags used for listing networks def list_network(*opts) command = ['docker', 'network', 'ls'].concat(*opts) output = execute(*command) output end - # @param[Array] opts - An array of flags used for listing networks + # Will delete _all_ defined but unused networks in the docker engine. Even + # networks not created by Vagrant. + # + # @param [Array] opts - An array of flags used for listing networks def prune_network(*opts) command = ['docker', 'network', 'prune', '--force'].concat(*opts) output = execute(*command) output end - # @param[String] network - name of network to remove - # @param[Array] opts - An array of flags used for listing networks - def rm_network(network, *opts) - command = ['docker', 'network', 'rm', network].concat(*opts) + # 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 + # + # @param [String] network - name of network to remove + def rm_network(network) + command = ['docker', 'network', 'rm', network] output = execute(*command) output end + # @param [Array] opts - An array of flags used for listing networks + def execute(*cmd, **opts, &block) + @executor.execute(*cmd, **opts, &block) + end + # ###################### # Docker network helpers # ###################### - # @param[String] network - name of network to look for + # @param [String] network - name of network to look for def existing_network?(network) result = list_network(["--format='{{json .Name}}'"]) #TODO: we should be more explicit here if we can result.include?(network) end - # @param[String] network - name of network to look for + # Looks to see if a docker network has already been defined + # + # @param [String] network - name of network to look for + # @return [Bool] def network_used?(network) result = inspect_network(network) begin @@ -252,13 +264,10 @@ module VagrantPlugins return result.first["Containers"].size > 0 rescue JSON::ParserError => e # Could not parse result of network inspection + # Handle this some how, maybe log error but not raise? end end - # @param[Array] opts - An array of flags used for listing networks - def execute(*cmd, **opts, &block) - @executor.execute(*cmd, **opts, &block) - end end end end