ghost 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +1 -1
- data/{README → README.md} +20 -11
- data/Rakefile +6 -6
- data/bin/ghost +101 -92
- data/bin/ghost-ssh +132 -0
- data/lib/ghost.rb +16 -2
- data/lib/ghost/linux-host.rb +146 -80
- data/lib/ghost/mac-host.rb +101 -99
- data/lib/ghost/ssh_config.rb +110 -0
- data/spec/etc_hosts_spec.rb +100 -65
- data/spec/ghost_spec.rb +89 -89
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/ssh_config_spec.rb +80 -0
- data/spec/ssh_config_template +11 -0
- metadata +29 -43
data/LICENSE
CHANGED
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/{README → README.md}
RENAMED
@@ -15,7 +15,7 @@ Intended Usage
|
|
15
15
|
This gem is designed primarily for web developers who need to add
|
16
16
|
and modify hostnames to their system for virtual hosts on their
|
17
17
|
local/remote web server. However, it could be of use to other people
|
18
|
-
who would otherwise modify their `/etc/hosts` file manually and
|
18
|
+
who would otherwise modify their `/etc/hosts` file manually and
|
19
19
|
flush the cache.
|
20
20
|
|
21
21
|
Command
|
@@ -68,28 +68,37 @@ Command
|
|
68
68
|
Library
|
69
69
|
-------
|
70
70
|
|
71
|
-
There is also a library that can be used in Ruby scripts. The `ghost`
|
72
|
-
command is a wrapper for the library. View the source of `bin/ghost`
|
71
|
+
There is also a library that can be used in Ruby scripts. The `ghost`
|
72
|
+
command is a wrapper for the library. View the source of `bin/ghost`
|
73
73
|
to see how to use the library.
|
74
74
|
|
75
75
|
Installation
|
76
76
|
============
|
77
77
|
|
78
78
|
sudo gem install ghost
|
79
|
-
|
79
|
+
|
80
80
|
Contributors
|
81
81
|
============
|
82
82
|
|
83
|
-
|
84
|
-
|
85
|
-
* [
|
86
|
-
* [
|
87
|
-
* [
|
83
|
+
If this list is ever out of date, you can get full contributor list with `git log --format='%aN (%ae)' | sort -u`
|
84
|
+
|
85
|
+
* [Alkesh Vaghmaria](https://github.com/alkesh)
|
86
|
+
* [Andrei Serdeliuc](https://github.com/extraordinaire)
|
87
|
+
* [Ben Hoskings](https://github.com/benhoskings)
|
88
|
+
* [Bodaniel Jeanes](https://github.com/bjeanes)
|
89
|
+
* [Courtois Simon](https://github.com/simonc)
|
90
|
+
* [Felipe Coury](https://github.com/fcoury)
|
91
|
+
* [Justin Mazzi](https://github.com/jmazzi)
|
92
|
+
* [Lars Fronius](https://github.com/LarsFronius)
|
93
|
+
* [Lee Jensen](https://github.com/outerim)
|
94
|
+
* [Mitchell Riley](https://github.com/mitchellvriley)
|
95
|
+
* [Ryan Bigg](https://github.com/radar)
|
96
|
+
* [Sam Beam](https://github.com/sbeam)
|
88
97
|
|
89
98
|
Legal Stuff
|
90
99
|
===========
|
91
100
|
|
92
|
-
Copyright (c) 2008-
|
101
|
+
Copyright (c) 2008-2012 Bodaniel Jeanes
|
93
102
|
|
94
103
|
Permission is hereby granted, free of charge, to any person obtaining
|
95
104
|
a copy of this software and associated documentation files (the
|
@@ -108,4 +117,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
108
117
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
109
118
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
110
119
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
111
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
120
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -10,19 +10,19 @@ Dir['tasks/**/*.rake'].each { |rake| load rake }
|
|
10
10
|
|
11
11
|
desc "list tasks"
|
12
12
|
task :default do
|
13
|
-
puts `rake -T
|
13
|
+
puts `rake -T`
|
14
14
|
end
|
15
15
|
|
16
|
-
desc "Outstanding TODO's"
|
17
|
-
task :todo do
|
16
|
+
desc "Outstanding TODO's"
|
17
|
+
task :todo do
|
18
18
|
files = ["**/*.{rb,rake}" "bin/*", "README.mkdn"]
|
19
|
-
|
19
|
+
|
20
20
|
File.open('TODO','w') do |f|
|
21
21
|
FileList[*files].egrep(/TODO|FIXME/) do |file, line, text|
|
22
22
|
output = "#{file}:#{line} - #{text.chomp.gsub(/^\s+|\s+$/ , "")}"
|
23
|
-
|
23
|
+
|
24
24
|
puts output
|
25
25
|
f.puts output
|
26
26
|
end
|
27
27
|
end
|
28
|
-
end
|
28
|
+
end
|
data/bin/ghost
CHANGED
@@ -3,12 +3,8 @@
|
|
3
3
|
# Created by Bodaniel Jeanes on 2008-8-19.
|
4
4
|
# Copyright (c) 2008. All rights reserved.
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
rescue LoadError
|
9
|
-
# no rubygems to load, so we fail silently
|
10
|
-
end
|
11
|
-
require 'ghost'
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/ghost')
|
7
|
+
require 'open-uri'
|
12
8
|
|
13
9
|
def help_text(exit_code = 0)
|
14
10
|
script_name = File.basename $0
|
@@ -16,7 +12,7 @@ def help_text(exit_code = 0)
|
|
16
12
|
#{script_name} modify <hostname> <ip> OR <hostname> (will lookup ip)
|
17
13
|
#{script_name} delete <hostname>
|
18
14
|
#{script_name} delete_matching <pattern>
|
19
|
-
#{script_name} list
|
15
|
+
#{script_name} list [<filter>]
|
20
16
|
#{script_name} empty
|
21
17
|
#{script_name} export
|
22
18
|
#{script_name} import <file>
|
@@ -24,101 +20,114 @@ def help_text(exit_code = 0)
|
|
24
20
|
exit(exit_code)
|
25
21
|
end
|
26
22
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
23
|
+
begin
|
24
|
+
if ARGV.size.zero? || ['-h', '--help', 'help'].include?(ARGV.first)
|
25
|
+
help_text
|
26
|
+
else
|
27
|
+
case ARGV[0]
|
28
|
+
when 'add'
|
29
|
+
if [2,3].include?(ARGV.size)
|
30
|
+
begin
|
31
|
+
ARGV.shift
|
32
|
+
host = Ghost::Host.add(*ARGV)
|
33
|
+
puts " [Adding] #{host.name} -> #{host.ip}"
|
34
|
+
exit 0
|
35
|
+
rescue SocketError
|
36
|
+
$stderr.puts "Cannot find IP for host"
|
37
|
+
exit 3
|
38
|
+
rescue Ghost::RecordExists
|
39
|
+
$stderr.puts "Cannot overwrite an existing entry. Use the modify subcommand"
|
40
|
+
exit 3
|
41
|
+
end
|
42
|
+
else
|
43
|
+
$stderr.puts "The add subcommand requires at least a hostname.\n\n"
|
44
|
+
help_text 2
|
45
|
+
end
|
46
|
+
when 'modify'
|
47
|
+
if ARGV.size == 3
|
34
48
|
ARGV.shift
|
35
|
-
|
36
|
-
|
49
|
+
ARGV << true
|
50
|
+
host = Ghost::Host.add(*ARGV)
|
51
|
+
puts " [Modifying] #{host.name} -> #{host.ip}"
|
52
|
+
exit 0
|
53
|
+
else
|
54
|
+
$stderr.puts "The modify subcommand requires a hostname and an IP.\n\n"
|
55
|
+
help_text 4
|
56
|
+
end
|
57
|
+
when 'delete', 'del', 'remove', 'rm'
|
58
|
+
if ARGV.size == 2
|
59
|
+
Ghost::Host.delete(ARGV[1])
|
60
|
+
puts " [Deleting] #{ARGV[1]}"
|
37
61
|
exit 0
|
38
|
-
|
39
|
-
$stderr.puts "
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
62
|
+
else
|
63
|
+
$stderr.puts "The delete subcommand requires a hostname.\n\n"
|
64
|
+
help_text 2
|
65
|
+
end
|
66
|
+
when 'delete_matching'
|
67
|
+
if ARGV.size == 2
|
68
|
+
matched = Ghost::Host.delete_matching(ARGV[1])
|
69
|
+
if matched.empty?
|
70
|
+
puts " No matching entries found"
|
71
|
+
else
|
72
|
+
matched.each { |h| puts " [Deleting] #{h}" }
|
73
|
+
end
|
74
|
+
exit 0
|
75
|
+
else
|
76
|
+
$stderr.puts "The delete_matching subcommand requires a hostname pattern.\n\n"
|
77
|
+
help_text 2
|
78
|
+
end
|
79
|
+
when 'list'
|
80
|
+
filter = %r|#{ARGV[1]}| if ARGV.size == 2
|
81
|
+
|
82
|
+
hosts = Ghost::Host.list
|
83
|
+
|
84
|
+
if filter
|
85
|
+
hosts.reject! { |host| host.name !~ filter && host.ip !~ filter }
|
86
|
+
end
|
87
|
+
|
88
|
+
pad = hosts.max{|a,b| a.to_s.length <=> b.to_s.length }.to_s.length
|
89
|
+
|
90
|
+
puts "Listing #{hosts.size} host(s):"
|
91
|
+
|
92
|
+
hosts.each do |host|
|
93
|
+
puts "#{host.name.rjust(pad+2)} -> #{host.ip}"
|
44
94
|
end
|
45
|
-
else
|
46
|
-
$stderr.puts "The add subcommand requires at least a hostname.\n\n"
|
47
|
-
help_text 2
|
48
|
-
end
|
49
|
-
when 'modify'
|
50
|
-
if ARGV.size == 3
|
51
|
-
ARGV.shift
|
52
|
-
ARGV << true
|
53
|
-
host = Host.add(*ARGV)
|
54
|
-
puts " [Modifying] #{host.name} -> #{host.ip}"
|
55
95
|
exit 0
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
when 'delete', 'del', 'remove', 'rm'
|
61
|
-
if ARGV.size == 2
|
62
|
-
Host.delete(ARGV[1])
|
63
|
-
puts " [Deleting] #{ARGV[1]}"
|
96
|
+
when 'empty'
|
97
|
+
print " [Emptying] "
|
98
|
+
Ghost::Host.empty!
|
99
|
+
puts "Done."
|
64
100
|
exit 0
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
when 'delete_matching'
|
70
|
-
if ARGV.size == 2
|
71
|
-
matched = Host.delete_matching(ARGV[1])
|
72
|
-
if matched.empty?
|
73
|
-
puts " No matching entries found"
|
74
|
-
else
|
75
|
-
matched.each { |h| puts " [Deleting] #{h}" }
|
101
|
+
when 'export'
|
102
|
+
hosts = Ghost::Host.list
|
103
|
+
hosts.each do |host|
|
104
|
+
puts "#{host.name} #{host.ip}"
|
76
105
|
end
|
77
106
|
exit 0
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
exit 0
|
92
|
-
when 'empty'
|
93
|
-
print " [Emptying] "
|
94
|
-
Host.empty!
|
95
|
-
puts "Done."
|
96
|
-
exit 0
|
97
|
-
when 'export'
|
98
|
-
hosts = Host.list
|
99
|
-
hosts.each do |host|
|
100
|
-
puts "#{host.name} #{host.ip}"
|
101
|
-
end
|
102
|
-
exit 0
|
103
|
-
when 'import'
|
104
|
-
if ARGV.size == 2
|
105
|
-
begin
|
106
|
-
File.foreach(ARGV[1]) do |line|
|
107
|
-
host_infos = line.strip.split(' ', 2)
|
108
|
-
host = Host.add(*host_infos)
|
109
|
-
puts " [Adding] #{host.name} -> #{host.ip}"
|
107
|
+
when 'import'
|
108
|
+
if ARGV.size == 2
|
109
|
+
begin
|
110
|
+
import_file = open(ARGV[1])
|
111
|
+
import_file.each_line do |line|
|
112
|
+
host_infos = line.strip.split(' ', 2)
|
113
|
+
host = Ghost::Host.add(*host_infos)
|
114
|
+
puts " [Adding] #{host.name} -> #{host.ip}"
|
115
|
+
end
|
116
|
+
exit 0
|
117
|
+
rescue
|
118
|
+
$stderr.puts "Cannot import. A problem occured while opening the import file."
|
119
|
+
exit 5
|
110
120
|
end
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
exit 5
|
121
|
+
else
|
122
|
+
$stderr.puts "The import command requires an input file.\n\n"
|
123
|
+
help_text 6
|
115
124
|
end
|
116
125
|
else
|
117
|
-
$stderr.puts "
|
118
|
-
help_text
|
126
|
+
$stderr.puts "Invalid option: #{ARGV[0]}"
|
127
|
+
help_text 1
|
119
128
|
end
|
120
|
-
else
|
121
|
-
$stderr.puts "Invalid option: #{ARGV[0]}"
|
122
|
-
help_text 1
|
123
129
|
end
|
130
|
+
rescue Errno::EACCES
|
131
|
+
$stderr.puts "Please re-run as root or using sudo"
|
132
|
+
exit 3
|
124
133
|
end
|
data/bin/ghost-ssh
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/ghost')
|
4
|
+
|
5
|
+
def help_text(exit_code = 0)
|
6
|
+
script_name = File.basename $0
|
7
|
+
puts """USAGE: #{script_name} add <host> <hostname> [--user=<user>] [--port=<port>]
|
8
|
+
#{script_name} modify <host> <hostname> [--user=<user>] [--port=<port>]
|
9
|
+
#{script_name} delete <host>
|
10
|
+
#{script_name} list
|
11
|
+
#{script_name} empty
|
12
|
+
#{script_name} export
|
13
|
+
#{script_name} import <file>
|
14
|
+
"""
|
15
|
+
exit(exit_code)
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse(argv)
|
19
|
+
argv.shift
|
20
|
+
|
21
|
+
new_config = {
|
22
|
+
:host => argv.shift,
|
23
|
+
:hostname => argv.shift
|
24
|
+
}
|
25
|
+
|
26
|
+
if argv.size > 0
|
27
|
+
argv.each do |arg|
|
28
|
+
if arg =~ /--([a-z]+)=(.*)/
|
29
|
+
new_config[$1.to_sym] = $2
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
new_config
|
35
|
+
end
|
36
|
+
|
37
|
+
if ARGV.size.zero? || ['-h', '--help', 'help'].include?(ARGV.first)
|
38
|
+
help_text
|
39
|
+
else
|
40
|
+
case ARGV[0]
|
41
|
+
when 'add'
|
42
|
+
if [3,4,5].include?(ARGV.size)
|
43
|
+
begin
|
44
|
+
new_config = parse(ARGV)
|
45
|
+
|
46
|
+
config = Ghost::SshConfig.add(new_config)
|
47
|
+
puts " [Adding] #{config.host} -> #{config.hostname}"
|
48
|
+
exit 0
|
49
|
+
rescue Ghost::SshConfigDuplicateError
|
50
|
+
$stderr.puts $!
|
51
|
+
exit 3
|
52
|
+
end
|
53
|
+
else
|
54
|
+
$stderr.puts "The add subcommand requires at least a host and a hostname.\n\n"
|
55
|
+
help_text 2
|
56
|
+
end
|
57
|
+
when 'modify'
|
58
|
+
if [3,4,5].include?(ARGV.size)
|
59
|
+
new_config = parse(ARGV)
|
60
|
+
new_config[:force] = true
|
61
|
+
config = Ghost::SshConfig.add(new_config)
|
62
|
+
puts " [Modifying] #{config.host} -> #{config.hostname}"
|
63
|
+
exit 0
|
64
|
+
else
|
65
|
+
$stderr.puts "The modify subcommand requires at least a host and a hostname.\n\n"
|
66
|
+
help_text 4
|
67
|
+
end
|
68
|
+
when 'delete', 'del', 'remove', 'rm'
|
69
|
+
if ARGV.size == 2
|
70
|
+
Ghost::SshConfig.delete(ARGV[1])
|
71
|
+
puts " [Deleting] #{ARGV[1]}"
|
72
|
+
exit 0
|
73
|
+
else
|
74
|
+
$stderr.puts "The delete subcommand requires a hostname.\n\n"
|
75
|
+
help_text 2
|
76
|
+
end
|
77
|
+
|
78
|
+
when 'list'
|
79
|
+
configs = Ghost::SshConfig.list
|
80
|
+
pad = configs.max{|a,b| a.to_s.length <=> b.to_s.length }.to_s.length
|
81
|
+
|
82
|
+
puts "Listing #{configs.size} configs(s):"
|
83
|
+
|
84
|
+
configs.each do |c|
|
85
|
+
user = c.user ? "#{c.user}@" : ""
|
86
|
+
puts "#{c.host.rjust(pad+2)} -> #{user}#{c.hostname}:#{c.port||22}"
|
87
|
+
end
|
88
|
+
exit 0
|
89
|
+
when 'empty'
|
90
|
+
print " [Emptying] "
|
91
|
+
Ghost::SshConfig.empty!
|
92
|
+
puts "Done."
|
93
|
+
exit 0
|
94
|
+
when 'export'
|
95
|
+
configs = Ghost::SshConfig.list
|
96
|
+
configs.each do |c|
|
97
|
+
puts "#{c.host},#{c.hostname},#{c.user},#{c.port}"
|
98
|
+
end
|
99
|
+
exit 0
|
100
|
+
when 'import'
|
101
|
+
if ARGV.size == 2
|
102
|
+
begin
|
103
|
+
File.foreach(ARGV[1]) do |line|
|
104
|
+
cfg_infos = line.strip.split(',')
|
105
|
+
hash = {
|
106
|
+
:host => cfg_infos[0],
|
107
|
+
:hostname => cfg_infos[1]
|
108
|
+
}
|
109
|
+
|
110
|
+
hash[:user] = cfg_infos[2] if cfg_infos.size > 2
|
111
|
+
hash[:port] = cfg_infos[3] if cfg_infos.size > 3
|
112
|
+
hash[:force] = true
|
113
|
+
|
114
|
+
config = Ghost::SshConfig.add(hash)
|
115
|
+
|
116
|
+
puts " [Adding] #{config.host} -> #{config.hostname}"
|
117
|
+
end
|
118
|
+
exit 0
|
119
|
+
rescue
|
120
|
+
$stderr.puts "Cannot import. A problem occured while opening the import file (#{$!.message})."
|
121
|
+
exit 5
|
122
|
+
end
|
123
|
+
else
|
124
|
+
$stderr.puts "The import command requires an input file.\n\n"
|
125
|
+
help_text 6
|
126
|
+
end
|
127
|
+
else
|
128
|
+
$stderr.puts "Invalid option: #{ARGV[0]}"
|
129
|
+
help_text 1
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|