lib/spoon.rb in docker-spoon-0.7.0 vs lib/spoon.rb in docker-spoon-0.8.0
- old
+ new
@@ -10,15 +10,10 @@
include Methadone::SH
version(Spoon::VERSION)
main do |instance|
- # Read config file & set options
- if File.exists?(options[:config])
- eval(File.open(options[:config]).read)
- end
-
D options.inspect
if options[:list]
instance_list
elsif options["list-images"]
image_list
@@ -43,33 +38,53 @@
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")
# Configurables
- options[:builddir] = '.'
+ options[:config] ||= "#{ENV['HOME']}/.spoonrc"
+ on("-c FILE", "--config", "Config file to use for spoon options")
+
+ # Read config file & set options
+ if File.exists?(options[:config])
+ eval(File.open(options[:config]).read)
+ end
+
+ 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[:add_authorized_keys] = false
- options[:url] = Docker.url
+ 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"
+ options[:image] ||= "spoon-pairing"
on("-i", "--image NAME", "Use image for spoon instance")
- options[:prefix] = 'spoon-'
+ options[:prefix] ||= 'spoon-'
on("-p", "--prefix PREFIX", "Prefix for container names")
- options[:command] = ''
- options[:config] = "#{ENV['HOME']}/.spoonrc"
- on("-c FILE", "--config", "Config file to use for spoon options")
+ 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)")
arg(:instance, :optional, "Spoon instance to connect to")
use_log_level_option
+ 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.apply_prefix(name)
"#{options[:prefix]}#{name}"
end
def self.remove_prefix(name)
@@ -127,10 +142,11 @@
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)
@@ -141,12 +157,15 @@
end
def self.instance_list
docker_url
puts "List of available spoon containers:"
- container_list = get_all_containers.select { |c| c.info["Names"].first.to_s.start_with? "/#{options[:prefix]}" }
- .sort { |c1, c2| c1.info["Names"].first.to_s <=> c2.info["Names"].first.to_s }
+ 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
@@ -198,65 +217,87 @@
def self.instance_destroy(name)
docker_url
container = get_container(name)
if container
- puts "Destroying #{name}"
- begin
- container.kill
- rescue
- puts "Failed to kill container #{container.id}"
- end
+ if confirm_delete?(name)
+ puts "Destroying #{name}"
+ begin
+ container.kill
+ rescue
+ puts "Failed to kill container #{container.id}"
+ end
- container.wait(10)
+ container.wait(10)
- begin
- container.delete(:force => true)
- rescue
- puts "Failed to remove container #{container.id}"
+ 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
- puts "Done!"
else
puts "No container named: #{name}"
end
end
def self.instance_exists?(name)
get_container(name)
end
- def self.instance_ssh(name, command='')
+ 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
+
+ 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)
- exec("ssh -t -o StrictHostKeyChecking=no -p #{ssh_port} pairing@#{host} #{ssh_command}")
+ 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)
+ else
+ system(ssh_cmd)
+ 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
- container = get_container(name)
- host = URI.parse(options[:url]).host
- key = File.read("#{ENV['HOME']}/.ssh/#{keyfile}")
- if container
- ssh_port = get_port('22', container)
- puts "Waiting for #{name}:#{ssh_port}..." until host_available?(host, ssh_port)
- system("ssh -t -o StrictHostKeyChecking=no -p #{ssh_port} pairing@#{host} \"mkdir -p .ssh && chmod 700 .ssh && echo '#{key}' >> ~/.ssh/authorized_keys\"")
- else
- puts "No container named: #{container.inspect}"
- end
+ 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
def self.instance_copy_files(name)
options[:copy_on_create].each do |file|
- puts "Copying file #{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)
@@ -265,14 +306,30 @@
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)
+ end
+ end
+
def self.get_all_containers
Docker::Container.all(:all => true)
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_running_containers
Docker::Container.all
end
def self.instance_start(container)
@@ -330,7 +387,5 @@
end
end
go!
end
-
-# option :debug, :type => :boolean, :default => true