class Specinfra::Backend::Docker

Public Class Methods

new(config = {}) click to toggle source
Calls superclass method
# File lib/specinfra/backend/docker.rb, line 4
def initialize(config = {})
  super

  begin
    require 'docker' unless defined?(::Docker)
  rescue LoadError
    fail "Docker client library is not available. Try installing `docker-api' gem."
  end

  ::Docker.url = get_config(:docker_url)

  if image = get_config(:docker_image)
    @images = []
    @base_image = get_or_pull_image(image)

    create_and_start_container
    ObjectSpace.define_finalizer(self, proc { cleanup_container })
  elsif container = get_config(:docker_container)
    @container = ::Docker::Container.get(container)
  else
    fail 'Please specify docker_image or docker_container.'
  end
end

Public Instance Methods

commit_container() click to toggle source
# File lib/specinfra/backend/docker.rb, line 48
def commit_container
  @container.commit
end
run_command(cmd, opts={}) click to toggle source
# File lib/specinfra/backend/docker.rb, line 28
def run_command(cmd, opts={})
  cmd = build_command(cmd)
  run_pre_command(opts)
  docker_run!(cmd, opts)
end
send_file(from, to) click to toggle source
# File lib/specinfra/backend/docker.rb, line 34
def send_file(from, to)
  if @base_image
    @images << commit_container if @container
    @images << current_image.insert_local('localPath' => from, 'outputPath' => to)
    cleanup_container
    create_and_start_container
  elsif @container
    # This needs Docker >= 1.8
    @container.archive_in(from, to)
  else
    fail 'Cannot call send_file without docker_image or docker_container.'
  end
end

Private Instance Methods

cleanup_container() click to toggle source
# File lib/specinfra/backend/docker.rb, line 79
def cleanup_container
  @container.stop
  @container.delete
end
create_and_start_container() click to toggle source
# File lib/specinfra/backend/docker.rb, line 54
def create_and_start_container
  opts = { 'Image' => current_image.id }

  if current_image.json["Config"]["Cmd"].nil? && current_image.json["Config"]["Entrypoint"].nil?
    opts.merge!({'Cmd' => ['/bin/sh']})
  end

  opts.merge!({'OpenStdin' => true})

  if path = get_config(:path)
    (opts['Env'] ||= []) << "PATH=#{path}"
  end

  env = get_config(:env).to_a.map { |v| v.join('=') }
  opts['Env'] = opts['Env'].to_a.concat(env)

  opts.merge!(get_config(:docker_container_create_options) || {})

  @container = ::Docker::Container.create(opts)
  @container.start
  while @container.json['State'].key?('Health') && @container.json['State']['Health']['Status'] == "starting" do
    sleep 0.5
  end
end
current_image() click to toggle source
# File lib/specinfra/backend/docker.rb, line 84
def current_image
  @images.last || @base_image
end
docker_run!(cmd, opts={}) click to toggle source
# File lib/specinfra/backend/docker.rb, line 88
def docker_run!(cmd, opts={})
  opts.merge!(get_config(:docker_container_exec_options) || {})
  stdout, stderr, status = @container.exec(cmd.shellsplit, opts)

  CommandResult.new :stdout => stdout.join, :stderr => stderr.join,
  :exit_status => status
rescue ::Docker::Error::DockerError => e
  raise
rescue => e
  @container.kill
  err = stderr.nil? ? ([e.message] + e.backtrace) : stderr
  CommandResult.new :stdout => [stdout].join, :stderr => err.join,
  :exit_status => (status || 1)
end
get_or_pull_image(name) click to toggle source
# File lib/specinfra/backend/docker.rb, line 103
def get_or_pull_image(name)
  begin
    ::Docker::Image.get(name)
  rescue ::Docker::Error::NotFoundError
    ::Docker::Image.create('fromImage' => name)
  end
end
run_pre_command(opts) click to toggle source
# File lib/specinfra/backend/docker.rb, line 111
def run_pre_command(opts)
  if get_config(:pre_command)
    cmd = build_command(get_config(:pre_command))
    docker_run!(cmd, opts)
  end
end