Class: GDKBox::Docker
- Inherits:
-
Object
- Object
- GDKBox::Docker
- 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
- #available? ⇒ Boolean
-
#cp_into(name, src, dest) ⇒ Object
Copy a host path into a container at dest (a full destination path).
-
#exec(name, script, user: nil, workdir: nil, env: {}, input: nil, check: true) ⇒ Object
Run a bash script inside a running container.
-
#image_exists?(image) ⇒ Boolean
Whether the image is already present locally.
-
#initialize(shell: Shell.new) ⇒ Docker
constructor
A new instance of Docker.
-
#list_names ⇒ Object
Names of all gdkbox-managed containers, derived from labels.
-
#pull(image, &block) ⇒ Object
Pull an image.
- #rm(name, force: true) ⇒ Object
-
#run_container(name:, image:, publish: [], labels: {}, env: {}, args: [], hostname: nil) ⇒ Object
Start a detached container, returning its id.
- #start(name) ⇒ Object
-
#state(name) ⇒ Object
The container's lifecycle state, or :absent if it does not exist.
- #stop(name) ⇒ Object
Constructor Details
Instance Method Details
#available? ⇒ 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).
227 228 229 |
# File 'lib/gdkbox/docker.rb', line 227 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.
217 218 219 220 221 222 223 224 |
# File 'lib/gdkbox/docker.rb', line 217 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 |
#image_exists?(image) ⇒ Boolean
Whether the image is already present locally.
19 20 21 |
# File 'lib/gdkbox/docker.rb', line 19 def image_exists?(image) @shell.run("docker", "image", "inspect", image).success? end |
#list_names ⇒ Object
Names of all gdkbox-managed containers, derived from labels.
255 256 257 258 259 260 261 262 263 264 |
# File 'lib/gdkbox/docker.rb', line 255 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.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/gdkbox/docker.rb', line 27 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
239 240 241 242 243 244 |
# File 'lib/gdkbox/docker.rb', line 239 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: [], hostname: nil) ⇒ Object
Start a detached container, returning its id. When a hostname is given, Docker also maps it to the container's IP in the container's /etc/hosts and re-applies that mapping on every start (a manual edit would be lost: Docker regenerates the file when the container starts).
199 200 201 202 203 204 205 206 207 208 |
# File 'lib/gdkbox/docker.rb', line 199 def run_container(name:, image:, publish: [], labels: {}, env: {}, args: [], hostname: nil) cmd = ["docker", "run", "-d", "--name", name] cmd.push("--hostname", hostname) if hostname 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
231 232 233 |
# File 'lib/gdkbox/docker.rb', line 231 def start(name) @shell.run!("docker", "start", name) end |
#state(name) ⇒ Object
The container's lifecycle state, or :absent if it does not exist.
247 248 249 250 251 252 |
# File 'lib/gdkbox/docker.rb', line 247 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
235 236 237 |
# File 'lib/gdkbox/docker.rb', line 235 def stop(name) @shell.run!("docker", "stop", name) end |