Last active
August 29, 2015 14:06
-
-
Save minimum2scp/1897c669b83b412a1ce2 to your computer and use it in GitHub Desktop.
docker ps や docker images の結果を peco で絞りこんで ssh, scp したり docker kill, stop, rm, rmi とかできます。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## for zsh user | |
docker () { | |
local -a peco | |
zparseopts -D -E -peco=peco | |
if [ $#peco -eq 1 -a "${peco[1]}" = "--peco" ] | |
then | |
peco-docker "$@" | |
else | |
command docker "$@" | |
fi | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env ruby | |
require 'optparse' | |
require 'tempfile' | |
begin | |
require 'term/ansicolor' | |
rescue LoadError => e | |
raise e | |
warn "term-ansicolor gem not found, disabled color output\n" | |
sleep 1 | |
end | |
DOCKER_COMMANDS = { | |
:container => %w[attach commit cp diff inspect kill logs port pause restart rm start stop top unpause wait], | |
:image => %w[history rmi run save tag] | |
} | |
def select_container(all_flag:true, peco_opts:"", multi:true, header:false) | |
docker_ps_opts = all_flag ? "-a" : "" | |
docker_ps_output = `docker ps #{docker_ps_opts}` | |
max_width = docker_ps_output.lines.map(&:chomp).map(&:length).max | |
container_ids = docker_ps_output.lines[1..-1].map{|line| line.split(/\s{2,}/).first} | |
ip_address_mapping = Hash[ `docker inspect -f '{{ printf "%.12s" .Id }} {{ .NetworkSettings.IPAddress }}' #{container_ids.join(' ')}`.lines.map(&:chomp).map(&:split) ] | |
selected = IO.popen("peco #{peco_opts}", "r+"){|peco| | |
docker_ps_output.lines.each do |line| | |
container_id, image, command, created, status, ports, names = line.split(/\s{2,}/) | |
case container_id | |
when 'CONTAINER ID' | |
peco.puts '%-15s %s' % ['IP ADDRESS', line] | |
else | |
if status =~ /\Aup/i | |
peco.puts '%-15s %s' % [ip_address_mapping[container_id], line] | |
else | |
peco.puts '%-15s %s' % ['(none)', line] | |
end | |
end | |
end | |
peco.close_write | |
peco.read | |
} | |
selected = selected.lines.map(&:chomp).map{|line| | |
ip_address, container_id, image, command, created, status, ports, names = line.split(/\s{2,}/) | |
{ | |
:line => line, | |
:ip_address => ip_address, | |
:container_id => container_id, | |
:image => image, | |
:created => created, | |
:status => status, | |
:ports => ports, | |
:names => names | |
} | |
} | |
if !header | |
selected = selected.select{|container_hash| container_hash[:container_id] != 'CONTAINER ID'} | |
end | |
if !multi && selected.size != 1 | |
abort "select just only 1 container" | |
end | |
if block_given? | |
yield selected | |
else | |
return selected | |
end | |
end | |
def select_container_id(all_flag:true, multi:true, header:false, peco_opts:"") | |
select_container(all_flag:all_flag, multi:multi, header:header, peco_opts:peco_opts) do |container_hash_list| | |
container_hash_list.map{|container_hash| container_hash[:container_id]} | |
end | |
end | |
def select_container_ip(all_flag:true, multi:true, header:false, peco_opts:"") | |
select_container(all_flag:all_flag, multi:multi, header:header, peco_opts:peco_opts) do |container_hash_list| | |
container_hash_list.map{|container_hash| container_hash[:ip_address]} | |
end | |
end | |
def select_image_id(tree_flag:false, tag_flag:false, peco_opts:"") | |
docker_opts = tree_flag ? '--tree' : '' | |
selected = `docker images #{docker_opts} 2>/dev/null | peco #{peco_opts}` | |
if tree_flag | |
if tag_flag | |
selected.lines.map(&:chomp).map{|line| line =~ /Tags: (.+)$/ && $1.split(/, /)}.flatten.compact.select{|tag| tag != '<none>:<none>' } | |
else | |
selected.lines.map(&:chomp).map{|line| line.gsub(/^.+([0-9a-f]{12}) Virtual Size:.+$/, '\1') } | |
end | |
else | |
if tag_flag | |
selected.lines.map(&:chomp).map{|line| line.split[0..1].join(":") }.select{|tag| tag != '<none>:<none>' } | |
else | |
selected.lines.map(&:chomp).map{|line| line.split[2] } | |
end | |
end | |
end | |
def scp_command(argv, ssh_user: nil, placeholder: nil) | |
ip = select_container_ip(multi:false).first | |
remote = ssh_user ? ssh_user + '@' + ip : ip | |
if ! ARGV.any?{|a| a.include?(placeholder)} | |
warn "placeholder #{placeholder} is required for scp\n" | |
exit 1 | |
end | |
scp_argv = argv_placeholder_replace(argv, opts[:placeholder], [remote]) | |
"scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no #{scp_argv.join(' ')}" | |
end | |
def ssh_command(argv, ssh_user: nil, placeholder: nil) | |
ip = select_container_ip(multi:false).first | |
remote = ssh_user ? ssh_user + '@' + ip : ip | |
ssh_argv = argv_placeholder_replace(argv, placeholder, [remote]) | |
"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no #{ssh_argv.join(' ')}" | |
end | |
def ssh_copy_id_command(argv, ssh_user: nil, placeolder: nil) | |
ip = select_container_ip(multi:false).first | |
remote = ssh_user ? ssh_user + '@' + ip : ip | |
ssh_copy_id_argv = argv_placeholder_replace(argv, placeholder, [remote]) | |
"ssh-copy-id -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no #{ssh_copy_id_argv.join(' ')}" | |
end | |
def diff_images(tree_flag: false, tag_flag: false, diff_images: :fileopts) | |
image1, image2 = select_image_id(tree_flag: tree_flag, tag_flag: tag_flag, peco_opts: "--prompt \"select 2 images>\"") | |
if !(image1 && image2) | |
puts "select 2 images for diff-images" | |
exit 1 | |
end | |
cmd = case diff_images | |
when :package then "bash -c 'export LANG=C; export COLUMNS=120; dpkg -l'" | |
when :filename then "bash -c 'export LANG=C; find / | sort'" | |
end | |
tempfile1, tempfile2 = [image1, image2].map do |img| | |
t = Tempfile.new(["docker-diff.", ".out"]) | |
t.close | |
run_cmd = "docker run --rm #{img} #{cmd} > #{t.path}" | |
yield run_cmd | |
t | |
end | |
diff_cmd = "diff -u #{tempfile1.path} #{tempfile2.path}" | |
yield diff_cmd | |
end | |
def docker_command(c: nil, argv: nil, type: nil, placeholder: nil, tree_flag: nil, tag_flag: nil) | |
case type | |
when :container | |
container_ids = select_container_id | |
docker_subcmd_argv = argv_placeholder_replace(argv, placeholder, container_ids) | |
when :image | |
image_ids = select_image_id(tree_flag: tree_flag, tag_flag: tag_flag) | |
docker_subcmd_argv = argv_placeholder_replace(argv, placeholder, image_ids) | |
end | |
"docker #{c} #{docker_subcmd_argv.join(' ')}" | |
end | |
def argv_placeholder_replace(argv, placeholder, replaces) | |
if argv.any?{|a| a.include?(placeholder)} | |
argv.map{|a| a.include?(placeholder) ? replaces.map{|r| a.gsub(placeholder, r)} : a}.flatten | |
else | |
argv + replaces | |
end | |
end | |
def invoke_command(cmd, use_exec:true, dry_run:false, interactive:false) | |
puts ->(c){ | |
c ? (dry_run ? c.blue{c.bold{ cmd }} : c.green{c.bold{ cmd }}) : cmd | |
}[defined?(Term::ANSIColor) && Term::ANSIColor] | |
if interactive | |
print "run command? [Y/n] " | |
if $stdin.gets.chomp !~ /\A(|y|Y)\Z/ | |
puts "aborted." | |
exit 0 | |
end | |
end | |
if !dry_run | |
use_exec ? exec(cmd) : system(cmd) | |
end | |
end | |
## | |
## main | |
## | |
opts = { | |
:ssh_user => 'debian', | |
:tree => false, | |
:diff_images => :filename, | |
:dry_run => false, | |
:placeholder => '{}', | |
:interactive => true, | |
:tag => false, | |
} | |
ARGV.options do |q| | |
q.banner = "#{File.basename(__FILE__)} [options] [actions] -- [args_for_docker]\n" | |
actions = { | |
"c" => "alias of container", | |
"container" => "select container id from docker ps", | |
"i" => "alias of image", | |
"image" => "select image id from docker images", | |
"I" => "alias of ip", | |
"ip" => "select container ip from docker ps and docker inspect", | |
"scp" => "scp from/to container", | |
"ssh" => "ssh into container", | |
"ssh-copy-id" => "ssh-copy-id to container", | |
"d" => "alias of diff-images", | |
"diff-images" => "select 2 images and diff images", | |
} | |
DOCKER_COMMANDS[:container].each do |c| | |
actions[c] = "invoke \"docker #{c} [args_for_docker]\" with container id" | |
end | |
DOCKER_COMMANDS[:image].each do |c| | |
actions[c] = "invoke \"docker #{c} [args_for_docker]\" with image id" | |
end | |
q.banner << "[actions]\n" | |
actions.each do |k,v| | |
q.banner << " %-16s : %s\n" % [k,v] | |
end | |
q.banner << "\n" | |
q.banner << "[options]\n" | |
q.on("-i", "--[no-]interactive", TrueClass){ |arg| | |
opts[:interactive] = arg | |
} | |
q.on("-f", "--force", "same as --no-interactive"){ | |
opts[:interactive] = false | |
} | |
q.on("-n", "--dry-run"){ | |
opts[:dry_run] = true | |
} | |
q.on("-p", "--diff-packages", "diff-images with package" ){ | |
opts[:diff_images] = :package | |
} | |
q.on("-q", "--place-holder=STR", "placeholder for conainer ids, image ids (default is #{opts[:placeholder]})"){ |arg| | |
opts[:placeholder] = arg | |
} | |
q.on("-t", "--tree", "use --tree option for docker images"){ | |
opts[:tree] = true | |
} | |
q.on("-T", "--tag", "select tag instead of image id"){ | |
opts[:tag] = true | |
} | |
q.on("-u", "--[no-]ssh-user=USER", "ssh username for \"#{File.basename(__FILE__)} ssh\" (default is #{opts[:ssh_user]})"){ |arg| | |
opts[:ssh_user] = arg | |
} | |
q.parse! | |
end | |
case c = ARGV.shift | |
when 'c', 'container' | |
puts select_container(header:true){ |container_hash_list| container_hash_list.map{|container_hash| container_hash[:line]}.join("\n") } | |
when 'i', 'image' | |
puts select_image_id(tree_flag: opts[:tree], tag_flag: opts[:tag]).join(" ") | |
when 'I', 'ip' | |
puts select_container_ip.join(" ") | |
when 'd', 'diff-images' | |
diff_images(tree_flag: opts[:tree], tag_flag: opts[:tag], diff_images: opts[:diff_images]) do |cmd| | |
invoke_command(cmd, use_exec:false, dry_run:opts[:dry_run], interactive:opts[:interactive]) | |
end | |
when 'scp' | |
cmd = scp_command(ARGV, ssh_user: opts[:ssh_user], placeholder: opts[:placeholder]) | |
invoke_command(cmd, use_exec:true, dry_run:opts[:dry_run], interactive:opts[:interactive]) | |
when 'ssh' | |
cmd = ssh_command(ARGV, ssh_user: opts[:ssh_user], placeholder: opts[:placeholder]) | |
invoke_command(cmd, use_exec:true, dry_run:opts[:dry_run], interactive:opts[:interactive]) | |
when 'ssh-copy-id' | |
cmd = ssh_copy_id_command(ARGV, ssh_user: opts[:ssh_user], placeholder: opts[:placeholder]) | |
invoke_command(cmd, use_exec:true, dry_run:opts[:dry_run], interactive:opts[:interactive]) | |
when *DOCKER_COMMANDS[:container] | |
cmd = docker_command(c: c, argv: ARGV, type: :container, placeholder: opts[:placeholder], tree_flag: opts[:tree], tag_flag: opts[:tag]) | |
invoke_command(cmd, use_exec:true, dry_run:opts[:dry_run], interactive:opts[:interactive]) | |
when *DOCKER_COMMANDS[:image] | |
cmd = docker_command(c: c, argv: ARGV, type: :image, placeholder: opts[:placeholder], tree_flag: opts[:tree], tag_flag: opts[:tag]) | |
invoke_command(cmd, use_exec:true, dry_run:opts[:dry_run], interactive:opts[:interactive]) | |
else | |
warn "unknown command: #{c}" | |
puts ARGV.options | |
exit 1 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment