lib/spoon.rb in docker-spoon-0.8.0 vs lib/spoon.rb in docker-spoon-1.0.0
- old
+ new
@@ -1,391 +1,548 @@
require "spoon/version"
require 'docker'
require 'json'
require 'uri'
require 'rainbow'
+require 'optparse'
module Spoon
- include Methadone::Main
- include Methadone::CLILogging
- include Methadone::SH
- version(Spoon::VERSION)
+ class Client
- main do |instance|
+ attr_accessor :options
+ @options = {}
- D options.inspect
- if options[:list]
- instance_list
- elsif options["list-images"]
- image_list
- elsif options[:build]
- image_build
- elsif options[:destroy]
- instance_destroy(apply_prefix(options[:destroy]))
- elsif options[:network]
- instance_network(apply_prefix(options[:network]))
- elsif instance
- instance_connect(apply_prefix(instance), options[:command])
- else
- help_now!("You either need to provide an action or an instance to connect to")
+ # This combines our default configs with our command line and config file
+ # configurations in the desired precedence
+ def self.combine_config
+ config = self.parse(ARGV)
+
+ # init config only options
+ @options["pre-build-commands"] = []
+ @options[:copy_on_create] = []
+ @options[:run_on_create] = []
+ @options[:add_authorized_keys] = false
+ @options[:command] = ''
+
+ # init command line options
+ @options[:builddir] = '.'
+ @options[:url] = ::Docker.url
+ @options[:image] = 'spoon-pairing'
+ @options[:prefix] = 'spoon-'
+ @options[:privileged] ||= false
+
+ # Eval config file
+ D "Config file is: #{config[:config]}"
+ options = {}
+ if File.exists?(config[:config])
+ eval(File.read(config[:config]))
+ else
+ puts "File #{config[:config]} does not exist"
+ exit(1)
+ end
+
+ # Read in config file values
+ options.each do |k, v|
+ @options[k] = v
+ end
+
+ # Read in command line values
+ config.each do |k, v|
+ @options[k] = v
+ end
+
+ @options
end
- end
+ def self.main
- description "Create & Connect to pairing environments in Docker"
+ instance = false
+ @options = combine_config
+ instance = ARGV[0]
+ D @options.inspect
+ if @options[:list]
+ instance_list
+ elsif @options["list-images"]
+ image_list
+ elsif @options[:build]
+ image_build
+ elsif @options[:destroy]
+ instance_destroy(apply_prefix(@options[:destroy]))
+ elsif @options[:kill]
+ instance_kill(apply_prefix(@options[:kill]))
+ elsif @options[:restart]
+ instance_restart(apply_prefix(@options[:restart]))
+ elsif @options[:network]
+ instance_network(apply_prefix(@options[:network]))
+ elsif instance
+ instance_connect(apply_prefix(instance), @options[:command])
+ else
+ puts("You either need to provide an action or an instance to connect to")
+ exit
+ end
+ end
- # Actions
- on("-l", "--list", "List available spoon instances")
- on("-d", "--destroy NAME", "Destroy spoon instance with NAME")
- on("-b", "--build", "Build image from Dockerfile using name passed to --image")
- on("-n", "--network NAME", "Display exposed ports using name passed to NAME")
+ def self.parse(args)
+ config = {}
+ optparser = OptionParser.new do |opts|
- # Configurables
- options[:config] ||= "#{ENV['HOME']}/.spoonrc"
- on("-c FILE", "--config", "Config file to use for spoon options")
+ opts.banner = "Usage: spoon [options] [instance name]\n\n"
+ opts.banner += "Create & Connect to pairing environments in Docker\n\n"
- # Read config file & set options
- if File.exists?(options[:config])
- eval(File.open(options[:config]).read)
- end
+ opts.program_name = "spoon"
- options[:builddir] ||= '.'
- on("--builddir DIR", "Directory containing Dockerfile")
- on("--pre-build-commands", "List of commands to run locally before building image")
- # These are config only options
- options[:copy_on_create] ||= []
- options[:run_on_create] ||= []
- options[:add_authorized_keys] ||= false
- options[:url] ||= Docker.url
- on("-u", "--url URL", "Docker url to connect to")
- on("-L", "--list-images", "List available spoon images")
- options[:image] ||= "spoon-pairing"
- on("-i", "--image NAME", "Use image for spoon instance")
- options[:prefix] ||= 'spoon-'
- on("-p", "--prefix PREFIX", "Prefix for container names")
- options[:command] ||= ''
- on("-f", "--force", "Skip any confirmations")
- on("--debug", "Enable debug")
- on("--debugssh", "Enable SSH debugging")
- on("-P PORT", "--portforwards", "Forward PORT over ssh (must be > 1023)")
+ opts.on("-l", "--list", "List available spoon instances") do
+ config[:list] = true
+ end
+ opts.on("-d", "--destroy NAME", "Destroy spoon instance with NAME") do |destroy|
+ config[:destroy] = destroy
+ end
+ opts.on("-b", "--build", "Build image from Dockerfile using name passed to --image") do
+ config[:build] = true
+ end
+ opts.on("-n", "--network NAME", "Display exposed ports using name passed to NAME") do |name|
+ config[:network] = name
+ end
+ opts.on("--restart NAME", "Restart the specified spoon instance") do |name|
+ config[:restart] = name
+ end
+ opts.on("--kill NAME", "Kill the specified spoon instance") do |name|
+ config[:kill] = name
+ end
+ config[:config] = "#{ENV['HOME']}/.spoonrc"
+ opts.on("-c", "--config FILE", "Config file to use for spoon @options") do |c|
+ config[:config] = c
+ end
- arg(:instance, :optional, "Spoon instance to connect to")
+ opts.on("--builddir DIR", "Directory containing Dockerfile") do |b|
+ config[:builddir] = b
+ end
- use_log_level_option
+ opts.on("--url URL", "Docker url to connect to") do |url|
+ config[:url] = url
+ end
- def self.confirm_delete?(name)
- if options[:force]
- return true
- else
- print "Are you sure you want to delete #{name}? (y/n) "
- answer = $stdin.gets.chomp.downcase
- return answer == "y"
- end
- end
+ opts.on("--list-images", "List available spoon images") do
+ config["list-images"] = true
+ end
- def self.apply_prefix(name)
- "#{options[:prefix]}#{name}"
- end
+ opts.on("--image NAME", "Use image for spoon instance") do |image|
+ config[:image] = image
+ end
- def self.remove_prefix(name)
- name.gsub(/\/?#{options[:prefix]}/, '')
- end
+ opts.on("--prefix PREFIX", "Prefix for container names") do |prefix|
+ config[:prefix] = prefix
+ end
- def self.image_build
- # Run pre-build commands
- options["pre-build-commands"].each do |command|
- sh command
- end unless options["pre-build-commands"].nil?
- D "pre-build commands complete, building Docker image"
+ opts.on("--privileged", "Enable privileged mode for new containers") do |privileged|
+ config[:privileged] = true
+ end
- docker_url
- build_opts = { 't' => options[:image], 'rm' => true }
- docker_connection = ::Docker::Connection.new(options[:url], :read_timeout => 3000)
+ opts.on("--force", "Skip any confirmations") do
+ config[:force] = true
+ end
- Docker::Image.build_from_dir(options[:builddir], build_opts, docker_connection) do |chunk|
- print_docker_response(chunk)
- end
- end
+ opts.on("--nologin", "Do not ssh to contianer, just create") do
+ config[:nologin] = true
+ end
- def self.image_list
- docker_url
- Docker::Image.all.each do |image|
- next if image.info["RepoTags"] == ["<none>:<none>"]
- puts "Image: #{image.info["RepoTags"]}"
- end
- end
+ opts.on("--debug", "Enable debug") do
+ config[:debug] = true
+ end
- def self.print_parsed_response(response)
- case response
- when Hash
- response.each do |key, value|
- case key
- when 'stream'
- puts value
- else
- puts "#{key}: #{value}"
+ opts.on("--debugssh", "Enable SSH debugging") do
+ config[:debugssh] = true
end
+
+ opts.on("--ports PORT", Array, "Expose additional docker ports") do |ports|
+ config[:ports] = ports
+ end
+
+ opts.on("--portforwards PORT", Array, "Forward PORT over ssh (must be > 1023)") do |portforwards|
+ config[:portforwards] = portforwards
+ end
+
+ opts.on("--version", "Show version") do
+ puts Spoon::VERSION
+ exit
+ end
+
+ opts.on("-h", "--help", "Show help") do
+ puts opts
+ exit
+ end
end
- when Array
- response.each do |hash|
- print_parsed_response(hash)
+
+ begin
+ optparser.parse!(ARGV)
+ rescue OptionParser::MissingArgument, OptionParser::InvalidOption
+ puts $!.to_s
+ puts optparser
+ exit(1)
end
+ config
end
- end
- def self.print_docker_response(json)
- print_parsed_response(JSON.parse(json))
- end
+ def self.confirm_delete?(name)
+ if @options[:force]
+ return true
+ else
+ print "Are you sure you want to delete #{name}? (y/n) "
+ answer = $stdin.gets.chomp.downcase
+ return answer == "y"
+ end
+ end
- def self.instance_connect(name, command='')
- docker_url
- if not instance_exists? name
- puts "The `#{name}` container doesn't exist, creating..."
- instance_create(name)
- instance_copy_authorized_keys(name, options[:add_authorized_keys])
- instance_copy_files(name)
- instance_run_actions(name)
+ def self.apply_prefix(name)
+ "#{@options[:prefix]}#{name}"
end
- container = get_container(name)
- unless is_running?(container)
- instance_start(container)
+ def self.remove_prefix(name)
+ name.sub(/\/?#{@options[:prefix]}/, '')
end
- puts "Connecting to `#{name}`"
- instance_ssh(name, command)
- end
+ def self.image_build
+ # Run pre-build commands
+ @options["pre-build-commands"].each do |command|
+ sh command
+ end unless @options["pre-build-commands"].nil?
+ D "pre-build commands complete, building Docker image"
- def self.instance_list
- docker_url
- puts "List of available spoon containers:"
- container_list = get_spoon_containers
- if container_list.empty?
- puts "No spoon containers running at #{options[:url]}"
- exit
+ docker_url
+ build_opts = { 't' => @options[:image], 'rm' => true }
+ docker_connection = ::Docker::Connection.new(@options[:url], :read_timeout => 3000)
+
+ # Quick sanity check for Dockerfile
+ unless File.exist?("#{@options[:builddir]}/Dockerfile")
+ puts "Directory `#{@options[:builddir]}` must contain a Dockerfile... cannot continue"
+ exit(1)
+ end
+
+ ::Docker::Image.build_from_dir(@options[:builddir], build_opts, docker_connection) do |chunk|
+ print_docker_response(chunk)
+ end
end
- max_width_container_name = remove_prefix(container_list.max_by {|c| c.info["Names"].first.to_s.length }.info["Names"].first.to_s)
- max_name_width = max_width_container_name.length
- container_list.each do |container|
- name = container.info["Names"].first.to_s
- running = is_running?(container) ? Rainbow("Running").green : Rainbow("Stopped").red
- puts "#{remove_prefix(name)} [ #{running} ]".rjust(max_name_width + 22) + " " + Rainbow(image_name(container)).yellow
+
+ def self.image_list
+ docker_url
+ ::Docker::Image.all.each do |image|
+ next if image.info["RepoTags"] == ["<none>:<none>"]
+ puts "Image: #{image.info["RepoTags"]}"
+ end
end
- end
- def self.image_name(container)
- env = Hash[container.json['Config']['Env'].collect { |v| v.split('=') }]
- return env['IMAGE_NAME'] || container.json['Config']['Image'].split(':').first
- end
+ def self.print_parsed_response(response)
+ case response
+ when Hash
+ response.each do |key, value|
+ case key
+ when 'stream'
+ puts value
+ else
+ puts "#{key}: #{value}"
+ end
+ end
+ when Array
+ response.each do |hash|
+ print_parsed_response(hash)
+ end
+ end
+ end
- def self.strip_slash(name)
- if name.start_with? "/"
- name[1..-1]
- else
- name
+ def self.print_docker_response(json)
+ print_parsed_response(JSON.parse(json))
end
- end
- def self.is_running?(container)
- container = Docker::Container.get(container.info["id"])
- status = container.info["State"]["Running"] || nil
- unless status.nil?
- return status
- else
- return false
+ def self.instance_connect(name, command='')
+ docker_url
+ if not instance_exists? name
+ puts "The '#{name}' container doesn't exist, creating..."
+ instance_create(name)
+ instance_copy_authorized_keys(name, @options[:add_authorized_keys])
+ instance_copy_files(name)
+ instance_run_actions(name)
+ end
+
+ container = get_container(name)
+ unless is_running?(container)
+ instance_start(container)
+ end
+
+ puts "Connecting to `#{name}`"
+ instance_ssh(name, command)
end
- end
- def self.instance_network(name)
- docker_url
+ def self.instance_list
+ docker_url
+ puts "List of available spoon containers:"
+ container_list = get_spoon_containers
+ if container_list.empty?
+ puts "No spoon containers running at #{@options[:url]}"
+ exit
+ end
+ max_width_container_name = remove_prefix(container_list.max_by {|c| c.info["Names"].first.to_s.length }.info["Names"].first.to_s)
+ max_name_width = max_width_container_name.length
+ container_list.each do |container|
+ name = container.info["Names"].first.to_s
+ running = is_running?(container) ? Rainbow("Running").green : Rainbow("Stopped").red
+ puts "#{remove_prefix(name)} [ #{running} ]".rjust(max_name_width + 22) + " " + Rainbow(image_name(container)).yellow
+ end
+ end
- container = get_container(name)
+ def self.image_name(container)
+ env = Hash[container.json['Config']['Env'].collect { |v| v.split('=') }]
+ return env['IMAGE_NAME'] || container.json['Config']['Image']
+ end
- if is_running?(container)
- host = URI.parse(options[:url]).host
- puts "Host: #{host}"
- ports = container.json['NetworkSettings']['Ports']
- ports.each do |p_name, p_port|
- tcp_name = p_name.split('/')[0]
- puts "#{tcp_name} -> #{p_port.first['HostPort']}"
+ def self.strip_slash(name)
+ if name.start_with? "/"
+ name[1..-1]
+ else
+ name
end
- else
- puts "Container is not running, cannot show ports"
end
- end
- def self.instance_destroy(name)
- docker_url
- container = get_container(name)
+ def self.is_running?(container)
+ if /^Up.+/ =~ container.info["Status"]
+ return $~
+ else
+ return false
+ end
+ end
- if container
- if confirm_delete?(name)
- puts "Destroying #{name}"
- begin
- container.kill
- rescue
- puts "Failed to kill container #{container.id}"
- end
+ def self.instance_network(name)
+ docker_url
- container.wait(10)
+ container = get_container(name)
- begin
- container.delete(:force => true)
- rescue
- puts "Failed to remove container #{container.id}"
+ if is_running?(container)
+ host = URI.parse(@options[:url]).host
+ puts "Host: #{host}"
+ ports = container.json['NetworkSettings']['Ports']
+ ports.each do |p_name, p_port|
+ tcp_name = p_name.split('/')[0]
+ puts "#{tcp_name} -> #{p_port.first['HostPort']}"
end
- puts "Done!"
else
- puts "Delete aborted.. #{name} lives to pair another day."
+ puts "Container is not running, cannot show ports"
end
- else
- puts "No container named: #{name}"
end
- end
- def self.instance_exists?(name)
- get_container(name)
- end
+ def self.instance_destroy(name)
+ docker_url
+ container = get_container(name)
- def self.get_port_forwards(forwards = "")
- if options[:portforwards]
- options[:portforwards].split.each do |port|
- (lport,rport) = port.split(':')
- forwards << "-L #{lport}:127.0.0.1:#{rport || lport} "
- end
- end
- return forwards
- end
+ if container
+ if confirm_delete?(name)
+ puts "Destroying #{name}"
+ begin
+ container.kill
+ rescue
+ puts "Failed to kill container #{container.id}"
+ end
- def self.instance_ssh(name, command='', exec=true)
- container = get_container(name)
- forwards = get_port_forwards
- D "Got forwards: #{forwards}"
- host = URI.parse(options[:url]).host
- if container
- ssh_command = "\"#{command}\"" if not command.empty?
- ssh_port = get_port('22', container)
- puts "Waiting for #{name}:#{ssh_port}..." until host_available?(host, ssh_port)
- ssh_options = "-t -o StrictHostKeyChecking=no -p #{ssh_port} #{forwards} "
- ssh_options << "-v " if options[:debugssh]
- ssh_cmd = "ssh #{ssh_options} pairing@#{host} #{ssh_command}"
- D "SSH CMD: #{ssh_cmd}"
- if exec
- exec(ssh_cmd)
+ container.wait(10)
+
+ begin
+ container.delete(:force => true)
+ rescue
+ puts "Failed to remove container #{container.id}"
+ end
+ puts "Done!"
+ else
+ puts "Delete aborted.. #{name} lives to pair another day."
+ end
else
- system(ssh_cmd)
+ puts "No container named: #{name}"
end
- else
- puts "No container named: #{container.inspect}"
end
- end
- def self.instance_copy_authorized_keys(name, keyfile)
- D "Setting up authorized_keys file"
- # We sleep this once to cope w/ slow starting ssh daemon on create
- sleep 1
- if keyfile
- full_keyfile = "#{ENV['HOME']}/.ssh/#{keyfile}"
- key = File.read(full_keyfile).chop
- D "Read keyfile `#{full_keyfile}` with contents:\n#{key}"
- cmd = "mkdir -p .ssh ; chmod 700 .ssh ; echo '#{key}' >> .ssh/authorized_keys"
- instance_ssh(name, cmd, false)
+ def self.instance_exists?(name)
+ get_container(name)
end
- end
- def self.instance_copy_files(name)
- options[:copy_on_create].each do |file|
- D "Copying file #{file}"
+ def self.get_port_forwards(forwards = "")
+ if @options[:portforwards]
+ @options[:portforwards].each do |port|
+ (lport,rport) = port.split(':')
+ forwards << "-L #{lport}:127.0.0.1:#{rport || lport} "
+ end
+ end
+ return forwards
+ end
+
+ def self.instance_ssh(name, command='', exec=true)
container = get_container(name)
- host = URI.parse(options[:url]).host
+ forwards = get_port_forwards
+ D "Got forwards: #{forwards}"
+ host = URI.parse(@options[:url]).host
if container
+ ssh_command = "\"#{command}\"" if not command.empty?
ssh_port = get_port('22', container)
puts "Waiting for #{name}:#{ssh_port}..." until host_available?(host, ssh_port)
- system("scp -o StrictHostKeyChecking=no -P #{ssh_port} #{ENV['HOME']}/#{file} pairing@#{host}:#{file}")
+ ssh_options = "-t -o StrictHostKeyChecking=no -p #{ssh_port} #{forwards} "
+ ssh_options << "-v " if @options[:debugssh]
+ ssh_cmd = "ssh #{ssh_options} pairing@#{host} #{ssh_command}"
+ puts "SSH Forwards: #{forwards}" unless forwards.empty?
+ D "SSH CMD: #{ssh_cmd}"
+ return if @options[:nologin]
+ if exec
+ exec(ssh_cmd)
+ else
+ system(ssh_cmd)
+ end
else
puts "No container named: #{container.inspect}"
end
end
- end
- def self.instance_run_actions(name)
- options[:run_on_create].each do |action|
- puts "Running command: #{action}"
- instance_ssh(name, action, false)
+ def self.instance_copy_authorized_keys(name, keyfile)
+ D "Setting up authorized_keys file"
+ # We sleep this once to cope w/ slow starting ssh daemon on create
+ sleep 1
+ if keyfile
+ full_keyfile = "#{ENV['HOME']}/.ssh/#{keyfile}"
+ key = File.read(full_keyfile).chop
+ D "Read keyfile `#{full_keyfile}` with contents:\n#{key}"
+ cmd = "mkdir -p .ssh ; chmod 700 .ssh ; echo '#{key}' >> .ssh/authorized_keys"
+ instance_ssh(name, cmd, false)
+ end
end
- end
- def self.get_all_containers
- Docker::Container.all(:all => true)
- end
+ def self.instance_copy_files(name)
+ @options[:copy_on_create].each do |file|
+ D "Copying file #{file}"
+ container = get_container(name)
+ host = URI.parse(@options[:url]).host
+ if container
+ ssh_port = get_port('22', container)
+ puts "Waiting for #{name}:#{ssh_port}..." until host_available?(host, ssh_port)
+ return if @options[:nologin]
+ system("scp -o StrictHostKeyChecking=no -P #{ssh_port} #{ENV['HOME']}/#{file} pairing@#{host}:#{file}")
+ else
+ puts "No container named: #{container.inspect}"
+ end
+ end
+ end
- def self.get_spoon_containers
- container_list = get_all_containers.select { |c| c.info["Names"].first.to_s.start_with? "/#{options[:prefix]}" }
- unless container_list.empty?
- return container_list.sort { |c1, c2| c1.info["Names"].first.to_s <=> c2.info["Names"].first.to_s }
- else
- return container_list
+ def self.instance_run_actions(name)
+ @options[:run_on_create].each do |action|
+ puts "Running command: #{action}"
+ instance_ssh(name, action, false)
+ end
end
- end
- def self.get_running_containers
- Docker::Container.all
- end
+ def self.get_all_containers
+ ::Docker::Container.all(:all => true)
+ end
- def self.instance_start(container)
- container.start!
- end
+ def self.get_spoon_containers
+ container_list = get_all_containers.select { |c| c.info["Names"].first.to_s.start_with? "/#{@options[:prefix]}" }
+ unless container_list.empty?
+ return container_list.sort { |c1, c2| c1.info["Names"].first.to_s <=> c2.info["Names"].first.to_s }
+ else
+ return container_list
+ end
+ end
- def self.get_container(name)
- docker_url
- container_list = get_all_containers
+ def self.get_running_containers
+ ::Docker::Container.all
+ end
- l_name = strip_slash(name)
- container_list.each do |container|
- if container.info["Names"].first.to_s == "/#{l_name}"
- return container
+ def self.instance_start(container)
+ container.start!
+ end
+
+ def self.instance_restart(name)
+ container = get_container(name)
+ container.kill
+ container.start!
+ puts "Container #{name} restarted"
+ end
+
+ def self.instance_kill(name)
+ container = get_container(name)
+ container.kill
+ puts "Container #{name} killed"
+ end
+
+ def self.get_container(name)
+ docker_url
+ container_list = get_all_containers
+
+ l_name = strip_slash(name)
+ container_list.each do |container|
+ if container.info["Names"].first.to_s == "/#{l_name}"
+ return container
+ end
end
+ return nil
end
- return nil
- end
- def self.instance_create(name)
- docker_url
- container = Docker::Container.create({
- 'Image' => options[:image],
- 'name' => name,
- 'Entrypoint' => 'runit',
- 'Hostname' => remove_prefix(name)
- })
- container = container.start({ 'PublishAllPorts' => true })
- end
+ def self.container_config(name)
+ data = {
+ :Cmd => 'runit',
+ :Image => @options[:image],
+ :AttachStdout => true,
+ :AttachStderr => true,
+ :Privileged => @options[:privileged],
+ :PublishAllPorts => true,
+ :Tty => true
+ }
+ # Yes, this key must be a string
+ data['name'] = name
+ data[:CpuShares] = @options[:cpu] if @options[:cpu]
+ data[:Dns] = @options[:dns] if @options[:dns]
+ data[:Hostname] = remove_prefix(name)
+ data[:Memory] = @options[:memory] if @options[:memory]
+ ports = ['22'] + Array(@options[:ports]).map { |mapping| mapping.to_s }
+ ports.compact!
+ data[:PortSpecs] = ports
+ data[:PortBindings] = ports.inject({}) do |bindings, mapping|
+ guest_port, host_port = mapping.split(':').reverse
+ bindings["#{guest_port}/tcp"] = [{
+ :HostIp => '',
+ :HostPort => host_port || ''
+ }]
+ bindings
+ end
+ data[:Volumes] = Hash[Array(@options[:volume]).map { |volume| [volume, {}] }]
+ data
+ end
- def self.host_available?(hostname, port)
- socket = TCPSocket.new(hostname, port)
- IO.select([socket], nil, nil, 5)
- rescue SocketError, Errno::ECONNREFUSED,
- Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
- sleep(0.25)
- false
- rescue Errno::EPERM, Errno::ETIMEDOUT
- false
- ensure
- socket && socket.close
- end
+ def self.instance_create(name)
+ docker_url
+ container = ::Docker::Container.create(container_config(name))
+ container = container.start(container_config(name))
+ end
- def self.docker_url
- Docker.url = options[:url]
- end
+ def self.host_available?(hostname, port)
+ socket = TCPSocket.new(hostname, port)
+ IO.select([socket], nil, nil, 5)
+ rescue SocketError, Errno::ECONNREFUSED,
+ Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
+ sleep(0.25)
+ false
+ rescue Errno::EPERM, Errno::ETIMEDOUT
+ false
+ ensure
+ socket && socket.close
+ end
- def self.get_port(port, container)
- container.json['NetworkSettings']['Ports']["#{port}/tcp"].first['HostPort']
- end
+ def self.docker_url
+ ::Docker.url = @options[:url]
+ end
- def self.D(message)
- if options[:debug]
- puts "D: #{message}"
+ def self.get_port(port, container)
+ container.json['NetworkSettings']['Ports']["#{port}/tcp"].first['HostPort']
end
- end
- go!
+ def self.D(message)
+ if @options[:debug]
+ puts "D: #{message}"
+ end
+ end
+
+ main
+ end
end