Navigation Menu

Skip to content
This repository has been archived by the owner on Mar 30, 2020. It is now read-only.

Commit

Permalink
Merge pull request #10 from fcoury/ssh_config
Browse files Browse the repository at this point in the history
Added ghost-ssh to manipulate ~/.ssh/config file.
  • Loading branch information
bjeanes committed Jul 20, 2011
2 parents 06837f9 + 458b12d commit db6e121
Show file tree
Hide file tree
Showing 7 changed files with 343 additions and 1 deletion.
140 changes: 140 additions & 0 deletions bin/ghost-ssh
@@ -0,0 +1,140 @@
#!/usr/bin/env ruby
#
# Created by Bodaniel Jeanes on 2008-8-19.
# Copyright (c) 2008. All rights reserved.

begin
require 'rubygems'
rescue LoadError
# no rubygems to load, so we fail silently
end
require File.join(File.dirname(__FILE__), "..", "lib", "ghost")

def help_text(exit_code = 0)
script_name = File.basename $0
puts """USAGE: #{script_name} add <host> <hostname> [--user=<user>] [--port=<port>]
#{script_name} modify <host> <hostname> [--user=<user>] [--port=<port>]
#{script_name} delete <host>
#{script_name} list
#{script_name} empty
#{script_name} export
#{script_name} import <file>
"""
exit(exit_code)
end

def parse(argv)
argv.shift

new_config = {
:host => argv.shift,
:hostname => argv.shift
}

if argv.size > 0
argv.each do |arg|
if arg =~ /--([a-z]+)=(.*)/
new_config[$1.to_sym] = $2
end
end
end

new_config
end

if ARGV.size.zero? || ['-h', '--help', 'help'].include?(ARGV.first)
help_text
else
case ARGV[0]
when 'add'
if [3,4,5].include?(ARGV.size)
begin
new_config = parse(ARGV)

config = SshConfig.add(new_config)
puts " [Adding] #{config.host} -> #{config.hostname}"
exit 0
rescue SshConfigDuplicateError
$stderr.puts $!
exit 3
end
else
$stderr.puts "The add subcommand requires at least a host and a hostname.\n\n"
help_text 2
end
when 'modify'
if [3,4,5].include?(ARGV.size)
new_config = parse(ARGV)
new_config[:force] = true
config = SshConfig.add(new_config)
puts " [Modifying] #{config.host} -> #{config.hostname}"
exit 0
else
$stderr.puts "The modify subcommand requires at least a host and a hostname.\n\n"
help_text 4
end
when 'delete', 'del', 'remove', 'rm'
if ARGV.size == 2
SshConfig.delete(ARGV[1])
puts " [Deleting] #{ARGV[1]}"
exit 0
else
$stderr.puts "The delete subcommand requires a hostname.\n\n"
help_text 2
end

when 'list'
configs = SshConfig.list
pad = configs.max{|a,b| a.to_s.length <=> b.to_s.length }.to_s.length

puts "Listing #{configs.size} configs(s):"

configs.each do |c|
user = c.user ? "#{c.user}@" : ""
puts "#{c.host.rjust(pad+2)} -> #{user}#{c.hostname}:#{c.port||22}"
end
exit 0
when 'empty'
print " [Emptying] "
SshConfig.empty!
puts "Done."
exit 0
when 'export'
configs = SshConfig.list
configs.each do |c|
puts "#{c.host},#{c.hostname},#{c.user},#{c.port}"
end
exit 0
when 'import'
if ARGV.size == 2
begin
File.foreach(ARGV[1]) do |line|
cfg_infos = line.strip.split(',')
hash = {
:host => cfg_infos[0],
:hostname => cfg_infos[1]
}

hash[:user] = cfg_infos[2] if cfg_infos.size > 2
hash[:port] = cfg_infos[3] if cfg_infos.size > 3
hash[:force] = true

config = SshConfig.add(hash)

puts " [Adding] #{config.host} -> #{config.hostname}"
end
exit 0
rescue
$stderr.puts "Cannot import. A problem occured while opening the import file (#{$!.message})."
exit 5
end
else
$stderr.puts "The import command requires an input file.\n\n"
help_text 6
end
else
$stderr.puts "Invalid option: #{ARGV[0]}"
help_text 1
end
end

2 changes: 1 addition & 1 deletion ghost.gemspec
Expand Up @@ -11,7 +11,7 @@ Gem::Specification.new do |s|
s.default_executable = %q{ghost}
s.description = %q{Allows you to create, list, and modify local hostnames}
s.email = %q{me@bjeanes.com}
s.executables = ["ghost"]
s.executables = ["ghost", "ghost-ssh"]
s.extra_rdoc_files = ["README", "LICENSE", "TODO"]
s.files = ["LICENSE", "README", "Rakefile", "TODO", "bin/ghost", "lib/ghost", "lib/ghost/linux-host.rb", "lib/ghost/mac-host.rb", "lib/ghost.rb", "spec/etc_hosts_spec.rb", "spec/ghost_spec.rb", "spec/spec.opts", "spec/spec_helper.rb"]
s.homepage = %q{http://github.com/bjeanes/ghost}
Expand Down
2 changes: 2 additions & 0 deletions lib/ghost.rb
Expand Up @@ -6,3 +6,5 @@
when /linux/
require 'ghost/linux-host'
end

require 'ghost/ssh_config'
108 changes: 108 additions & 0 deletions lib/ghost/ssh_config.rb
@@ -0,0 +1,108 @@
class SshConfigDuplicateError < StandardError; end

class SshConfig
@@ssh_config = "#{ENV['HOME']}/.ssh/config"

attr_accessor :host, :hostname, :user, :port
alias :to_s :host

def initialize(host, hostname, user, port)
@host = host
@hostname = hostname
@user = user
@port = port
end

class << self
protected :new

def list
lines = File.read(@@ssh_config).split("\n")

entries = []
current_entry = []

lines << ""
lines.each do |line|
if line.strip.empty?
entries << parse(current_entry) if current_entry.any?
current_entry = []
else
current_entry << line
end
end

entries
end

def add(args)
host = args[:host]
hostname = args[:hostname]
user = args[:user] || "root"
port = args[:port] || "22"

force = args[:force]

if find_by_host(host).nil? || force
delete(host)

new_config = SshConfig.new(host, hostname, user, port)

configs = list
configs << new_config

write(configs)

new_config
else
raise SshConfigDuplicateError, "Can not overwrite existing record"
end
end

def find_by_host(host)
list.find { |c| c.host == host }
end

def empty!
write([])
end

def delete(host)
configs = list
configs = configs.delete_if { |c| c.host == host }
write(configs)
end

def write(configs)
hosts = []

configs.sort! { |a,b| a.host <=> b.host }
configs.each do |c|
hosts << "Host #{c.host}"
hosts << " HostName #{c.hostname}"
hosts << " User #{c.user}" if c.user
hosts << " Port #{c.port}" if c.port
hosts << ""
end

File.open(@@ssh_config, 'w') {|f| f.print hosts.join("\n") }
end

def parse(config)
host = config.first[/Host (.*)/, 1]
config_hash = {}

config[1..-1].each do |entry|
entry.strip!
next if entry.empty?
key, value = entry.strip.split(" ")
config_hash[key.downcase] = value
end

SshConfig.new(host,
config_hash['hostname'], config_hash['user'],
config_hash['port'])
end

end
end
80 changes: 80 additions & 0 deletions spec/ssh_config_spec.rb
@@ -0,0 +1,80 @@
require 'ghost/ssh_config'

$ssh_config_template = File.expand_path(File.join(File.dirname(__FILE__), "ssh_config_template"))
$ssh_config_file = File.expand_path(File.join(File.dirname(__FILE__), "ssh_config"))

describe SshConfig do
before(:all) do
class SshConfig
@@ssh_config = $ssh_config_file
end
end
before { `cp #{$ssh_config_template.inspect} #{$ssh_config_file.inspect}` }
after { `rm -f #{$ssh_config_file.inspect}` }

subject do
SshConfig
end

it "has an Host" do
subject.empty!

host = "miami-b01"
hostname = "192.168.227.128"
user = "root"
port = "389"

subject.add(
:host => host,
:hostname => hostname,
:user => user,
:port => port)

config = subject.list.first
config.host.should eql(host)
config.hostname.should eql(hostname)
config.user.should eql(user)
config.port.should eql(port)
end

describe '#list' do
it "returns an array" do
subject.list.should be_instance_of(Array)
end

it "contains instances of SshConfig" do
subject.empty!
subject.add(
:host => "miami-b01",
:hostname => "192.168.227.128",
:user => "root",
:port => "39")

config = subject.list

(config = subject.list.first).should be_instance_of(SshConfig)

config.host.should eql("miami-b01")
config.hostname.should eql("192.168.227.128")
config.user.should eql("root")
config.port.should eql("39")
end

it "parses the .ssh/config format" do
configs = SshConfig.list
configs.should have(3).items

configs[0].host.should eql("example1")
configs[0].hostname.should eql("192.168.227.128")
configs[0].user.should eql("root")

configs[1].host.should eql("example2")
configs[1].hostname.should eql("example2.webbynode.com")
configs[1].user.should eql("root")
configs[1].port.should eql("2022")

configs[2].host.should eql("example3")
configs[2].hostname.should eql("10.0.0.1")
end
end
end
11 changes: 11 additions & 0 deletions spec/ssh_config_template
@@ -0,0 +1,11 @@
Host example1
HostName 192.168.227.128
User root

Host example2
HostName example2.webbynode.com
User root
Port 2022

Host example3
HostName 10.0.0.1
1 change: 1 addition & 0 deletions tasks/deployment.rake
Expand Up @@ -20,6 +20,7 @@ spec = Gem::Specification.new do |s|
s.email = EMAIL
s.homepage = HOMEPAGE
s.executables << 'ghost'
s.executables << 'ghost-ssh'
s.autorequire = GEM
s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{bin,lib,spec}/**/*")
end
Expand Down

0 comments on commit db6e121

Please sign in to comment.