Module: Kernel
- Defined in:
- lib/donce.rb
Overview
Execute Docker container and clean up afterwards.
This function helps building temporary Docker images, run Docker containers, and clean up afterwards — may be convenient for automated tests (for example, with Minitest):
class MyTest < Minitest::Test
def test_prints_hello_world
stdout = donce(
dockerfile: '
FROM ubuntu
CMD echo "Hello, world!"
'
)
assert_equal("Hello, world!\n", stdout)
end
end
It’s possible to pass a block to it too, which will lead to background execution of the container (in daemon mode):
def test_runs_daemon
donce(dockerfile: "FROM ubuntu\nCMD sleep 9999") do |id|
refute_empty(id) # the ID of the container
end
end
If you need to run docker
via sudo
, simply set DONCE_SUDO
environment variable to any value.
- Author
-
Yegor Bugayenko ([email protected])
- Copyright
-
Copyright © 2025 Yegor Bugayenko
- License
-
MIT
Instance Method Summary collapse
-
#donce(dockerfile: nil, image: nil, home: nil, log: $stdout, args: '', env: {}, root: false, command: '', timeout: 10, volumes: {}, ports: {}) ⇒ String
Build Docker image (or use existing one), run Docker container, and then clean up.
-
#donce_host ⇒ String
The name of the localhost inside Docker container.
Instance Method Details
#donce(dockerfile: nil, image: nil, home: nil, log: $stdout, args: '', env: {}, root: false, command: '', timeout: 10, volumes: {}, ports: {}) ⇒ String
Build Docker image (or use existing one), run Docker container, and then clean up.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/donce.rb', line 83 def donce(dockerfile: nil, image: nil, home: nil, log: $stdout, args: '', env: {}, root: false, command: '', timeout: 10, volumes: {}, ports: {}) raise 'Either use "dockerfile" or "home"' if dockerfile && home raise 'Either use "dockerfile" or "image"' if dockerfile && image raise 'Either use "image" or "home"' if home && image raise 'Either "dockerfile", or "home", or "image" must be provided' if !dockerfile && !home && !image docker = ENV['DONCE_SUDO'] ? 'sudo docker' : 'docker' img = if image image else i = "donce-#{SecureRandom.hex(6)}" if dockerfile Dir.mktmpdir do |tmp| File.write(File.join(tmp, 'Dockerfile'), dockerfile) qbash("#{docker} build #{Shellwords.escape(tmp)} -t #{i}", log:) end else qbash("#{docker} build #{Shellwords.escape(home)} -t #{i}", log:) end i end container = "donce-#{SecureRandom.hex(6)}" begin stdout = nil code = 0 begin cmd = [ docker, 'run', block_given? ? '-d' : nil, '--name', Shellwords.escape(container), OS.linux? ? nil : "--add-host #{donce_host}:host-gateway", args, env.map { |k, v| "-e #{Shellwords.escape("#{k}=#{v}")}" }.join(' '), ports.map { |k, v| "-p #{Shellwords.escape("#{k}:#{v}")}" }.join(' '), volumes.map { |k, v| "-v #{Shellwords.escape("#{k}:#{v}")}" }.join(' '), root ? nil : "--user=#{Shellwords.escape("#{Process.uid}:#{Process.gid}")}", Shellwords.escape(img), command ].compact.join(' ') stdout, code = Timeout.timeout(timeout) do qbash( cmd, log:, accept: nil, both: true, env: ) end unless code.zero? log.error(stdout) raise \ "Failed to run #{cmd} " \ "(exit code is ##{code}, stdout has #{stdout.split("\n").count} lines)" end yield container if block_given? ensure logs = qbash( "#{docker} logs #{Shellwords.escape(container)}", level: code.zero? ? Logger::DEBUG : Logger::ERROR, log: ) stdout = logs if block_given? qbash("#{docker} rm -f #{Shellwords.escape(container)}", log:) end stdout ensure Timeout.timeout(10) do qbash("#{docker} rmi #{img}", log:) unless image end end end |
#donce_host ⇒ String
The name of the localhost inside Docker container.
65 66 67 |
# File 'lib/donce.rb', line 65 def donce_host OS.linux? ? '172.17.0.1' : 'host.docker.internal' end |