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