class Vanagon::Engine::Docker

Public Class Methods

new(platform, target = nil, **opts) click to toggle source

Both the docker_image and the docker command itself are required for the docker engine to work

Calls superclass method Vanagon::Engine::Base::new
# File lib/vanagon/engine/docker.rb, line 9
def initialize(platform, target = nil, **opts)
  super

  @docker_cmd = Vanagon::Utilities.find_program_on_path('docker')
  @required_attributes << "docker_image"
  @required_attributes.delete('ssh_port') if @platform.use_docker_exec
end

Public Instance Methods

build_host_name() click to toggle source

Return the docker image name to build on

# File lib/vanagon/engine/docker.rb, line 23
def build_host_name
  if @build_host_name.nil?
    validate_platform
    # Docker requires container names to match: [a-zA-Z0-9][a-zA-Z0-9_.-]
    # So, transform slashes and colons commonly used as separators in
    # image names.
    @build_host_name = @platform.docker_image.gsub(%r{[/:]}, '_')
  end

  @build_host_name
end
dispatch(command, return_output = false) click to toggle source
Calls superclass method Vanagon::Engine::Base#dispatch
# File lib/vanagon/engine/docker.rb, line 59
def dispatch(command, return_output = false)
  if @platform.use_docker_exec
    docker_exec(command, return_output)
  else
    super
  end
end
docker_cp(source, target) click to toggle source

Copy files between a container and the host

# File lib/vanagon/engine/docker.rb, line 97
def docker_cp(source, target)
  Vanagon::Utilities.ex("#{@docker_cmd} cp '#{source}' '#{target}'")
end
docker_cp_globs_from(globs, host_path) click to toggle source

Copy files matching a glob pattern from the container to the host

@note Globs are expanded by running ‘/bin/sh` in the container, which

may not support the same variety of expressions as Ruby's `Dir.glob`.
For example, `**` may not work.
# File lib/vanagon/engine/docker.rb, line 115
def docker_cp_globs_from(globs, host_path)
  Array(globs).each do |glob|
    # Match the behavior of `rsync -r` when both paths are directories
    # by copying the contents of the directory instead of the directory.
    glob += '*' if glob.end_with?('/') && host_path.end_with?('/')

    # TODO: This doesn't handle "interesting" paths. E.g. paths with
    #   spaces or other special non-glob characters. This could be
    #   fixed with a variant of `Shellwords.shellescape` that allows
    #   glob characters to pass through.
    paths = docker_exec(%(for file in #{glob};do [ -e "$file" ] && printf '%s\\0' "${file}";done), true).split("\0")

    paths.each do |path|
      docker_cp("#{build_host_name}-builder:#{path}", host_path)
    end
  end
end
docker_cp_globs_to(globs, container_path) click to toggle source

Copy files matching a glob pattern from the host to the container

# File lib/vanagon/engine/docker.rb, line 102
def docker_cp_globs_to(globs, container_path)
  Array(globs).each do |glob|
    Dir.glob(glob).each do |path|
      docker_cp(path, "#{build_host_name}-builder:#{container_path}")
    end
  end
end
docker_exec(command, return_output = false) click to toggle source

Execute a command on a container via docker exec

# File lib/vanagon/engine/docker.rb, line 90
def docker_exec(command, return_output = false)
  command = command.gsub("'", "'\\\\''")
  Vanagon::Utilities.local_command("#{@docker_cmd} exec #{build_host_name}-builder /bin/sh -c '#{command}'",
                                   return_command_output: return_output)
end
name() click to toggle source

Get the engine name

# File lib/vanagon/engine/docker.rb, line 18
def name
  'docker'
end
retrieve_built_artifact(artifacts_to_fetch, no_packaging) click to toggle source
# File lib/vanagon/engine/docker.rb, line 75
def retrieve_built_artifact(artifacts_to_fetch, no_packaging)
  if @platform.use_docker_exec
    output_path = 'output/'
    FileUtils.mkdir_p(output_path)
    unless no_packaging
      artifacts_to_fetch << "#{@remote_workdir}/output/*"
    end

    docker_cp_globs_from(artifacts_to_fetch, 'output/')
  else
    super
  end
end
select_target() click to toggle source

This method is used to obtain a vm to build upon using a docker container. @raise [Vanagon::Error] if a target cannot be obtained

# File lib/vanagon/engine/docker.rb, line 38
def select_target
  ssh_args = @platform.use_docker_exec ? '' : "-p #{@target_port}:22"
  extra_args = @platform.docker_run_args.nil? ? [] : @platform.docker_run_args

  Vanagon::Utilities.ex("#{@docker_cmd} run -d --name #{build_host_name}-builder #{ssh_args} #{extra_args.join(' ')} #{@platform.docker_image}")
  @target = URI.parse('localhost')

  wait_for_ssh unless @platform.use_docker_exec
rescue StandardError => e
  raise Vanagon::Error.wrap(e, "Something went wrong getting a target vm to build on using Docker.")
end
ship_workdir(workdir) click to toggle source
Calls superclass method Vanagon::Engine::Base#ship_workdir
# File lib/vanagon/engine/docker.rb, line 67
def ship_workdir(workdir)
  if @platform.use_docker_exec
    docker_cp_globs_to("#{workdir}/*", @remote_workdir)
  else
    super
  end
end
teardown() click to toggle source

This method is used to tell the vmpooler to delete the instance of the vm that was being used so the pool can be replenished.

# File lib/vanagon/engine/docker.rb, line 52
def teardown
  Vanagon::Utilities.ex("#{@docker_cmd} stop #{build_host_name}-builder")
  Vanagon::Utilities.ex("#{@docker_cmd} rm #{build_host_name}-builder")
rescue Vanagon::Error => e
  VanagonLogger.error "There was a problem tearing down the docker container #{build_host_name}-builder (#{e.message})."
end
wait_for_ssh() click to toggle source

Wait for ssh to come up in the container

Retry 5 times with a 1 second sleep between errors to account for network resets while SSHD is starting. Allow a maximum of 5 seconds for SSHD to start.

@raise [Vanagon::Error] if a SSH connection cannot be established. @return [void]

# File lib/vanagon/engine/docker.rb, line 141
def wait_for_ssh
  Vanagon::Utilities.retry_with_timeout(5, 5) do
    begin
      Vanagon::Utilities.remote_ssh_command("#{@target_user}@#{@target}", 'exit', @target_port)
    rescue StandardError => e
      sleep(1) # Give SSHD some time to start.
      raise e
    end
  end
rescue StandardError => e
  raise Vanagon::Error.wrap(e, "SSH was not up in the container after 5 seconds.")
end