139 lines
3.7 KiB
Ruby
139 lines
3.7 KiB
Ruby
require "vagrant/util/busy"
|
|
require "vagrant/util/subprocess"
|
|
require "vagrant/util/retryable"
|
|
|
|
require 'log4r'
|
|
require 'json'
|
|
|
|
module VagrantPlugins
|
|
module DockerProvider
|
|
class Driver
|
|
include Vagrant::Util::Retryable
|
|
|
|
def initialize
|
|
@logger = Log4r::Logger.new("vagrant::docker::driver")
|
|
end
|
|
|
|
def create(params)
|
|
image = params.fetch(:image)
|
|
ports = Array(params[:ports])
|
|
volumes = Array(params[:volumes])
|
|
name = params.fetch(:name)
|
|
cmd = Array(params.fetch(:cmd))
|
|
|
|
run_cmd = %W(docker run -name #{name} -d)
|
|
run_cmd += ports.map { |p| ['-p', p.to_s] }
|
|
run_cmd += volumes.map { |v| ['-v', v.to_s] }
|
|
run_cmd += %W(-privileged) if params[:privileged]
|
|
run_cmd += %W(-h #{params[:hostname]}) if params[:hostname]
|
|
run_cmd += [image, cmd]
|
|
|
|
retryable(tries: 10, sleep: 1) do
|
|
execute(*run_cmd.flatten).chomp
|
|
end
|
|
end
|
|
|
|
def state(cid)
|
|
case
|
|
when running?(cid)
|
|
:running
|
|
when created?(cid)
|
|
:stopped
|
|
else
|
|
:not_created
|
|
end
|
|
end
|
|
|
|
def created?(cid)
|
|
result = execute('docker', 'ps', '-a', '-q', '-notrunc').to_s
|
|
result =~ /^#{Regexp.escape cid}$/
|
|
end
|
|
|
|
def running?(cid)
|
|
result = execute('docker', 'ps', '-q', '-notrunc')
|
|
result =~ /^#{Regexp.escape cid}$/m
|
|
end
|
|
|
|
def privileged?(cid)
|
|
inspect_container(cid)['HostConfig']['Privileged']
|
|
end
|
|
|
|
def start(cid)
|
|
unless running?(cid)
|
|
execute('docker', 'start', cid)
|
|
# This resets the cached information we have around, allowing `vagrant reload`s
|
|
# to work properly
|
|
# TODO: Add spec to verify this behavior
|
|
@data = nil
|
|
end
|
|
end
|
|
|
|
def stop(cid)
|
|
if running?(cid)
|
|
execute('docker', 'stop', '-t', '1', cid)
|
|
end
|
|
end
|
|
|
|
def rm(cid)
|
|
if created?(cid)
|
|
execute('docker', 'rm', '-v', cid)
|
|
end
|
|
end
|
|
|
|
def inspect_container(cid)
|
|
# DISCUSS: Is there a chance that this json will change after the container
|
|
# has been brought up?
|
|
@data ||= JSON.parse(execute('docker', 'inspect', cid)).first
|
|
end
|
|
|
|
def all_containers
|
|
execute('docker', 'ps', '-a', '-q', '-notrunc').to_s.split
|
|
end
|
|
|
|
def docker_bridge_ip
|
|
output = execute('/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'docker0')
|
|
if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
|
|
return $1.to_s
|
|
else
|
|
# TODO: Raise an user friendly message
|
|
raise 'Unable to fetch docker bridge IP!'
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def execute(*cmd, &block)
|
|
result = raw(*cmd, &block)
|
|
|
|
if result.exit_code != 0
|
|
if @interrupted
|
|
@logger.info("Exit code != 0, but interrupted. Ignoring.")
|
|
else
|
|
msg = result.stdout.gsub("\r\n", "\n")
|
|
msg << result.stderr.gsub("\r\n", "\n")
|
|
raise "#{cmd.inspect}\n#{msg}" #Errors::ExecuteError, :command => command.inspect
|
|
end
|
|
end
|
|
|
|
# Return the output, making sure to replace any Windows-style
|
|
# newlines with Unix-style.
|
|
result.stdout.gsub("\r\n", "\n")
|
|
end
|
|
|
|
def raw(*cmd, &block)
|
|
int_callback = lambda do
|
|
@interrupted = true
|
|
@logger.info("Interrupted.")
|
|
end
|
|
|
|
# Append in the options for subprocess
|
|
cmd << { :notify => [:stdout, :stderr] }
|
|
|
|
Vagrant::Util::Busy.busy(int_callback) do
|
|
Vagrant::Util::Subprocess.execute(*cmd, &block)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|