class Vanagon::Engine::Docker
Public Class Methods
Both the docker_image and the docker command itself are required for the docker engine to work
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
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
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
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
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
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
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
Get the engine name
# File lib/vanagon/engine/docker.rb, line 18 def name 'docker' end
Vanagon::Engine::Base#retrieve_built_artifact
# 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
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
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
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 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