Class: Vanagon::Engine::Docker

Inherits:
Base
  • Object
show all
Defined in:
lib/vanagon/engine/docker.rb

Instance Attribute Summary

Attributes inherited from Base

#remote_workdir, #target

Instance Method Summary collapse

Methods inherited from Base

#get_remote_workdir, #parse_target_defaults, #setup, #startup, #validate_platform

Constructor Details

#initialize(platform, target = nil, **opts) ⇒ Docker

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



9
10
11
12
13
14
15
# 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

Instance Method Details

#build_host_nameObject

Return the docker image name to build on



23
24
25
26
27
28
29
30
31
32
33
# 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) ⇒ Object



59
60
61
62
63
64
65
# 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) ⇒ Object

Copy files between a container and the host



97
98
99
# 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) ⇒ Object

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.

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



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# 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) ⇒ Object

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



102
103
104
105
106
107
108
# 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) ⇒ Object

Execute a command on a container via docker exec



90
91
92
93
94
# 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

#nameObject

Get the engine name



18
19
20
# File 'lib/vanagon/engine/docker.rb', line 18

def name
  'docker'
end

#retrieve_built_artifact(artifacts_to_fetch, no_packaging) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
# 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_targetObject

This method is used to obtain a vm to build upon using a docker container.

Raises:



38
39
40
41
42
43
44
45
46
47
48
# 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.new('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) ⇒ Object



67
68
69
70
71
72
73
# 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

#teardownObject

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.



52
53
54
55
56
57
# 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_sshvoid

This method returns an undefined value.

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.

Raises:



141
142
143
144
145
146
147
148
149
150
151
152
# 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