Skip to content
Browse files

Merge pull request #10 from fcoury/ssh_config

Added ghost-ssh to manipulate ~/.ssh/config file.
  • Loading branch information...
2 parents 06837f9 + 458b12d commit db6e121e6138c8fa16938f6db8cd9e09df53fbbb @bjeanes committed Jul 19, 2011
Showing with 343 additions and 1 deletion.
  1. +140 −0 bin/ghost-ssh
  2. +1 −1 ghost.gemspec
  3. +2 −0 lib/ghost.rb
  4. +108 −0 lib/ghost/ssh_config.rb
  5. +80 −0 spec/ssh_config_spec.rb
  6. +11 −0 spec/ssh_config_template
  7. +1 −0 tasks/deployment.rake
View
140 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
+
View
2 ghost.gemspec
@@ -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}
View
2 lib/ghost.rb
@@ -6,3 +6,5 @@
when /linux/
require 'ghost/linux-host'
end
+
+require 'ghost/ssh_config'
View
108 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
View
80 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
View
11 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
View
1 tasks/deployment.rake
@@ -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

0 comments on commit db6e121

Please sign in to comment.
Something went wrong with that request. Please try again.