runssh 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest ADDED
@@ -0,0 +1,12 @@
1
+ require 'autotest/fsevent'
2
+ require 'autotest/growl'
3
+
4
+ Autotest.add_hook(:initialize) {|at|
5
+ at.add_exception %r{^\.git} # ignore Version Control System
6
+ at.add_exception %r{^./tmp} # ignore temp files, lest autotest will run again, and again...
7
+ # at.clear_mappings # take out the default (test/test*rb)
8
+ at.add_mapping(%r{^lib/.*\.rb$}) {|f, _|
9
+ Dir['spec/**/*.rb']
10
+ }
11
+ nil
12
+ }
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ html/
2
+ *.tmproj
3
+ .DS_Store
4
+ .rvmrc
5
+ coverage/
6
+ .rake_tasks*
7
+ *.gem
8
+ pkg/
9
+ .rubyamp-config.yml
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in testme.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,42 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ runssh (0.2.0.rc.1)
5
+ highline (~> 1.6.1)
6
+ trollop (~> 1.16.2)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ ZenTest (4.4.2)
12
+ autotest (4.4.6)
13
+ ZenTest (>= 4.4.1)
14
+ autotest-fsevent (0.2.4)
15
+ sys-uname
16
+ autotest-growl (0.2.9)
17
+ diff-lcs (1.1.2)
18
+ highline (1.6.1)
19
+ rcov (0.9.9)
20
+ rspec (2.1.0)
21
+ rspec-core (~> 2.1.0)
22
+ rspec-expectations (~> 2.1.0)
23
+ rspec-mocks (~> 2.1.0)
24
+ rspec-core (2.1.0)
25
+ rspec-expectations (2.1.0)
26
+ diff-lcs (~> 1.1.2)
27
+ rspec-mocks (2.1.0)
28
+ sys-uname (0.8.5)
29
+ trollop (1.16.2)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ autotest (~> 4.4.4)
36
+ autotest-fsevent (~> 0.2.3)
37
+ autotest-growl (~> 0.2.6)
38
+ highline (~> 1.6.1)
39
+ rcov (~> 0.9.9)
40
+ rspec (~> 2.1.0)
41
+ runssh!
42
+ trollop (~> 1.16.2)
data/README.rdoc CHANGED
@@ -53,18 +53,16 @@ of bookmarks:
53
53
 
54
54
  You can decide to arrange your bookmarks by customers and/or location
55
55
  and/or internal/external addresses etc. To access a host definition you
56
- specify the full path to that host. In the above example to access _host2_
56
+ specify the full path to that host. In the above example to access <i>host2</i>
57
57
  (e.g, print it's definition) run:
58
58
  runssh print customer1 location1 host2
59
59
 
60
60
  == requirements:
61
- (versions are specified in the gem definition in the Rakefile)
62
-
63
- Runtime:
64
- * Trollop
65
- Development:
66
- * rspec
67
- * rcov
61
+ Dependencies (runtime and development) are specified in <i>runssh.gemspec</i>.
62
+ When installing through gem, dependencies will be installed by default. For
63
+ development, you need to install _bundler_ 1.0.x and run (inside the
64
+ project directory):
65
+ bundle install
68
66
 
69
67
  == License
70
68
  This program is distributed under the GPL v2 license.
@@ -76,12 +74,33 @@ This program is distributed under the GPL v2 license.
76
74
  [0.1.1] Improved docs.
77
75
 
78
76
  == TODO
77
+ * Convert to use thor as parent for RunSSHLib::CLI
79
78
  * Create a _proper_ zsh completion script.
80
79
  * Add scp capabilities
81
80
  * Add tunneling support:
82
81
  1. Configured tunneling
83
82
  2. tunneling defined on the command line.
84
83
  * Remote commands (e.g, with no login).
84
+ * Shell via gateway (connect to a firewall and from there open shell to the host).
85
85
  * Rename (or move) host definition
86
86
  * Maybe replace invoking ssh from the command line with some library.
87
- * Automatic deletion of empty groups.
87
+ * Automatic deletion of empty groups.
88
+
89
+ === Tasks for 0.2
90
+ * Add support for autotest ✓
91
+ * Use Highline for questions ✓
92
+ * Migrate to new ConfigFile:
93
+ 1. Create a new _SshHostDef_ class ✓
94
+ 2. Add version to the config file (use "VERSION" key as string and not as
95
+ symbol (so it won't interfere with the paths) ✓
96
+ 3. Create discovery of config version on initialization:
97
+ * If config file is un-versioned abort the application and suggest
98
+ running with <tt>--update-config</tt> argument. ✓
99
+ 4. Create a update-config method for upgrading the configurations file:
100
+ * Inform the user of backup name. ✓
101
+ * Copy the original to backup. ✓
102
+ * Convert all _HostDef_ instances to _SshHostDef_. ✓
103
+ * Save configuration and inform the user. ✓
104
+ 4. Make sure _VERSION_ is excluded form the listing of groups. ✓
105
+ 5. Convert existing functionality with the new configuration. ✓
106
+ * Add support for remote command. ✓
data/Rakefile CHANGED
@@ -16,49 +16,21 @@
16
16
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
  #
18
18
 
19
- require 'lib/runsshlib'
20
- require 'rubygems'
21
- require 'rake'
19
+ require 'bundler'
22
20
  require 'rake/rdoctask'
23
- require 'rake/gempackagetask'
24
21
  require 'rspec/core/rake_task'
25
22
 
26
- spec = Gem::Specification.new do |s|
27
- s.platform = Gem::Platform::RUBY
28
- s.summary = "CLI utility to bookmark multiple ssh connections with hierarchy."
29
- s.name = 'runssh'
30
- s.version = RunSSHLib::Version::STRING
31
- s.homepage = 'http://github.com/babysnakes/runssh'
32
- s.required_ruby_version = '~> 1.8.7'
33
- s.has_rdoc = true
34
- s.extra_rdoc_files = ['README.rdoc']
35
- s.rdoc_options << '--main' << 'README.rdoc'
36
- s.author = 'Haim Ashkenazi'
37
- s.email = 'haim@babysnakes.org'
38
- s.add_dependency('trollop', '~> 1.16.2')
39
- s.add_development_dependency('rspec', "~> 2.0.1")
40
- s.add_development_dependency('rcov', '~> 0.9.9')
41
- s.require_path = 'lib'
42
- s.executables << 'runssh'
43
- s.files = %w(README.rdoc gpl-2.0.txt Rakefile) + Dir.glob("{lib,bin,spec}/**/*")
44
- s.description = <<EOF
45
- Runssh is a command line utility to help bookmark many
46
- ssh connections in heirarchial groups.
47
- EOF
48
- end
49
-
50
- Rake::GemPackageTask.new(spec) do |pkg|
51
- pkg.need_zip = true
52
- pkg.need_tar = true
53
- end
23
+ Bundler::GemHelper.install_tasks
54
24
 
55
25
  RSpec::Core::RakeTask.new do |t|
56
- t.rcov = true
26
+ t.rcov = ENV['rcov'] == "true" ? true : false
57
27
  t.rcov_opts = %w(--exclude gems\/,spec\/)
28
+ t.name = :specs
58
29
  # t.warning = true # rspec produces too many warnings so it's commented.
59
30
  end
60
31
 
61
32
  Rake::RDocTask.new do |rd|
62
33
  rd.main = "README.rdoc"
63
34
  rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
35
+ rd.options << "-c" << 'UTF-8'
64
36
  end
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { "rspec2" }
data/bin/runssh CHANGED
@@ -19,10 +19,12 @@
19
19
  #
20
20
 
21
21
  require 'rubygems'
22
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
22
23
  require 'runsshlib'
23
24
 
24
25
  # verify required versions
25
26
  gem 'trollop', "1.16.2"
27
+ gem 'highline', '1.6.1'
26
28
 
27
29
  # let's clone and reset ARGV for future use
28
30
  args = ARGV.clone
data/lib/runsshlib/cli.rb CHANGED
@@ -28,12 +28,12 @@ module RunSSHLib
28
28
  args.unshift '-h' if args.empty?
29
29
  args.unshift '-h' if args == ['help']
30
30
  @global_options = parse_args(args)
31
+ return if @global_options[:update_config]
31
32
 
32
33
  # workaround to enable 'help COMMAND' functionality.
33
34
  if args.first == 'help'; args.shift; args << '-h'; end
34
35
  # indicate path completion request
35
36
  @completion_requested = args.delete('?')
36
-
37
37
  @cmd = extract_subcommand(args)
38
38
  @options = parse_subcommand(@cmd, args)
39
39
  @c = init_config
@@ -41,12 +41,23 @@ module RunSSHLib
41
41
  @path = args.map { |e| e.to_sym }
42
42
  rescue ConfigError, InvalidSubCommandError, Errno::ENOENT => e
43
43
  Trollop.die e.message
44
+ rescue OlderConfigVersionError => e
45
+ message = <<-EOM
46
+ You seem to use older configuration version. Did you upgrade runssh?
47
+ If so, please run <%= color('runssh [ -f config ] --update-config', :blue) %> in order to
48
+ update your configuration to the current version.
49
+
50
+ Your old configuration will be saved with the suffix <%= color(".#{e.message}", :underline) %>
51
+ EOM
52
+ HighLine.new.say(message)
53
+ abort ''
44
54
  end
45
55
 
46
56
  # run
47
57
  def run
48
- # did the user request completions? if not run the approproate command.
49
- if @completion_requested
58
+ if @global_options[:update_config]
59
+ run_update_config
60
+ elsif @completion_requested
50
61
  puts @c.list_groups(@path)
51
62
  else
52
63
  command_name = 'run_' + @cmd
@@ -92,6 +103,9 @@ Global options:
92
103
  EOS
93
104
  opt :config_file, "alternate config file",
94
105
  :type => :string, :short => :f
106
+ opt :update_config, "update configuration from previous version." +
107
+ " this option should run without COMMAND",
108
+ :short => :U
95
109
  version "RunSSH version #{Version::STRING}"
96
110
  stop_on_unknown
97
111
  end
@@ -119,19 +133,32 @@ EOS
119
133
  def parse_subcommand(cmd, args)
120
134
  case cmd
121
135
  when 'shell'
122
- Trollop::options(args) do
136
+ options = Trollop::options(args) do
123
137
  banner <<-EOS
124
- Usage: runssh [global_options] shell [options] <path>
138
+ Usage: runssh [global_options] shell [options] <path> [-- <remote command>]
125
139
 
126
140
  Connect to the specified host using ssh.
127
141
 
128
142
  <path> : See main help for description of path.
129
143
 
144
+ If you only want to run remote command instead of full shell, you can
145
+ append "-- <remote command>" to the regular command. To list /tmp on a host
146
+ bookmarked as "some host" run:
147
+ runssh shell some host -- ls -l /tmp
148
+
130
149
  Options:
131
150
  EOS
132
151
  opt :login, "override the login in the configuration",
133
152
  :type => :string
153
+ stop_on "--"
134
154
  end
155
+ # handle the case of remote command (indicated by --)
156
+ if ind = args.index("--")
157
+ rmt = args.slice!(ind, args.size - ind)
158
+ rmt.delete_at(0) # remove --
159
+ options[:remote_cmd] = rmt.join(" ")
160
+ end
161
+ options
135
162
  when 'add'
136
163
  Trollop::options(args) do
137
164
  banner <<-EOS
@@ -147,8 +174,8 @@ Options:
147
174
  EOS
148
175
  opt :host_name, 'The name or address of the host (e.g, host.example.com)',
149
176
  :short => :n, :type => :string, :required => true
150
- opt :user, 'The user to connect as (optional)',
151
- :short => :u, :type => :string
177
+ opt :login, 'The user to connect as (optional)',
178
+ :type => :string
152
179
  end
153
180
  when 'update'
154
181
  Trollop::options(args) do
@@ -165,8 +192,8 @@ Options:
165
192
  EOS
166
193
  opt :host_name, 'The name or address of the host (e.g, host.example.com)',
167
194
  :short => :n, :type => :string, :required => true
168
- opt :user, 'The user to connect as (optional)',
169
- :short => :u, :type => :string
195
+ opt :login, 'The user to connect as (optional)',
196
+ :type => :string
170
197
  end
171
198
  when 'del'
172
199
  Trollop::options(args) do
@@ -224,32 +251,37 @@ EOS
224
251
  end
225
252
 
226
253
  def init_config
227
- config = @global_options[:config_file] ?
228
- @global_options[:config_file] : DEFAULT_CONFIG
254
+ config = @global_options[:config_file] || DEFAULT_CONFIG
229
255
  ConfigFile.new(config)
230
256
  end
231
257
 
232
258
  def run_shell(path)
233
259
  host = @c.get_host(path)
234
- s = SshBackend.new(host, @options)
235
- s.shell
260
+ # only override if value exist
261
+ # TODO: this works only for some types (e.g, not boolean) but
262
+ # currently this is all we need. We may need to make it better
263
+ # later.
264
+ definition = host.definition.merge(@options) do |key, this, other|
265
+ other ? other : this
266
+ end
267
+ SshBackend.shell(definition)
236
268
  end
237
269
 
238
270
  def run_add(path)
239
271
  # extract the host definition name
240
272
  host = path.pop
241
- @c.add_host_def(path, host,
242
- HostDef.new(@options[:host_name], @options[:user]))
273
+ options = extract_definition @options
274
+ @c.add_host_def(path, host, SshHostDef.new(options))
243
275
  end
244
276
 
245
277
  def run_update(path)
246
- @c.update_host_def(path,
247
- HostDef.new(@options[:host_name], @options[:user]))
278
+ @c.update_host_def(path, SshHostDef.new(extract_definition @options))
248
279
  end
249
280
 
250
281
  def run_del(path)
251
- question = "Are you sure you want to delete \"" + path.join(':') + "\""
252
- if verify_yn(question)
282
+ question = "Are you sure you want to delete \"" + path.join(':') + "\"" +
283
+ "? [yes/no] "
284
+ if HighLine.new.agree(question)
253
285
  @c.delete_path(path)
254
286
  else
255
287
  puts 'canceled'
@@ -258,17 +290,15 @@ EOS
258
290
 
259
291
  def run_print(path)
260
292
  host = @c.get_host(path)
261
- output = "Host definition for: #{path.last}",
262
- " * host: #{host.name}",
263
- " * user: #{host.login ? host.login : 'current user'}"
264
- puts output
293
+ puts "Host definition for: #{path.last}"
294
+ puts host.to_print
265
295
  end
266
296
 
267
297
  # we don't use path here, it's just for easier invocation.
268
298
  def run_import(path)
269
299
  question = "Importing a file OVERWRITES existing configuration. " +
270
- "Are you sure"
271
- if verify_yn(question)
300
+ "Are you sure? [yes/no] "
301
+ if HighLine.new.agree(question)
272
302
  @c.import(@options[:input_file])
273
303
  else
274
304
  puts 'canceled'
@@ -280,13 +310,30 @@ EOS
280
310
  @c.export(@options[:output_file])
281
311
  end
282
312
 
283
- # Verifies a presented question. If response is 'y' it returns
284
- # true, else false.
285
- #
286
- # The supplied question should not include the (y/n)? postfix.
287
- def verify_yn question
288
- print question, " (y/n)? "
289
- gets.chomp == 'y'
313
+ # extract keys relevant for definition of SshHostDef
314
+ def extract_definition options
315
+ valid_definition = [:host_name, :login]
316
+ options.reject do |key, value|
317
+ ! valid_definition.include?(key)
318
+ end
319
+ end
320
+
321
+ # updating configurations
322
+ def run_update_config
323
+ config = @global_options[:config_file] || DEFAULT_CONFIG
324
+ c = ConfigFile.new(config, true)
325
+ result = c.update_config
326
+ if result
327
+ message = <<-EOM
328
+ Your config file is now updated to the approproate version.
329
+ Your old config file is copied to <%= color("#{result}", :blue) %>.
330
+ EOM
331
+ HighLine.new.say(message)
332
+ elsif
333
+ message = "Your configuration seems to be at the appropriate version!" +
334
+ " No update was performed."
335
+ HighLine.new.say(message)
336
+ end
290
337
  end
291
338
  end
292
339
  end
@@ -27,17 +27,27 @@ module RunSSHLib
27
27
  # configuration, but should also be able to import/export
28
28
  # to/from yaml file.
29
29
  class ConfigFile
30
+ Version = 1.0 # Config version
30
31
 
31
32
  # Initialize new ConfigFile. Uses supplied config_file or the default
32
33
  # '~/.runssh'. If file doesn't exist, it issues a warning and creates
33
34
  # a new empty one.
34
- def initialize(config_file)
35
+ def initialize(config_file, old_version=false)
35
36
  @config_file = config_file
36
37
  if File.exists? config_file
37
38
  File.open(config_file) { |io| @config = Marshal.load(io) }
39
+ if ! @config['VERSION']
40
+ raise OlderConfigVersionError, 'none' unless old_version
41
+ elsif @config['VERSION'] > Version
42
+ # This is for the future, to avoid reading more advanced
43
+ # configuration version in an old runssh version
44
+ error = "The configuration file is for a newer version of runssh!"
45
+ raise ConfigError, error
46
+ end
38
47
  else
39
48
  # warn "Config file not found. It must be the first time you run this app..."
40
49
  @config = Hash.new
50
+ @config['VERSION'] = Version
41
51
  save
42
52
  end
43
53
  end
@@ -47,14 +57,14 @@ module RunSSHLib
47
57
  # path:: An array of symbols that represent the path
48
58
  # for the host. e.g, [:client, :datacenter1].
49
59
  # name:: The name of the host definition as symbol.
50
- # host_def:: A HostDef instance.
60
+ # host_def:: A SshHostDef instance.
51
61
  def add_host_def(path, name, host_def)
52
62
  # sanity
53
- raise ConfigError.new('Invalid host definition') unless host_def.instance_of? HostDef
63
+ raise ConfigError.new('Invalid host definition') unless host_def.instance_of? SshHostDef
54
64
 
55
65
  k = path.inject(@config) do |hsh, key|
56
66
  if hsh.include? key
57
- if hsh[key].instance_of? HostDef
67
+ if hsh[key].instance_of? SshHostDef
58
68
  raise ConfigError.new('Cannot override host definition with path!')
59
69
  end
60
70
  hsh[key]
@@ -74,7 +84,7 @@ module RunSSHLib
74
84
  def update_host_def(path, host_def)
75
85
  # sanity
76
86
  raise ConfigError.new('Invalid host definition!') if not
77
- host_def.instance_of? HostDef
87
+ host_def.instance_of? SshHostDef
78
88
 
79
89
  # we need to separate the host name from the path
80
90
  # in order to get the key of the host definition.
@@ -83,7 +93,7 @@ module RunSSHLib
83
93
  raise ConfigError, 'Invalid path!' unless groups
84
94
  if groups.include? host
85
95
  raise ConfigError.new("Cannot overwrite group with host definition") unless
86
- groups[host].instance_of? HostDef
96
+ groups[host].instance_of? SshHostDef
87
97
  groups[host] = host_def
88
98
  else
89
99
  raise ConfigError.new("Host definition doesn't exist!")
@@ -110,7 +120,7 @@ module RunSSHLib
110
120
  def list_groups(path)
111
121
  value = retrieve_path(path, 'Invalid path!')
112
122
  if value.instance_of? Hash
113
- value.keys
123
+ value.keys.reject { |i| i == 'VERSION' }
114
124
  else
115
125
  []
116
126
  end
@@ -124,7 +134,7 @@ module RunSSHLib
124
134
  value = retrieve_path(path, 'Invalid path!')
125
135
  raise ConfigError.new('Invalid path!') unless value
126
136
 
127
- if value[mykey].instance_of? HostDef or value[mykey] == {}
137
+ if value[mykey].instance_of? SshHostDef or value[mykey] == {}
128
138
  value.delete(mykey)
129
139
  elsif not value[mykey]
130
140
  raise ConfigError.new('Invalid path!')
@@ -135,25 +145,46 @@ module RunSSHLib
135
145
  save
136
146
  end
137
147
 
138
- # Export config as YAML to the supplied file.
148
+ # Import config from YAML from the specified file.
139
149
  def import(file)
140
150
  require 'yaml'
141
- @config = YAML.load_file(file)
151
+ config = YAML.load_file(file)
152
+ raise ConfigError, "The imported file is from a different version of " +
153
+ "runssh (config: #{config['VERSION']})! aborting." unless
154
+ config['VERSION'] == Version
155
+ @config = config
142
156
  save
143
157
  end
144
158
 
145
- # Import config from YAML from the specified file.
159
+ # Export config as YAML to the supplied file.
146
160
  def export(file)
147
161
  require 'yaml'
148
162
  File.open(file, 'w') { |out| YAML.dump(@config, out) }
149
163
  end
150
164
 
165
+ # Spacial case - perform update to the configuration. This should
166
+ # later include handling of +all+ versions of the config!
167
+ #
168
+ # Returns the name of the backup file or nil if there was no need
169
+ # for backup.
170
+ def update_config
171
+ return if @config['VERSION'] == Version
172
+ backup_file = @config_file + '.none'
173
+ require 'fileutils'
174
+ new_config = config_none_to_10(@config)
175
+ FileUtils.move(@config_file, backup_file)
176
+ @config = new_config
177
+ @config['VERSION'] = Version
178
+ save
179
+ backup_file
180
+ end
181
+
151
182
  private
152
183
 
153
184
  def save
154
- require 'ftools'
185
+ require 'fileutils'
155
186
  # create backup (File.copy always seems to overwrite existing file)
156
- File.copy(@config_file, @config_file + '.bak') if File.exists? @config_file
187
+ FileUtils.copy(@config_file, @config_file + '.bak') if File.exists? @config_file
157
188
  File.open(@config_file, 'w') { |out| Marshal.dump(@config, out) }
158
189
  end
159
190
 
@@ -163,5 +194,22 @@ module RunSSHLib
163
194
  hsh[ky]
164
195
  end
165
196
  end
197
+
198
+ # convert the config hash from version none (runssh 0.1) to version 1.0
199
+ # group is the hash that holds all the groups/hostdefs (@config).
200
+ def config_none_to_10 group
201
+ group.each do |key, value|
202
+ case
203
+ when value.instance_of?(RunSSHLib::HostDef)
204
+ hsh = Hash.new
205
+ hsh[:host_name] = value.name
206
+ hsh[:login] = value.login if value.login
207
+ group[key] = RunSSHLib::SshHostDef.new(hsh)
208
+ when value.instance_of?(Hash)
209
+ config_none_to_10(value)
210
+ end
211
+ end
212
+ group
213
+ end
166
214
  end
167
215
  end
@@ -18,19 +18,24 @@
18
18
 
19
19
  module RunSSHLib
20
20
 
21
- # A class to handle ssh operations.
22
- class SshBackend
23
- # New backend with host/login details.
24
- def initialize(host_def, overrides)
25
- @host = host_def.name
26
- @user = overrides[:login] ? overrides[:login] : host_def.login
27
- end
21
+ # A collection of ssh procedures.
22
+ module SshBackend
23
+ module_function
28
24
 
29
25
  # run shell on remote host.
30
- def shell
31
- command = "ssh #{@user ? %Q(-l #{@user}) : ''} #{@host}"
26
+ # definition:: A Hash containing required data for
27
+ # making shell connection (e.g., :host_name, :login).
28
+ #
29
+ # For running remote commands add to the _definition_ hash
30
+ # the entire remote command as a string with a +:remote_cmd+ key.
31
+ def shell(definition)
32
+ raise "no hostname" unless definition[:host_name] # should never happen
33
+ command = "ssh "
34
+ command << "-l #{definition[:login]} " if definition[:login]
35
+ command << "#{definition[:host_name]}"
36
+ command << %( -- "#{definition[:remote_cmd]}") if
37
+ (definition[:remote_cmd] && (!definition[:remote_cmd].empty?))
32
38
  exec command
33
39
  end
34
-
35
40
  end
36
41
  end
@@ -0,0 +1,49 @@
1
+ #
2
+ # Copyright (C) 2010 Haim Ashkenazi
3
+ #
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+ #
18
+ module RunSSHLib
19
+ class SshHostDef
20
+ attr_reader :definition
21
+
22
+ # Initialize ssh host def with definition.
23
+ #
24
+ # [definition] A hash containing ssh options. <i>:host_name</i> is required.
25
+ # Could also be a hostname (string) for quick defining SshHostDef
26
+ # with only hostname.
27
+ def initialize(definition)
28
+ if definition.instance_of? String
29
+ definition = { :host_name => definition }
30
+ end
31
+ raise ArgumentError, "Missing hostname" unless definition[:host_name]
32
+ @definition = definition
33
+ end
34
+
35
+ # should be equal if @definition is equal
36
+ def ==(other)
37
+ return false if other.nil?
38
+ return false unless other.instance_of? SshHostDef
39
+ definition == other.definition
40
+ end
41
+ alias_method :eql?, :==
42
+
43
+ def to_print
44
+ out = " * host: #{definition[:host_name]}"
45
+ out << "\n * login: #{definition[:login] || 'current user'}"
46
+ out
47
+ end
48
+ end
49
+ end