204 lines
4.6 KiB
Ruby
Executable File
204 lines
4.6 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'],
|
|
['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"
|
|
build: "/data/dir"
|
|
|
|
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
|
|
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."
|
|
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
|
|
|
|
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
|
|
|
|
opts = GetoptLong.new(
|
|
[ '--sample', '-s', GetoptLong::NO_ARGUMENT ],
|
|
[ '--create', '-c', GetoptLong::NO_ARGUMENT ],
|
|
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
|
|
[ '--dry-run', '-n', 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
|
|
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
|
|
|
|
|