Class: VagrantPlugins::VagrantCommunicatorDocker::Communicator

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant-communicator-docker/vagrant-communicator-docker.rb

Overview

This class provides communication with Docker instances

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(machine) ⇒ Communicator

Returns a new instance of Communicator.



19
20
21
22
23
24
25
26
# File 'lib/vagrant-communicator-docker/vagrant-communicator-docker.rb', line 19

def initialize(machine)
  @logger  = Log4r::Logger.new("vagrant::communication::docker")
  @machine = machine
  @machineID = machine.id
  @logger.debug("MACHINE ID #{@machineID}")
  @logger.debug("MACHINE SHELL: #{@machine.config.communicator.bash_shell}")
  @logger.debug("SHELL WAIT: #{@machine.config.communicator.bash_wait}")
end

Class Method Details

.match?(machine) ⇒ Boolean

Returns:

  • (Boolean)


14
15
16
17
# File 'lib/vagrant-communicator-docker/vagrant-communicator-docker.rb', line 14

def self.match?(machine)
  # All machines are currently expected to be docker instances.
  return true
end

Instance Method Details

#download(from, to) ⇒ Object

Download a file from the remote machine to the local machine.

Parameters:

  • from (String)

    Path of the file on the remote machine.

  • to (String)

    Path of where to save the file locally.



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/vagrant-communicator-docker/vagrant-communicator-docker.rb', line 70

def download(from, to)
  wait_for_ready(@machine.config.communicator.bash_wait)
  @logger.debug("DOCKER COMMUNICATOR - DOWNLOAD from: #{from} to: #{to}")
  tempfile = "/tmp/#{SecureRandom.urlsafe_base64}.tar"
  @logger.debug("DOCKER COMMUNICATOR - tempfile - #{tempfile}")
  File.open(tempfile, "w") do |file|
      @container.archive_out(from) do |chunk|
          file.write(chunk)
      end
  end
  Gem::Package::TarReader.new( File.open(tempfile) ) do |tar|
      tar.each do |entry|
          File.open to, "wb" do |f|
              f.print entry.read
          end
      end
  end
end

#execute(command, opts = nil) {|type, data| ... } ⇒ Integer

Execute a command on the remote machine. The exact semantics of this method are up to the implementor, but in general the users of this class will expect this to be a shell.

This method gives you no way to write data back to the remote machine, so only execute commands that don’t expect input.

Parameters:

  • command (String)

    Command to execute.

Yields:

  • (type, data)

    Realtime output of the command being executed.

Yield Parameters:

  • type (String)

    Type of the output. This can be ‘:stdout`, `:stderr`, etc. The exact types are up to the implementor.

  • data (String)

    Data for the given output.

Returns:

  • (Integer)

    Exit code of the command.



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/vagrant-communicator-docker/vagrant-communicator-docker.rb', line 117

def execute(command, opts=nil)
  begin
      @logger.debug("DOCKER COMMUNICATOR - EXECUTE: #{command}")
      wait_for_ready(@machine.config.communicator.bash_wait)
      @logger.debug("DOCKER COMMUNICATOR - Ready to EXECUTE")
      result = @container.exec([@machine.config.communicator.bash_shell, '-c' , command], tty: true)
      @logger.debug(result)
      if result.first.length > 0
          result.first.first.each_line do |line|
              yield :stdout, line if block_given?
          end
      end
      return result.last
  rescue => e
      @logger.info("DOCKER COMMUNICATOR: Error running command " + command + " on guest using shell #{@machine.config.communicator.bash_shell}")
      @logger.error ([e.message]+e.backtrace).join($/)
  end
  return 255
end

#ready?Boolean

Returns:

  • (Boolean)


28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/vagrant-communicator-docker/vagrant-communicator-docker.rb', line 28

def ready?
  begin
      @logger.info(Docker.version)
      @container = Docker::Container.get(@machineID)
      @logger.debug(@container.json)
      # If we reached this point then we successfully connected
      @logger.debug("DOCKER COMMUNICATOR connected to #{@machineID}")
      true
  rescue
      @logger.debug("DOCKER COMMUNICATOR - Could not make connection to #{@machineID}")
      false
  end
end

#reset!Object

a persistent connection to the remote machine, this connection should be terminated and re-established. The communicator instance should be in a “fresh” state after calling this method.



165
166
167
# File 'lib/vagrant-communicator-docker/vagrant-communicator-docker.rb', line 165

def reset!
   @logger.debug("DOCKER COMMUNICATOR - RESET - NOT IMPLEMENTED")
end

#sudo(command, opts = nil) ⇒ Object

Executes a command on the remote machine with administrative privileges. See #execute for documentation, as the API is the same.

See Also:



142
143
144
145
# File 'lib/vagrant-communicator-docker/vagrant-communicator-docker.rb', line 142

def sudo(command, opts=nil)
  @logger.debug("DOCKER COMMUNICATOR - EXECUTE WITH SUDO: #{command}")
  execute(command, opts)
end

#test(command, opts = nil) ⇒ Object

Executes a command and returns true if the command succeeded, and false otherwise. By default, this executes as a normal user, and it is up to the communicator implementation if they expose an option for running tests as an administrator.

See Also:



153
154
155
156
157
158
159
# File 'lib/vagrant-communicator-docker/vagrant-communicator-docker.rb', line 153

def test(command, opts=nil)
  result = execute(command)
  if result == 0
      return true
  end
  return false
end

#upload(from, to) ⇒ Object

Upload a file to the remote machine.

Parameters:

  • from (String)

    Path of the file locally to upload.

  • to (String)

    Path of where to save the file on the remote machine.



94
95
96
97
98
99
100
101
# File 'lib/vagrant-communicator-docker/vagrant-communicator-docker.rb', line 94

def upload(from, to)
  wait_for_ready(@machine.config.communicator.bash_wait)
  to_folder = File.dirname(to)
  from_filename = File.basename(from)
  @logger.debug("DOCKER COMMUNICATOR - upload from: #{from} to: #{to_folder} from_filename: #{from_filename}")
  @container.archive_in(from, File.dirname(to), overwrite: true)
  execute("mv #{to_folder}/#{from_filename} #{to}")
end

#wait_for_ready(duration) ⇒ Boolean

wait_for_ready waits until the communicator is ready, blocking until then. It will wait up to the given duration or raise an exception if something goes wrong.

Parameters:

  • duration (Integer)

    Timeout in seconds.

Returns:

  • (Boolean)

    Will return true on successful connection or false on timeout.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/vagrant-communicator-docker/vagrant-communicator-docker.rb', line 49

def wait_for_ready(duration)
  # By default, we implement a naive solution.
  begin
    Timeout.timeout(duration) do
      while true
        return true if ready?
        sleep 0.5
      end
    end
  rescue Timeout::Error
    # We timed out, we failed.
    @logger.debug("DOCKER COMMUNICATOR - Timeout to ready")
  end

  return false
end