runssh 0.1.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/README.rdoc +45 -0
- data/Rakefile +64 -0
- data/bin/runssh +31 -0
- data/bin/runssh_comp.sh +24 -0
- data/gpl-2.0.txt +339 -0
- data/lib/runsshlib/cli.rb +287 -0
- data/lib/runsshlib/config_file.rb +167 -0
- data/lib/runsshlib/ssh_backend.rb +36 -0
- data/lib/runsshlib.rb +44 -0
- data/spec/fixtures/runssh.yml +19 -0
- data/spec/runsshlib/cli_spec.rb +334 -0
- data/spec/runsshlib/config_file_spec.rb +275 -0
- data/spec/runsshlib/ssh_backend_spec.rb +68 -0
- data/spec/spec_helper.rb +85 -0
- metadata +133 -0
@@ -0,0 +1,334 @@
|
|
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
|
+
require 'lib/runsshlib'
|
19
|
+
require 'spec_helper'
|
20
|
+
require 'stringio'
|
21
|
+
|
22
|
+
describe "The CLI interface" do
|
23
|
+
# a shortcut to verify the help for print command
|
24
|
+
def match_print
|
25
|
+
@buffer.should match(/Print host configuration/)
|
26
|
+
end
|
27
|
+
|
28
|
+
before(:each) do
|
29
|
+
@buffer = ""
|
30
|
+
$stdout = StringIO.open(@buffer, 'w')
|
31
|
+
$stderr = StringIO.open(@buffer, 'w')
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "when initialized" do
|
35
|
+
it "should display help when run with no arguments" do
|
36
|
+
expect { RunSSHLib::CLI.new([]) }.to exit_normaly
|
37
|
+
@buffer.should match(/Available commands:/)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should display help when called with help as the only parameter" do
|
41
|
+
expect { RunSSHLib::CLI.new(['help']) }.to exit_normaly
|
42
|
+
@buffer.should match(/Available commands:/)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should correctly process the -f argument" do
|
46
|
+
cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} print test))
|
47
|
+
global_options = cli.instance_variable_get :@global_options
|
48
|
+
global_options[:config_file].should eql("#{TMP_FILE}")
|
49
|
+
cli.instance_variable_get(:@c).
|
50
|
+
instance_variable_get(:@config_file).
|
51
|
+
should eql("#{TMP_FILE}")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should display version when asked for" do
|
55
|
+
expect { RunSSHLib::CLI.new(['-v'])}.to exit_normaly
|
56
|
+
@buffer.should include(RunSSHLib::Version::STRING)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should correctly process the `help command` scheme" do
|
60
|
+
expect do
|
61
|
+
RunSSHLib::CLI.new(%w(help print ?))
|
62
|
+
end.to exit_normaly
|
63
|
+
match_print
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should correctly parse the `?` for completion" do
|
67
|
+
cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} print ?))
|
68
|
+
cli.instance_variable_get(:@completion_requested).should be_true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should raise an error when invoked with invalid command" do
|
72
|
+
expect do
|
73
|
+
RunSSHLib::CLI.new(%W(-f #{TMP_FILE} wrong))
|
74
|
+
end.to exit_abnormaly
|
75
|
+
@buffer.should match(/invalid command/)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "main help" do
|
80
|
+
it "should include a description of all subcommands" do
|
81
|
+
expect { RunSSHLib::CLI.new([]) }.to exit_normaly
|
82
|
+
RunSSHLib::CLI::COMMAND.each do |subcmd|
|
83
|
+
@buffer.should include("* #{subcmd}")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "when run" do
|
89
|
+
before(:all) do
|
90
|
+
import_fixtures
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should parse display completions and exit if requested" do
|
94
|
+
cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} print ?))
|
95
|
+
cli.should_not_receive(:run_print)
|
96
|
+
cli.instance_variable_get(:@c).should_receive(:list_groups).with([])
|
97
|
+
cli.run
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "with subcommand" do
|
101
|
+
describe "shell" do
|
102
|
+
before(:each) do
|
103
|
+
@shell_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} shell))
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should require a path" do
|
107
|
+
expect { @shell_cli.run }.to exit_abnormaly
|
108
|
+
@buffer.should include("not host definition")
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should execute the run_shell method" do
|
112
|
+
@shell_cli.should_receive(:run_shell)
|
113
|
+
@shell_cli.run
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should parse 's' as shell" do
|
117
|
+
@shell_cli.send(:extract_subcommand, %w(s root)).should eql('shell')
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should have all required arguments" do
|
121
|
+
options = @shell_cli.instance_variable_get :@options
|
122
|
+
options.should have_key(:login)
|
123
|
+
end
|
124
|
+
|
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))
|
133
|
+
cli.run
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "add" do
|
138
|
+
before(:each) do
|
139
|
+
@add_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} add -n host one two))
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should parse 'a' as add" do
|
143
|
+
@add_cli.send(:extract_subcommand, ['a']).should eql('add')
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should have all required arguments" do
|
147
|
+
options = @add_cli.instance_variable_get :@options
|
148
|
+
options.should have_key(:host_name)
|
149
|
+
options.should have_key(:user)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should invoke the add_host_def" do
|
153
|
+
@add_cli.instance_variable_get(:@c).should_receive(:add_host_def).
|
154
|
+
with([:one], :two, RunSSHLib::HostDef.new('host'))
|
155
|
+
@add_cli.run
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe "del" do
|
160
|
+
before(:each) do
|
161
|
+
@d_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} del))
|
162
|
+
@d_cli.instance_variable_get(:@c).stub(:delete_path)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should parse 'd' as del" do
|
166
|
+
@d_cli.send(:extract_subcommand, ['d']).should eql('del')
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should verify the deletion" do
|
170
|
+
@d_cli.should_receive(:verify_yn).with(/Are you sure/)
|
171
|
+
@d_cli.run
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should perform the deletion upon confirmation" do
|
175
|
+
@d_cli.should_receive(:verify_yn).and_return(true)
|
176
|
+
@d_cli.instance_variable_get(:@c).should_receive(:delete_path)
|
177
|
+
@d_cli.run
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should cancel the deletion if not confirmed" do
|
181
|
+
@d_cli.should_receive(:verify_yn).and_return(false)
|
182
|
+
@d_cli.instance_variable_get(:@c).should_not_receive(:delete_path)
|
183
|
+
@d_cli.run
|
184
|
+
@buffer.should match(/cancel/)
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should pass the right path to delete_path" do
|
188
|
+
cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} del one two three))
|
189
|
+
cli.stub(:verify_yn).and_return(true)
|
190
|
+
cli.instance_variable_get(:@c).should_receive(:delete_path).
|
191
|
+
with([:one, :two, :three])
|
192
|
+
cli.run
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "update" do
|
197
|
+
before(:each) do
|
198
|
+
@update_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} update -n newhost root))
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should parse 'u' as update" do
|
202
|
+
@update_cli.send(:extract_subcommand, ['u']).should eql('update')
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should have all required argumants" do
|
206
|
+
options = @update_cli.instance_variable_get :@options
|
207
|
+
options.should have_key(:host_name)
|
208
|
+
options.should have_key(:user)
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should invoke update_host_def" do
|
212
|
+
config = @update_cli.instance_variable_get :@c
|
213
|
+
config.should_receive(:update_host_def).
|
214
|
+
with([:root], RunSSHLib::HostDef.new('newhost'))
|
215
|
+
@update_cli.run
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "print" do
|
220
|
+
before(:each) do
|
221
|
+
@p_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} print cust2 dc internal somehost))
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should parse 'p' as print" do
|
225
|
+
@p_cli.send(:extract_subcommand, ['p']).should eql('print')
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should print correctly host definition with user" do
|
229
|
+
@p_cli.run
|
230
|
+
@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
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe "import" do
|
245
|
+
before(:each) do
|
246
|
+
@i_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} import -i inputfile))
|
247
|
+
@i_cli.instance_variable_get(:@c).stub(:import)
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should parse 'i' as import" do
|
251
|
+
@i_cli.send(:extract_subcommand, ['i']).should eql('import')
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should have the right arguments" do
|
255
|
+
options = @i_cli.instance_variable_get :@options
|
256
|
+
options.include?(:input_file)
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should verify the import with the user" do
|
260
|
+
@i_cli.should_receive(:verify_yn).with(/OVERWRITES/)
|
261
|
+
@i_cli.run
|
262
|
+
end
|
263
|
+
|
264
|
+
it "should run import upon confirmation" do
|
265
|
+
@i_cli.should_receive(:verify_yn).and_return(true)
|
266
|
+
@i_cli.instance_variable_get(:@c).should_receive(:import)
|
267
|
+
@i_cli.run
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should cancel if not confirmed" do
|
271
|
+
@i_cli.should_receive(:verify_yn).and_return(false)
|
272
|
+
@i_cli.instance_variable_get(:@c).should_not_receive(:import)
|
273
|
+
@i_cli.run
|
274
|
+
@buffer.should match(/cancel/)
|
275
|
+
end
|
276
|
+
|
277
|
+
it "should pass the right argument to import" do
|
278
|
+
@i_cli.should_receive(:verify_yn).and_return(true)
|
279
|
+
@i_cli.instance_variable_get(:@c).should_receive(:import).
|
280
|
+
with('inputfile')
|
281
|
+
@i_cli.run
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "export" do
|
286
|
+
before(:each) do
|
287
|
+
@e_cli = RunSSHLib::CLI.new(%W(-f #{TMP_FILE} export -o somefile))
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should parse 'e' as export" do
|
291
|
+
@e_cli.send(:extract_subcommand, ['e']).should eql('export')
|
292
|
+
end
|
293
|
+
|
294
|
+
it "should run export with the right parameters" do
|
295
|
+
@e_cli.instance_variable_get(:@c).should_receive(:export).
|
296
|
+
with('somefile')
|
297
|
+
@e_cli.run
|
298
|
+
end
|
299
|
+
end
|
300
|
+
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
|
+
end
|
330
|
+
|
331
|
+
after(:all) do
|
332
|
+
cleanup_tmp_file
|
333
|
+
end
|
334
|
+
end
|
@@ -0,0 +1,275 @@
|
|
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
|
+
require 'lib/runsshlib'
|
20
|
+
require 'tmpdir'
|
21
|
+
require 'spec_helper'
|
22
|
+
|
23
|
+
describe "RunSSH Configuration class" do
|
24
|
+
|
25
|
+
def read_config
|
26
|
+
File.open(@temp_file) { |io| Marshal.load(io) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def initial_data
|
30
|
+
@c = RunSSHLib::ConfigFile.new(@temp_file)
|
31
|
+
@c.add_host_def([:one, :two, :three], :www,
|
32
|
+
RunSSHLib::HostDef.new('www.example.com', 'me'))
|
33
|
+
end
|
34
|
+
|
35
|
+
before(:all) do
|
36
|
+
@temp_file = File.join(Dir.tmpdir, 'tempfile')
|
37
|
+
@temp_file_bak = @temp_file + '.bak'
|
38
|
+
@h1 = RunSSHLib::HostDef.new('a.b.c')
|
39
|
+
@h2 = RunSSHLib::HostDef.new('b.b.c', 'meme')
|
40
|
+
@tmp_yml = File.join(Dir.tmpdir, 'tempyml')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should save a new empty configuration if none exists" do
|
44
|
+
RunSSHLib::ConfigFile.new(@temp_file)
|
45
|
+
read_config.should == {}
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should create a backup while saving" do
|
49
|
+
c = RunSSHLib::ConfigFile.new(@temp_file)
|
50
|
+
c.send(:save)
|
51
|
+
# the 2 files should match
|
52
|
+
File.read(@temp_file).should == File.read(@temp_file_bak)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should overwrite existing backup if one already exists" do
|
56
|
+
# create a new file and a copy of it
|
57
|
+
c = RunSSHLib::ConfigFile.new(@temp_file)
|
58
|
+
c.send(:save)
|
59
|
+
# sanity
|
60
|
+
File.read(@temp_file).should == File.read(@temp_file_bak)
|
61
|
+
b = RunSSHLib::ConfigFile.new(@temp_file)
|
62
|
+
b.add_host_def([:one, :two, :three], :www, @h1)
|
63
|
+
File.read(@temp_file).should_not == File.read(@temp_file_bak)
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "when adding host" do
|
68
|
+
it "should not overwrite existing paths with host definition" do
|
69
|
+
initial_data
|
70
|
+
lambda {@c.add_host_def([:one, :two], :three, @h1)}.
|
71
|
+
should raise_exception(RunSSHLib::ConfigError, /path already exist/)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should not overwrite existing hosts with path" do
|
75
|
+
initial_data
|
76
|
+
lambda {@c.add_host_def([:one, :two, :three, :www], :host, @h1)}.
|
77
|
+
should raise_exception(RunSSHLib::ConfigError,
|
78
|
+
/Cannot override host definition with path/)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should not overwrite existing hosts" do
|
82
|
+
initial_data
|
83
|
+
lambda {@c.add_host_def([:one, :two, :three], :www, @h1)}.
|
84
|
+
should raise_exception(RunSSHLib::ConfigError, /path already exist/)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should fail if invalid host definition" do
|
88
|
+
initial_data
|
89
|
+
lambda { @c.add_host_def([], :host, :error) }.
|
90
|
+
should raise_error(RunSSHLib::ConfigError, /Invalid host definition/)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should correctly merge different paths" do
|
94
|
+
c = RunSSHLib::ConfigFile.new(@temp_file)
|
95
|
+
c.add_host_def([:one, :two], :h1, @h1)
|
96
|
+
c.add_host_def([:three, :four], :h2, @h2)
|
97
|
+
d = RunSSHLib::ConfigFile.new(@temp_file)
|
98
|
+
d.get_host([:one, :two, :h1]).should == @h1
|
99
|
+
d.get_host([:three, :four, :h2]).should == @h2
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should correctly merge paths with common path" do
|
103
|
+
c = RunSSHLib::ConfigFile.new(@temp_file)
|
104
|
+
c.add_host_def([:one, :two], :h1, @h1)
|
105
|
+
c.add_host_def([:one, :three], :h2, @h2)
|
106
|
+
d = RunSSHLib::ConfigFile.new(@temp_file)
|
107
|
+
d.get_host([:one, :two, :h1]).should == @h1
|
108
|
+
d.get_host([:one, :three, :h2]).should == @h2
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should correctly merge hosts under same path" do
|
112
|
+
c = RunSSHLib::ConfigFile.new(@temp_file)
|
113
|
+
c.add_host_def([:one, :two], :h1, @h1)
|
114
|
+
c.add_host_def([:one, :two], :h2, @h2)
|
115
|
+
d = RunSSHLib::ConfigFile.new(@temp_file)
|
116
|
+
d.get_host([:one, :two, :h1]).should == @h1
|
117
|
+
d.get_host([:one, :two, :h2]).should == @h2
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "when updating host" do
|
122
|
+
it "should refuse to *update* non-existing host" do
|
123
|
+
initial_data
|
124
|
+
lambda { @c.update_host_def([:one, :two, :host], @h1) }.
|
125
|
+
should raise_error(RunSSHLib::ConfigError,
|
126
|
+
/Host definition doesn't exist/)
|
127
|
+
lambda { @c.update_host_def([:wrong, :path], @h1) }.
|
128
|
+
should raise_error(RunSSHLib::ConfigError, /Invalid path!/)
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should refuse to accept invalid host definition" do
|
133
|
+
initial_data
|
134
|
+
lambda { @c.update_host_def([:one, :two, :three, :www], :wrong) }.
|
135
|
+
should raise_error(RunSSHLib::ConfigError, /Invalid/)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should NOT replace path with host definition" do
|
139
|
+
initial_data
|
140
|
+
lambda { @c.update_host_def([:one, :two, :three], @h2) }.
|
141
|
+
should raise_error(RunSSHLib::ConfigError, /Cannot overwrite/)
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should correctly update and save host definition" do
|
146
|
+
initial_data
|
147
|
+
@c.update_host_def([:one, :two, :three, :www], @h2)
|
148
|
+
c = RunSSHLib::ConfigFile.new(@temp_file)
|
149
|
+
c.get_host([:one, :two, :three, :www]).should == @h2
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should handle invalid paths correctly" do
|
153
|
+
initial_data
|
154
|
+
lambda { @c.update_host_def([:two, :three, :four], @h2) }.
|
155
|
+
should raise_error(RunSSHLib::ConfigError, /Invalid/)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe "when deleting host" do
|
160
|
+
before(:each) do
|
161
|
+
initial_data
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should raise error if path is invalid" do
|
165
|
+
lambda { @c.delete_path([:one, :three]) }.
|
166
|
+
should raise_error(RunSSHLib::ConfigError, /invalid path/i)
|
167
|
+
lambda { @c.delete_path([:non, :existing]) }.
|
168
|
+
should raise_error(RunSSHLib::ConfigError, /invalid path/i)
|
169
|
+
lambda { @c.delete_path([]) }.
|
170
|
+
should raise_error(RunSSHLib::ConfigError, /invalid path/i)
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should refuse to delete non-empty groups" do
|
174
|
+
lambda { @c.delete_path([:one, :two]) }.
|
175
|
+
should raise_error(RunSSHLib::ConfigError,
|
176
|
+
/Supplied path is non-empty group/)
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should delete host definitions" do
|
180
|
+
@c.add_host_def([:another, :path], :host, @h2)
|
181
|
+
@c.delete_path([:another, :path, :host])
|
182
|
+
b = RunSSHLib::ConfigFile.new(@temp_file)
|
183
|
+
b.list_groups([:another, :path]).should == []
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should delete empty groups" do
|
187
|
+
@c.add_host_def([:another, :path], :host, @h2)
|
188
|
+
@c.delete_path([:another, :path, :host])
|
189
|
+
# I should be able to delete the empty path
|
190
|
+
@c.delete_path([:another, :path])
|
191
|
+
b = RunSSHLib::ConfigFile.new(@temp_file)
|
192
|
+
b.list_groups([:another]).should == []
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "when quering for host" do
|
197
|
+
it "should raise error if requested host is path" do
|
198
|
+
initial_data
|
199
|
+
lambda { @c.get_host([:one, :two]) }.
|
200
|
+
should raise_error(RunSSHLib::ConfigError,
|
201
|
+
/is a group, not host definition/)
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should raise error if requested host doesn't exist" do
|
205
|
+
initial_data
|
206
|
+
lambda { @c.get_host([:one, :two, :three, :www2]) }.
|
207
|
+
should raise_error(RunSSHLib::ConfigError, /oesn't exist!/)
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should raise the correct error even if the path is completely invalid" do
|
211
|
+
initial_data
|
212
|
+
lambda { @c.get_host([:dummy, :two, :three, :www]) }.
|
213
|
+
should raise_error(RunSSHLib::ConfigError, /oesn't exist!/)
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should return the correct host when the path is right" do
|
217
|
+
c = RunSSHLib::ConfigFile.new(@temp_file)
|
218
|
+
c.add_host_def([:customer, :dc], :host1, @h1)
|
219
|
+
b = RunSSHLib::ConfigFile.new(@temp_file)
|
220
|
+
b.get_host([:customer, :dc, :host1]).should == @h1
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe "when listing subgroups" do
|
225
|
+
before(:each) do
|
226
|
+
initial_data
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should raise error if path is invalid" do
|
230
|
+
lambda { @c.list_groups([:one, :three, :four]) }.
|
231
|
+
should raise_error(RunSSHLib::ConfigError, /invalid path/i)
|
232
|
+
lambda { @c.list_groups([:two, :three, :four]) }.
|
233
|
+
should raise_error(RunSSHLib::ConfigError, /invalid path/i)
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should return [] if path points to host definition" do
|
237
|
+
@c.list_groups([:one, :two, :three, :www]).should == []
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should return [] for group without subgroups" do
|
241
|
+
@c.add_host_def([:another, :path], :host, @h2)
|
242
|
+
@c.delete_path([:another, :path, :host])
|
243
|
+
@c.list_groups([:another, :path]).should == []
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should return valid subgroups if there are any" do
|
247
|
+
@c.add_host_def([:one, :two, :four], :host, @h2)
|
248
|
+
@c.add_host_def([:one, :two, :three], :host, @h1)
|
249
|
+
@c.list_groups([:one, :two]).should include(:three, :four)
|
250
|
+
@c.list_groups([:one, :two, :three]).should include(:host, :www)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should correctly export and import YAML files" do
|
255
|
+
yml = File.join(File.dirname(__FILE__), '../fixtures', 'runssh.yml')
|
256
|
+
c = RunSSHLib::ConfigFile.new(@temp_file)
|
257
|
+
c.import(yml)
|
258
|
+
c.export(@tmp_yml)
|
259
|
+
require 'yaml'
|
260
|
+
YAML.load_file(@tmp_yml).should == YAML.load_file(yml)
|
261
|
+
end
|
262
|
+
|
263
|
+
after(:each) do
|
264
|
+
if File.exists? @temp_file
|
265
|
+
File.delete(@temp_file)
|
266
|
+
end
|
267
|
+
if File.exists? @temp_file_bak
|
268
|
+
File.delete(@temp_file_bak)
|
269
|
+
end
|
270
|
+
if File.exists? @tmp_yml
|
271
|
+
File.delete(@tmp_yml)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
@@ -0,0 +1,68 @@
|
|
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
|
+
require 'lib/runsshlib'
|
20
|
+
require 'spec_helper'
|
21
|
+
|
22
|
+
describe "SshBackend implementation" do
|
23
|
+
describe "when initializing" do
|
24
|
+
before(:all) do
|
25
|
+
@h = RunSSHLib::HostDef.new('a.example.com', 'me')
|
26
|
+
@o = {:login => 'you'}
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should override user in host definition with `overrides`" do
|
30
|
+
s = RunSSHLib::SshBackend.new(@h, @o)
|
31
|
+
s.instance_variable_get(:@host).should == 'a.example.com'
|
32
|
+
s.instance_variable_get(:@user).should == 'you'
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should accept host definitions if no overrides" do
|
36
|
+
s = RunSSHLib::SshBackend.new(@h, {})
|
37
|
+
s.instance_variable_get(:@host).should == 'a.example.com'
|
38
|
+
s.instance_variable_get(:@user).should == 'me'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "shell" do
|
43
|
+
before(:each) do
|
44
|
+
@hd1 = RunSSHLib::HostDef.new('a.example.com')
|
45
|
+
@hd2 = RunSSHLib::HostDef.new('b.example.com', 'user')
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should handle null user correctly" do
|
49
|
+
ssh = RunSSHLib::SshBackend.new(@hd1, {})
|
50
|
+
ssh.should_receive(:exec).with(/^ssh\s+a.example.com$/).and_return(nil)
|
51
|
+
ssh.shell
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should handle existing user correctly" do
|
55
|
+
ssh = RunSSHLib::SshBackend.new(@hd2, {})
|
56
|
+
ssh.should_receive(:exec).with(/^ssh\s+-l\s+user\s+b.example.com$/).
|
57
|
+
and_return(nil)
|
58
|
+
ssh.shell
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should use the overriding user instead of configured one" do
|
62
|
+
ssh = RunSSHLib::SshBackend.new(@hd2, {:login => 'another', })
|
63
|
+
ssh.should_receive(:exec).with(/^ssh\s+-l\s+another\s+b.example.com$/).
|
64
|
+
and_return(nil)
|
65
|
+
ssh.shell
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|