From e947960c09dab098f7c1cb7eafa3cf78b101a068 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 15 Apr 2014 20:52:06 -0700 Subject: [PATCH] providers/docker: use a custom communicator to go through host VM --- lib/vagrant/plugin/v2/communicator.rb | 2 +- plugins/providers/docker/communicator.rb | 83 ++++++++++++++++++++++++ plugins/providers/docker/errors.rb | 4 ++ plugins/providers/docker/plugin.rb | 6 ++ plugins/providers/docker/provider.rb | 6 ++ templates/locales/providers_docker.yml | 6 ++ 6 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 plugins/providers/docker/communicator.rb diff --git a/lib/vagrant/plugin/v2/communicator.rb b/lib/vagrant/plugin/v2/communicator.rb index ebe704918..511fb6002 100644 --- a/lib/vagrant/plugin/v2/communicator.rb +++ b/lib/vagrant/plugin/v2/communicator.rb @@ -23,7 +23,7 @@ module Vagrant # # @return [Boolean] def self.match?(machine) - false + true end # Initializes the communicator with the machine that we will be diff --git a/plugins/providers/docker/communicator.rb b/plugins/providers/docker/communicator.rb new file mode 100644 index 000000000..aa712358c --- /dev/null +++ b/plugins/providers/docker/communicator.rb @@ -0,0 +1,83 @@ +module VagrantPlugins + module DockerProvider + # This communicator uses the host VM as proxy to communicate to the + # actual Docker container via SSH. + class Communicator < Vagrant.plugin("2", :communicator) + def initialize(machine) + @machine = machine + @host_vm = machine.provider.host_vm + + # We only work on the Docker provider + if machine.provider_name != :docker + raise Errors::CommunicatorNotDocker + end + end + + #------------------------------------------------------------------- + # Communicator Methods + #------------------------------------------------------------------- + + def ready? + # We can't be ready if we can't talk to the host VM + return false if !@host_vm.communicate.ready? + + # We're ready if we can establish an SSH connection to the container + @host_vm.communicate.test("#{container_ssh_command} exit") + end + + def download(from, to) + raise "NOT IMPLEMENTED YET" + end + + def execute(command, opts=nil, &block) + @host_vm.communicate.execute( + "#{container_ssh_command} #{command}", opts, &block) + end + + def sudo(command, opts=nil) + end + + def test(command, **opts) + opts = { error_check: false }.merge(opts) + execute(command, opts) == 0 + end + + def upload(from, to) + # First, we upload this to the host VM to some temporary directory. + to_temp = "/tmp/docker_#{Time.now.to_i}_#{rand(100000)}" + @host_vm.communicate.upload(from, to_temp) + + # Then, we use `cat` to get that file into the Docker container. + @host_vm.communicate.execute( + "#{container_ssh_command} 'cat >#{to}' <#{to_temp}") + + # Remove the temporary file + @host_vm.communicate.execute("rm #{to_temp}", error_check: false) + end + + #------------------------------------------------------------------- + # Other Methods + #------------------------------------------------------------------- + + # This returns the raw SSH command string that can be used to + # connect via SSH to the container if you're on the same machine + # as the container. + # + # @return [String] + def container_ssh_command + # Get the container's SSH info + info = @machine.ssh_info + info[:port] ||= 22 + + # Build the SSH command + "ssh -i /home/vagrant/insecure " + + "-o Compression=yes " + + "-o ConnectTimeout=5 " + + "-o StrictHostKeyChecking=no " + + "-o UserKnownHostsFile=/dev/null " + + "-p#{info[:port]} " + + "#{info[:username]}@#{info[:host]}" + end + end + end +end diff --git a/plugins/providers/docker/errors.rb b/plugins/providers/docker/errors.rb index 59db72d4d..7d5cb2f32 100644 --- a/plugins/providers/docker/errors.rb +++ b/plugins/providers/docker/errors.rb @@ -5,6 +5,10 @@ module VagrantPlugins error_namespace("docker_provider.errors") end + class CommunicatorNonDocker < DockerError + error_key(:communicator_non_docker) + end + class ExecuteError < DockerError error_key(:execute_error) end diff --git a/plugins/providers/docker/plugin.rb b/plugins/providers/docker/plugin.rb index 9902c8101..1073f15e8 100644 --- a/plugins/providers/docker/plugin.rb +++ b/plugins/providers/docker/plugin.rb @@ -28,6 +28,12 @@ module VagrantPlugins Command::Logs end + communicator(:docker_hostvm) do + require_relative "communicator" + init! + Communicator + end + config(:docker, :provider) do require_relative 'config' init! diff --git a/plugins/providers/docker/provider.rb b/plugins/providers/docker/provider.rb index 3e1f1c423..7b6cf46a8 100644 --- a/plugins/providers/docker/provider.rb +++ b/plugins/providers/docker/provider.rb @@ -11,6 +11,12 @@ module VagrantPlugins def initialize(machine) @logger = Log4r::Logger.new("vagrant::provider::docker") @machine = machine + + if host_vm? + # We need to use a special communicator that proxies our + # SSH requests over our host VM to the container itself. + @machine.config.vm.communicator = :docker_hostvm + end end # @see Vagrant::Plugin::V2::Provider#action diff --git a/templates/locales/providers_docker.yml b/templates/locales/providers_docker.yml index e20a460f4..1ea9c0334 100644 --- a/templates/locales/providers_docker.yml +++ b/templates/locales/providers_docker.yml @@ -63,6 +63,12 @@ en: run exits and doesn't keep running. errors: + communicator_non_docker: |- + The "docker_hostvm" communicator was specified on a machine that + is not provided by the Docker provider. This is a bug with your + Vagrantfile. Please contact the creator of your Vagrant environment + and notify them to not use this communicator for anything except the + "docker" provider. config: cmd_not_set: |- The Docker command has not been set!