runssh 0.1.1 → 0.2.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.
@@ -0,0 +1,27 @@
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
+
19
+ module RunSSHLib
20
+ module Version
21
+ MAJOR = 0
22
+ MINOR = 2
23
+ BUILD = 0
24
+
25
+ STRING = [MAJOR, MINOR, BUILD].compact.join('.')
26
+ end
27
+ end
data/lib/runsshlib.rb CHANGED
@@ -19,6 +19,9 @@
19
19
  require 'runsshlib/cli'
20
20
  require 'runsshlib/config_file'
21
21
  require 'runsshlib/ssh_backend'
22
+ require 'runsshlib/ssh_host_def'
23
+ require 'runsshlib/version'
24
+ require 'highline'
22
25
 
23
26
  # Main RunSSHLib module.
24
27
  module RunSSHLib
@@ -31,14 +34,10 @@ module RunSSHLib
31
34
  # Indicates invalid command
32
35
  class InvalidSubCommandError < StandardError; end
33
36
 
37
+ # Indicates older config version.
38
+ # message should contain only the older config version!
39
+ class OlderConfigVersionError < StandardError; end
40
+
34
41
  # A placeholder for host definitions
35
42
  HostDef = Struct.new(:name, :login)
36
-
37
- module Version
38
- MAJOR = 0
39
- MINOR = 1
40
- BUILD = 1
41
-
42
- STRING = [MAJOR, MINOR, BUILD].compact.join('.')
43
- end
44
43
  end
data/runssh.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "runsshlib/version"
4
+
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = 'runssh'
7
+ s.version = RunSSHLib::Version::STRING
8
+ s.platform = Gem::Platform::RUBY
9
+ s.author = 'Haim Ashkenazi'
10
+ s.email = 'haim@babysnakes.org'
11
+ s.homepage = 'http://github.com/babysnakes/runssh'
12
+
13
+ s.summary = "CLI utility to bookmark multiple ssh connections with hierarchy."
14
+ s.description = <<EOF
15
+ Runssh is a command line utility to help bookmark many
16
+ ssh connections in heirarchial groups.
17
+ EOF
18
+
19
+ s.required_ruby_version = '>= 1.8.7'
20
+ s.add_dependency('trollop', '~> 1.16.2')
21
+ s.add_dependency('highline', '~> 1.6.1')
22
+ s.add_development_dependency('rspec', "~> 2.1.0")
23
+ s.add_development_dependency('rcov', '~> 0.9.9')
24
+ s.add_development_dependency('autotest', '~> 4.4.4')
25
+ s.add_development_dependency('autotest-fsevent', '~> 0.2.3')
26
+ s.add_development_dependency('autotest-growl', '~> 0.2.6')
27
+
28
+ s.has_rdoc = true
29
+ s.extra_rdoc_files = ['README.rdoc']
30
+ s.rdoc_options << '--main' << 'README.rdoc'
31
+ s.rdoc_options << '-c' << 'UTF-8'
32
+
33
+ s.files = `git ls-files`.split("\n")
34
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
35
+ s.executables << 'runssh'
36
+ s.require_paths = ['lib']
37
+ end
@@ -1,19 +1,24 @@
1
- ---
2
- :cust1:
3
- :dc1:
4
- :host2: !ruby/struct:RunSSHLib::HostDef
5
- name: b.host.com
6
- login: user1
7
- :host1: !ruby/struct:RunSSHLib::HostDef
8
- name: a.host.com
9
- login: user1
10
- :dc2:
11
- :host1: !ruby/struct:RunSSHLib::HostDef
12
- name: c.host.com
13
- login: user3
14
- :cust2:
15
- :dc:
16
- :internal:
17
- :somehost: !ruby/struct:RunSSHLib::HostDef
18
- name: a.example.com
19
- login: otheruser
1
+ ---
2
+ VERSION: 1.0
3
+ :cust1:
4
+ :dc1:
5
+ :host1: !ruby/object:RunSSHLib::SshHostDef
6
+ definition:
7
+ :login: user1
8
+ :host_name: a.host.com
9
+ :host2: !ruby/object:RunSSHLib::SshHostDef
10
+ definition:
11
+ :login: user1
12
+ :host_name: b.host.com
13
+ :dc2:
14
+ :host1: !ruby/object:RunSSHLib::SshHostDef
15
+ definition:
16
+ :login: user3
17
+ :host_name: c.host.com
18
+ :cust2:
19
+ :dc:
20
+ :internal:
21
+ :somehost: !ruby/object:RunSSHLib::SshHostDef
22
+ definition:
23
+ :login: otheruser
24
+ :host_name: a.example.com
@@ -0,0 +1,19 @@
1
+ ---
2
+ :cust1:
3
+ :dc1:
4
+ :host2: !ruby/struct:RunSSHLib::HostDef
5
+ name: b.host.com
6
+ login: user1
7
+ :host1: !ruby/struct:RunSSHLib::HostDef
8
+ name: a.host.com
9
+ login: user1
10
+ :dc2:
11
+ :host1: !ruby/struct:RunSSHLib::HostDef
12
+ name: c.host.com
13
+ login: user3
14
+ :cust2:
15
+ :dc:
16
+ :internal:
17
+ :somehost: !ruby/struct:RunSSHLib::HostDef
18
+ name: a.example.com
19
+ login: otheruser
@@ -15,9 +15,10 @@
15
15
  # along with this program; if not, write to the Free Software
16
16
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
  #
18
- require 'lib/runsshlib'
19
18
  require 'spec_helper'
19
+ require 'runsshlib'
20
20
  require 'stringio'
21
+ require 'yaml'
21
22
 
22
23
  describe "The CLI interface" do
23
24
  # a shortcut to verify the help for print command
@@ -74,6 +75,36 @@ describe "The CLI interface" do
74
75
  end.to exit_abnormaly
75
76
  @buffer.should match(/invalid command/)
76
77
  end
78
+
79
+ it "should display right message upon older configuration error" do
80
+ dump_config Hash.new
81
+ expect do
82
+ cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} print ?))
83
+ end.to exit_abnormaly
84
+ @buffer.should match(/--update-config/)
85
+ @buffer.should match(/.none/)
86
+ end
87
+
88
+ describe "with --update_config" do
89
+ let(:config_v_none) do
90
+ YAML.load_file(File.join(File.dirname(__FILE__), '..',
91
+ 'fixtures', 'runssh_v_none.yml'))
92
+ end
93
+
94
+ it "should accept --update-config as argument" do
95
+ cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} --update-config))
96
+ end
97
+
98
+ it "should not fail upon initialization if config is of older version" do
99
+ dump_config config_v_none
100
+ cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} --update-config))
101
+ end
102
+
103
+ it "should not initialize @config object after initialization" do
104
+ cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} --update-config))
105
+ cli.instance_variable_get(:@c).should be_nil
106
+ end
107
+ end
77
108
  end
78
109
 
79
110
  describe "main help" do
@@ -97,8 +128,43 @@ describe "The CLI interface" do
97
128
  cli.run
98
129
  end
99
130
 
131
+ it "should run run_update_config when called with --update-config" do
132
+ @cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} --update-config))
133
+ @cli.should_receive(:run_update_config)
134
+ @cli.run
135
+ end
136
+
137
+ context "update_config" do
138
+ let(:cf) { double('ConfigFile') }
139
+
140
+ it "should initialize ConfigFile with old_version=true and run update_config" do
141
+ cf.should_receive(:update_config)
142
+ RunSSHLib::ConfigFile.should_receive(:new).with(TMP_FILE, true).
143
+ and_return(cf)
144
+ @cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} --update-config))
145
+ @cli.run
146
+ end
147
+
148
+ it "should inform the user of success and backup file" do
149
+ backup_path = "/path/to/backup_file"
150
+ cf.should_receive(:update_config).and_return(backup_path)
151
+ RunSSHLib::ConfigFile.should_receive(:new).and_return(cf)
152
+ @cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} --update-config))
153
+ @cli.run
154
+ @buffer.should include(backup_path)
155
+ end
156
+
157
+ it "should inform the user if no backup was required" do
158
+ cf.should_receive(:update_config).and_return(nil)
159
+ RunSSHLib::ConfigFile.should_receive(:new).and_return(cf)
160
+ @cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} --update-config))
161
+ @cli.run
162
+ @buffer.should include("No update was performed")
163
+ end
164
+ end
165
+
100
166
  describe "with subcommand" do
101
- describe "shell" do
167
+ context "shell" do
102
168
  before(:each) do
103
169
  @shell_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} shell))
104
170
  end
@@ -121,15 +187,39 @@ describe "The CLI interface" do
121
187
  options = @shell_cli.instance_variable_get :@options
122
188
  options.should have_key(:login)
123
189
  end
190
+
191
+ it "should not overwrite nil arguments with saved ones when merging" do
192
+ import_fixtures
193
+ RunSSHLib::SshBackend.should_receive(:shell).
194
+ with(hash_including(
195
+ :host_name => "a.example.com",
196
+ :login => "otheruser")).
197
+ and_return(nil)
198
+ cli = RunSSHLib::CLI.new(
199
+ %W(-f #{TMP_FILE} shell cust2 dc internal somehost))
200
+ cli.run
201
+ end
202
+
203
+ it "should correctly call SshBackend.shell with merged definition" do
204
+ import_fixtures
205
+ RunSSHLib::SshBackend.should_receive(:shell).
206
+ with(hash_including(:host_name => "a.example.com",
207
+ :login => "someuser")).
208
+ and_return(nil)
209
+ cli = RunSSHLib::CLI.new(
210
+ %W(-f #{TMP_FILE} shell -l someuser cust2 dc internal somehost))
211
+ cli.run
212
+ end
124
213
 
125
- it "should correctly initialize SshBackend" do
126
- somehost = RunSSHLib::HostDef.new('a.example.com', 'otheruser')
127
- mock_ssh_backend = double('SshBackend')
128
- mock_ssh_backend.should_receive(:shell)
129
- RunSSHLib::SshBackend.should_receive(:new).
130
- with(somehost, {:login=>nil, :help=>false}).
131
- and_return(mock_ssh_backend)
132
- cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} shell cust2 dc internal somehost))
214
+ it "should correctly parse remote command (indicated by --)" do
215
+ import_fixtures
216
+ RunSSHLib::SshBackend.should_receive(:shell).
217
+ with(hash_including(:remote_cmd => "ls -l /tmp")).
218
+ and_return(nil)
219
+ cli = RunSSHLib::CLI.new(
220
+ %W(-f #{TMP_FILE} shell -l someuser cust2 dc internal somehost
221
+ -- ls -l /tmp)
222
+ )
133
223
  cli.run
134
224
  end
135
225
  end
@@ -146,12 +236,14 @@ describe "The CLI interface" do
146
236
  it "should have all required arguments" do
147
237
  options = @add_cli.instance_variable_get :@options
148
238
  options.should have_key(:host_name)
149
- options.should have_key(:user)
239
+ options.should have_key(:login)
150
240
  end
151
241
 
152
242
  it "should invoke the add_host_def" do
153
243
  @add_cli.instance_variable_get(:@c).should_receive(:add_host_def).
154
- with([:one], :two, RunSSHLib::HostDef.new('host'))
244
+ with([:one], :two, RunSSHLib::SshHostDef.new({
245
+ :host_name => 'host',:login => nil
246
+ }))
155
247
  @add_cli.run
156
248
  end
157
249
  end
@@ -160,6 +252,9 @@ describe "The CLI interface" do
160
252
  before(:each) do
161
253
  @d_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} del))
162
254
  @d_cli.instance_variable_get(:@c).stub(:delete_path)
255
+ @hl = double('HighLine')
256
+ HighLine.stub(:new) {@hl}
257
+ @hl.stub(:agree)
163
258
  end
164
259
 
165
260
  it "should parse 'd' as del" do
@@ -167,18 +262,18 @@ describe "The CLI interface" do
167
262
  end
168
263
 
169
264
  it "should verify the deletion" do
170
- @d_cli.should_receive(:verify_yn).with(/Are you sure/)
265
+ @hl.should_receive(:agree).with(/Are you sure/)
171
266
  @d_cli.run
172
267
  end
173
268
 
174
269
  it "should perform the deletion upon confirmation" do
175
- @d_cli.should_receive(:verify_yn).and_return(true)
270
+ @hl.should_receive(:agree).and_return(true)
176
271
  @d_cli.instance_variable_get(:@c).should_receive(:delete_path)
177
272
  @d_cli.run
178
273
  end
179
274
 
180
275
  it "should cancel the deletion if not confirmed" do
181
- @d_cli.should_receive(:verify_yn).and_return(false)
276
+ @hl.should_receive(:agree).and_return(false)
182
277
  @d_cli.instance_variable_get(:@c).should_not_receive(:delete_path)
183
278
  @d_cli.run
184
279
  @buffer.should match(/cancel/)
@@ -186,7 +281,7 @@ describe "The CLI interface" do
186
281
 
187
282
  it "should pass the right path to delete_path" do
188
283
  cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} del one two three))
189
- cli.stub(:verify_yn).and_return(true)
284
+ @hl.should_receive(:agree).and_return(true)
190
285
  cli.instance_variable_get(:@c).should_receive(:delete_path).
191
286
  with([:one, :two, :three])
192
287
  cli.run
@@ -205,19 +300,22 @@ describe "The CLI interface" do
205
300
  it "should have all required argumants" do
206
301
  options = @update_cli.instance_variable_get :@options
207
302
  options.should have_key(:host_name)
208
- options.should have_key(:user)
303
+ options.should have_key(:login)
209
304
  end
210
305
 
211
306
  it "should invoke update_host_def" do
212
307
  config = @update_cli.instance_variable_get :@c
213
308
  config.should_receive(:update_host_def).
214
- with([:root], RunSSHLib::HostDef.new('newhost'))
309
+ with([:root], RunSSHLib::SshHostDef.new({
310
+ :host_name => "newhost", :login => nil
311
+ }))
215
312
  @update_cli.run
216
313
  end
217
314
  end
218
315
 
219
316
  describe "print" do
220
317
  before(:each) do
318
+ import_fixtures
221
319
  @p_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} print cust2 dc internal somehost))
222
320
  end
223
321
 
@@ -225,19 +323,10 @@ describe "The CLI interface" do
225
323
  @p_cli.send(:extract_subcommand, ['p']).should eql('print')
226
324
  end
227
325
 
228
- it "should print correctly host definition with user" do
326
+ it "should print correctly host definition" do
229
327
  @p_cli.run
328
+ @buffer.should match(/Host definition for: somehost/)
230
329
  @buffer.should match(/host: a.example.com/)
231
- @buffer.should match(/user: otheruser/)
232
- end
233
-
234
- it "should print correctly host definition without user" do
235
- c = RunSSHLib::ConfigFile.new("#{TMP_FILE}")
236
- c.add_host_def([:three, :four], :five, RunSSHLib::HostDef.new('anewhost'))
237
- cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} print three four five))
238
- cli.run
239
- @buffer.should match(/host: anewhost/)
240
- @buffer.should match(/user: current user/)
241
330
  end
242
331
  end
243
332
 
@@ -245,6 +334,9 @@ describe "The CLI interface" do
245
334
  before(:each) do
246
335
  @i_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} import -i inputfile))
247
336
  @i_cli.instance_variable_get(:@c).stub(:import)
337
+ @hl = double('HighLine')
338
+ HighLine.stub(:new) {@hl}
339
+ @hl.stub(:agree)
248
340
  end
249
341
 
250
342
  it "should parse 'i' as import" do
@@ -257,25 +349,25 @@ describe "The CLI interface" do
257
349
  end
258
350
 
259
351
  it "should verify the import with the user" do
260
- @i_cli.should_receive(:verify_yn).with(/OVERWRITES/)
352
+ @hl.should_receive(:agree).with(/OVERWRITES/)
261
353
  @i_cli.run
262
354
  end
263
355
 
264
356
  it "should run import upon confirmation" do
265
- @i_cli.should_receive(:verify_yn).and_return(true)
357
+ @hl.should_receive(:agree).and_return(true)
266
358
  @i_cli.instance_variable_get(:@c).should_receive(:import)
267
359
  @i_cli.run
268
360
  end
269
361
 
270
362
  it "should cancel if not confirmed" do
271
- @i_cli.should_receive(:verify_yn).and_return(false)
363
+ @hl.should_receive(:agree).and_return(false)
272
364
  @i_cli.instance_variable_get(:@c).should_not_receive(:import)
273
365
  @i_cli.run
274
366
  @buffer.should match(/cancel/)
275
367
  end
276
368
 
277
369
  it "should pass the right argument to import" do
278
- @i_cli.should_receive(:verify_yn).and_return(true)
370
+ @hl.should_receive(:agree).and_return(true)
279
371
  @i_cli.instance_variable_get(:@c).should_receive(:import).
280
372
  with('inputfile')
281
373
  @i_cli.run
@@ -298,37 +390,9 @@ describe "The CLI interface" do
298
390
  end
299
391
  end
300
392
  end
301
-
302
- describe "verify_yn" do
303
- before(:each) do
304
- @verify_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} print))
305
- end
306
-
307
- it "should parse 'y' as true" do
308
- stdin = "y\n"
309
- $stdin = StringIO.open(stdin, 'r')
310
- @verify_cli.send(:verify_yn, 'question').should be_true
311
- end
312
-
313
- it "should parse all other as false" do
314
- tests = ["n", "\n", "Y\n", "maybe", "\n"]
315
- tests.each do |test_phrase|
316
- stdin = test_phrase
317
- $stdin = StringIO.open(stdin, 'r')
318
- @verify_cli.send(:verify_yn, 'question').should be_false
319
- end
320
- end
321
-
322
- it "should add postfix to the question" do
323
- stdin = "n"
324
- $stdin = StringIO.open(stdin, 'r')
325
- @verify_cli.send(:verify_yn, 'are you sure')
326
- @buffer.should eql('are you sure (y/n)? ')
327
- end
328
- end
329
393
  end
330
394
 
331
- after(:all) do
395
+ after(:each) do
332
396
  cleanup_tmp_file
333
397
  end
334
398
  end