docker-tools/dup.rb

217 lines
5.0 KiB
Ruby
Executable File

#!/usr/bin/ruby
require 'yaml'
require 'pp'
require 'shellwords'
require 'getoptlong'
MAPPINGS = [
['stdin_open', '--interactive', {:type=>:switch}],
['tty', '--tty', {:type=>:switch}],
['detach', '--detach', {:type=>:switch}],
['remove', '--rm', {:type=>:switch}],
['container_name', '--name', {:type=>:name}],
['ports', '--publish'],
['restart', '--restart'],
['environment', '--env'],
['volumes', '--volume'],
['mem_limit', '--memory'],
['links', '--link'],
['stop_signal', '--stop-signal'],
['stop_grace_period', '--stop-timeout'],
['devices', '--device'],
['net', '--net'],
['entrypoint', '--entrypoint'],
['image', nil],
['command', nil, {:escape=>false}]
]
def get_sample(name="container")
return <<~HEREDOC
#{name}:
image: "my/image:1.2.3"
restart: always
command: "/bin/bash"
entrypoint: "/script.sh"
build: "/data/dir"
pull: false
ports:
- "1234:1234"
- "8080"
- "1001:1001/udp"
environment:
VERSION: 1.7.0
TZ:
volumes:
- "/etc:/etc:ro"
links:
- "another_container"
container_name: "Something"
mem_limit: 125M
stdin_open: false
net: host
tty: false
detach: true
remove: false
stop_signal: SIGUSR1
stop_grace_period: 5
before_build:
- "echo 'Starting build'"
after_build:
- "echo 'After build'"
before_run:
- "echo 'Starting run'"
after_run:
- "echo 'Run has finished.'"
- "docker restart container"
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0"
HEREDOC
end
def action_sample
puts get_sample
exit 1
end
def action_help
puts "-h, --help Show this help."
puts "-n, --dry-run Don't execute any commands."
puts "-s, --sample Outputs a sample yml file."
puts "-c, --create Create a new container yml file."
puts "-p, --pull (Try to) Pull the image(s) before starting the container."
exit 1
end
def run(cmd, ignore_returnvalue=false)
puts "+ #{cmd}"
returnvalue = $dry_run ? true : system(cmd)
raise "Command returned a non-zero exit value." if returnvalue!=true && !ignore_returnvalue
end
def esc(obj, escape=true)
return obj unless escape
return obj.to_s.shellescape
end
def action_create(container, file)
raise "File #{file} already exists" if File.exists?(file)
File.open(file, "w") {|f| f.write(get_sample(container))}
exec("joe #{file.shellescape}")
exit 1 # will never be reached because exec replaces this process. But just in case... ;-)
end
def action_run(container, file)
raise "File #{file} not found." unless File.exists?(file)
data = File.open(file, "r") {|f| YAML.load(f.read)}
raise "Expected #{file} to define (at least) a container named #{container}." unless data.has_key?(container)
data.each do |key, data|
if data["build"]
(data["before_build"] || []).each{|cmd| run(cmd)}
cmd = ["docker", "build", "-t", data["image"].shellescape, data["build"].shellescape]
run(cmd.join(" "))
(data["after_build"] || []).each{|cmd| run(cmd)}
end
(data["before_run"] || []).each{|cmd| run(cmd)}
cmd = ["docker", "run"]
MAPPINGS.each do |mapping|
yml_name, cmd_name, options = *mapping
options ||= {}
if !data[yml_name]
if options[:type]==:name
cmd << cmd_name << esc(key)
end
next
end
if options[:type]==:switch
cmd << cmd_name
next
end
if data[yml_name].is_a?(Array)
data[yml_name].each {|val| cmd << cmd_name << esc(val, options[:escape])}
elsif data[yml_name].is_a?(Hash)
data[yml_name].each {|key, val| cmd << cmd_name << "#{esc(key, options[:escape])}=#{esc(val, options[:escape])}"}
else
cmd << cmd_name << esc(data[yml_name], options[:escape])
end
end
if data["pull"] || $pull
run("docker pull #{data["image"].shellescape}", true)
end
run("docker rm -f #{(data["container_name"] || key).shellescape}", true)
run(cmd.compact.join(" "))
(data["after_run"] || []).each{|cmd| run(cmd)}
end
end
action = :run
container = nil
$dry_run = false
$pull = false
opts = GetoptLong.new(
[ '--sample', '-s', GetoptLong::NO_ARGUMENT ],
[ '--create', '-c', GetoptLong::NO_ARGUMENT ],
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
[ '--dry-run', '-n', GetoptLong::NO_ARGUMENT ],
[ '--pull', '-p', GetoptLong::NO_ARGUMENT ]
)
opts.each do |opt, arg|
case opt
when '--sample'
action_sample
when '--create'
action = :create
when '--help'
action_help
when '--dry-run'
puts "Dry-run. Not going to execute any command."
$dry_run = true
when '--pull'
$pull = true
end
end
container = ARGV.shift
if container==nil || container==""
raise "No container given."
end
if ENV['DUP_DIR']
base_dir = ENV['DUP_DIR']
else
base_dir = File.join(Dir.home, ".dup")
puts "Environment variable DUP_DIR is not set. Looking for .yml files in #{base_dir}"
end
file = "%s/%s.yml" % [ base_dir, container ]
if action == :create
action_create(container, file)
elsif action == :run
action_run(container, file)
end