Class: GDKBox::Docker

Inherits:
Object
  • Object
show all
Defined in:
lib/gdkbox/docker.rb

Overview

A small, focused wrapper around the docker CLI.

Only the verbs gdkbox needs are exposed. Every container created by gdkbox is tagged with the gdkbox=true label plus a gdkbox.name label so they can be discovered later without relying on local metadata alone.

Defined Under Namespace

Classes: PullProgress

Instance Method Summary collapse

Constructor Details

#initialize(shell: Shell.new) ⇒ Docker

Returns a new instance of Docker.



10
11
12
# File 'lib/gdkbox/docker.rb', line 10

def initialize(shell: Shell.new)
  @shell = shell
end

Instance Method Details

#available?Boolean

Returns:

  • (Boolean)


14
15
16
# File 'lib/gdkbox/docker.rb', line 14

def available?
  !@shell.which("docker").nil?
end

#cp_into(name, src, dest) ⇒ Object

Copy a host path into a container at dest (a full destination path).



218
219
220
# File 'lib/gdkbox/docker.rb', line 218

def cp_into(name, src, dest)
  @shell.run!("docker", "cp", src, "#{name}:#{dest}")
end

#exec(name, script, user: nil, workdir: nil, env: {}, input: nil, check: true) ⇒ Object

Run a bash script inside a running container. The script is passed to bash -lc as a single argument; values that vary (keys, usernames) are passed through the environment to avoid quoting pitfalls.

When check is true (the default) a non-zero exit raises; pass check: false to capture output and exit status without raising, which is what agent runs want so the caller can surface the agent's output either way.



208
209
210
211
212
213
214
215
# File 'lib/gdkbox/docker.rb', line 208

def exec(name, script, user: nil, workdir: nil, env: {}, input: nil, check: true)
  cmd = ["docker", "exec", "-i"]
  cmd.push("-u", user) if user
  cmd.push("-w", workdir) if workdir
  env.each { |key, value| cmd.push("-e", "#{key}=#{value}") }
  cmd.push(name, "bash", "-lc", script)
  check ? @shell.run!(*cmd, input: input) : @shell.run(*cmd, input: input)
end

#list_namesObject

Names of all gdkbox-managed containers, derived from labels.



246
247
248
249
250
251
252
253
254
255
# File 'lib/gdkbox/docker.rb', line 246

def list_names
  result = @shell.run(
    "docker", "ps", "-a",
    "--filter", "label=gdkbox=true",
    "--format", '{{index .Labels "gdkbox.name"}}'
  )
  return [] unless result.success?

  result.stdout.split("\n").map(&:strip).reject(&:empty?)
end

#pull(image, &block) ⇒ Object

Pull an image. With no block the pull runs quietly and the (captured) Result is returned. With a block the pull is streamed and the block is called with a PullProgress after every line of docker pull output, so a caller can drive a progress bar. Raises CommandError on a non-zero exit.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/gdkbox/docker.rb', line 22

def pull(image, &block)
  return @shell.run!("docker", "pull", image) unless block

  tracker = PullProgress.new
  result = @shell.stream_tty("docker", "pull", image) do |line|
    tracker.ingest(line)
    block.call(tracker)
  end
  unless result.success?
    raise CommandError.new(["docker", "pull", image], result.status, result.stderr)
  end

  result
end

#rm(name, force: true) ⇒ Object



230
231
232
233
234
235
# File 'lib/gdkbox/docker.rb', line 230

def rm(name, force: true)
  cmd = ["docker", "rm"]
  cmd << "-f" if force
  cmd << name
  @shell.run(*cmd)
end

#run_container(name:, image:, publish: [], labels: {}, env: {}, args: []) ⇒ Object

Start a detached container, returning its id.



191
192
193
194
195
196
197
198
199
# File 'lib/gdkbox/docker.rb', line 191

def run_container(name:, image:, publish: [], labels: {}, env: {}, args: [])
  cmd = ["docker", "run", "-d", "--name", name]
  labels.each { |key, value| cmd.push("--label", "#{key}=#{value}") }
  env.each { |key, value| cmd.push("-e", "#{key}=#{value}") }
  publish.each { |mapping| cmd.push("-p", mapping) }
  cmd << image
  cmd.concat(args)
  @shell.run!(*cmd).stdout.strip
end

#start(name) ⇒ Object



222
223
224
# File 'lib/gdkbox/docker.rb', line 222

def start(name)
  @shell.run!("docker", "start", name)
end

#state(name) ⇒ Object

The container's lifecycle state, or :absent if it does not exist.



238
239
240
241
242
243
# File 'lib/gdkbox/docker.rb', line 238

def state(name)
  result = @shell.run("docker", "inspect", "-f", "{{.State.Status}}", name)
  return :absent unless result.success?

  result.stdout.strip.to_sym
end

#stop(name) ⇒ Object



226
227
228
# File 'lib/gdkbox/docker.rb', line 226

def stop(name)
  @shell.run!("docker", "stop", name)
end