runssh 0.2.2 → 0.4.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/.gitignore +2 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +22 -8
- data/README.rdoc +39 -9
- data/bin/runssh_comp.sh +1 -1
- data/features/add_bookmarks.feature +58 -0
- data/features/cli_help.feature +32 -0
- data/features/copy_id.feature +26 -0
- data/features/delete_bookmarks.feature +45 -0
- data/features/export_bookmarks.feature +19 -0
- data/features/import_bookmarks.feature +35 -0
- data/features/print_bookmarks.feature +42 -0
- data/features/run_shell.feature +83 -0
- data/features/step_definitions/argument_steps.rb +51 -0
- data/features/step_definitions/bookmarks_steps.rb +109 -0
- data/features/step_definitions/output_steps.rb +77 -0
- data/features/support/env.rb +36 -0
- data/features/update_bookmarks.feature +24 -0
- data/lib/runsshlib/cli.rb +248 -120
- data/lib/runsshlib/ssh_backend.rb +34 -4
- data/lib/runsshlib/ssh_host_def.rb +12 -2
- data/lib/runsshlib/version.rb +3 -3
- data/lib/runsshlib.rb +3 -0
- data/runssh.gemspec +0 -5
- data/spec/runsshlib/cli_spec.rb +181 -319
- data/spec/runsshlib/config_file_spec.rb +3 -5
- data/spec/runsshlib/ssh_backend_spec.rb +48 -3
- data/spec/runsshlib/ssh_host_def_spec.rb +5 -1
- data/spec/spec_helper.rb +4 -65
- data/spec/support/utils.rb +149 -0
- metadata +34 -86
@@ -0,0 +1,77 @@
|
|
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
|
+
Then /^I should get a "([^"]*)" error$/ do |error|
|
20
|
+
expect {
|
21
|
+
capture(:stderr, @input) do
|
22
|
+
cli = RunSSHLib::CLI.new(@args)
|
23
|
+
cli.run
|
24
|
+
end
|
25
|
+
}.to exit_abnormaly
|
26
|
+
@buf.should include(error)
|
27
|
+
end
|
28
|
+
|
29
|
+
Then /^I should be prompted with "([^"]*)"$/ do |output|
|
30
|
+
# I'm only interested in the output verification but I should hide the errors.
|
31
|
+
capture(:stderr) do
|
32
|
+
capture(:stdout, 'n\n') do
|
33
|
+
expect { RunSSHLib::CLI.new(@args).run }.to exit_abnormaly
|
34
|
+
end
|
35
|
+
end
|
36
|
+
@buf.should match(/#{output}/)
|
37
|
+
end
|
38
|
+
|
39
|
+
When /^I confirm the prompt$/ do
|
40
|
+
When %Q(I answer "yes" at the prompt)
|
41
|
+
end
|
42
|
+
|
43
|
+
When /^I answer "([^"]*)" at the prompt$/ do |input|
|
44
|
+
@input = input
|
45
|
+
end
|
46
|
+
|
47
|
+
Then /^It should run successfully$/ do
|
48
|
+
capture(:stdout, @input) do
|
49
|
+
RunSSHLib::CLI.new(@args).run
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Then /^It should exit normally$/ do
|
54
|
+
expect {
|
55
|
+
capture(:stdout, @input) do
|
56
|
+
cli = RunSSHLib::CLI.new(@args).run
|
57
|
+
end
|
58
|
+
}.to exit_normaly
|
59
|
+
end
|
60
|
+
|
61
|
+
Then /^The output should include "([^"]*)"$/ do |output|
|
62
|
+
@buf.should match(/#{output}/)
|
63
|
+
end
|
64
|
+
|
65
|
+
Then /^The output should not include "([^"]*)"$/ do |output|
|
66
|
+
@buf.should_not match(/#{output}/)
|
67
|
+
end
|
68
|
+
|
69
|
+
Then /^It should execute "(.*)"$/ do |command|
|
70
|
+
capture(:stdout) {
|
71
|
+
RunSSHLib::CLI.new(@args).run
|
72
|
+
}.should match(/^#{command}\s*\n$/)
|
73
|
+
end
|
74
|
+
|
75
|
+
Then /^The output file should contain "([^"]*)"$/ do |output|
|
76
|
+
File.read(TMP_YML).should include(output)
|
77
|
+
end
|
@@ -0,0 +1,36 @@
|
|
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 "#{File.expand_path('../../../spec/support/utils', __FILE__)}"
|
20
|
+
$:.unshift(File.join(File.dirname(__FILE__), "..", "..", "lib"))
|
21
|
+
|
22
|
+
require 'simplecov'
|
23
|
+
SimpleCov.start do
|
24
|
+
add_group "Sources", "/lib/"
|
25
|
+
end
|
26
|
+
require 'cucumber/rspec/doubles'
|
27
|
+
|
28
|
+
Before do |scenario|
|
29
|
+
stub_ssh_exec
|
30
|
+
@test_args = %W(-f #{TMP_FILE})
|
31
|
+
@input = ''
|
32
|
+
end
|
33
|
+
|
34
|
+
After do |scenario|
|
35
|
+
cleanup_tmp_file
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: Updating bookmarks
|
2
|
+
In order to modify existing bookmarks
|
3
|
+
I want to run runssh update on existing bookmark. This command should fail
|
4
|
+
if the bookmark doesn't exist yet (to avoid errors).
|
5
|
+
Currently the update command COMPLETELY overwrites the existing bookmark so
|
6
|
+
it's not very useful (to be fixed later).
|
7
|
+
|
8
|
+
Scenario: All available options runs successfully
|
9
|
+
Given Bookmark "one two three" exist with:
|
10
|
+
| name | value |
|
11
|
+
| host-name | some.host |
|
12
|
+
| login | somelogin |
|
13
|
+
When I run the "update" command with "one two three -n some.other.host -l otherlogin -L 8080:localhost:8080"
|
14
|
+
Then It should run successfully
|
15
|
+
And Bookmark "one two three" should contain:
|
16
|
+
| name | value |
|
17
|
+
| host_name | some.other.host |
|
18
|
+
| login | otherlogin |
|
19
|
+
| local_tunnel | 8080:localhost:8080 |
|
20
|
+
|
21
|
+
Scenario: Fails when updating non-existing bookmark
|
22
|
+
Given Empty database
|
23
|
+
When I run the "update" command with "one two -n somehost"
|
24
|
+
Then I should get a "Error: Invalid path!" error
|
data/lib/runsshlib/cli.rb
CHANGED
@@ -20,14 +20,42 @@ require 'trollop'
|
|
20
20
|
module RunSSHLib
|
21
21
|
class CLI
|
22
22
|
|
23
|
-
COMMAND = %w(shell add del update print import export)
|
23
|
+
COMMAND = %w(shell add del update print import export cpid)
|
24
|
+
MAIN_HELP = <<-EOS
|
25
|
+
Usage: runssh [global_options] COMMAND [options] <path>
|
26
|
+
|
27
|
+
A utility to bookmark multiple ssh connections in heirarchial order.
|
28
|
+
For a better understanding of host definitions and bookmarks, Read
|
29
|
+
the provided README.rdoc or go to http://github.com/babysnakes/runssh.
|
30
|
+
|
31
|
+
COMMAND : One of the commands mentioned below. It's possible to
|
32
|
+
type only part of the command as long as it's not ambiguous.
|
33
|
+
<path> : A space-separated list of names (e.g, one two three) that
|
34
|
+
leads to a host definition. For available completions
|
35
|
+
append " ?" to the end of path.
|
36
|
+
|
37
|
+
Available commands:
|
38
|
+
* shell : Open ssh shell on remote host
|
39
|
+
* add : Add host definition
|
40
|
+
* del : Delete host definition
|
41
|
+
* update : Update host definition
|
42
|
+
* print : Print host definition
|
43
|
+
* import : Import configuration
|
44
|
+
* export : Export configuration
|
45
|
+
* cpid : Copy ssh public key to authorized_keys on remote host
|
46
|
+
|
47
|
+
For help on commands run:
|
48
|
+
runssh help COMMAND
|
49
|
+
|
50
|
+
Global options:
|
51
|
+
EOS
|
24
52
|
|
25
53
|
# Initialize new CLI instance and parse the supplied
|
26
54
|
# arguments.
|
27
55
|
def initialize(args)
|
28
56
|
args.unshift '-h' if args.empty?
|
29
|
-
args.unshift '-h' if args == ['help']
|
30
57
|
@global_options = parse_args(args)
|
58
|
+
exit_with_help if args == ['help']
|
31
59
|
return if @global_options[:update_config]
|
32
60
|
|
33
61
|
# workaround to enable 'help COMMAND' functionality.
|
@@ -42,15 +70,9 @@ module RunSSHLib
|
|
42
70
|
rescue ConfigError, InvalidSubCommandError, Errno::ENOENT => e
|
43
71
|
Trollop.die e.message
|
44
72
|
rescue OlderConfigVersionError => e
|
45
|
-
message =
|
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
|
73
|
+
message = construct_update_config_message e.message
|
52
74
|
HighLine.new.say(message)
|
53
|
-
|
75
|
+
exit 1
|
54
76
|
end
|
55
77
|
|
56
78
|
# run
|
@@ -66,6 +88,8 @@ EOM
|
|
66
88
|
end
|
67
89
|
rescue ConfigError => e
|
68
90
|
Trollop.die e.message
|
91
|
+
rescue AbortError => e
|
92
|
+
abort e.message
|
69
93
|
end
|
70
94
|
|
71
95
|
private
|
@@ -74,33 +98,7 @@ EOM
|
|
74
98
|
def parse_args(args)
|
75
99
|
Trollop::options(args) do
|
76
100
|
# TODO: This should be generated automatically somehow!!
|
77
|
-
banner
|
78
|
-
Usage: runssh [global_options] COMMAND [options] <path>
|
79
|
-
|
80
|
-
A utility to bookmark multiple ssh connections in heirarchial order.
|
81
|
-
For a better understanding of host definitions and bookmarks, Read
|
82
|
-
the provided README.rdoc or go to http://github.com/babysnakes/runssh.
|
83
|
-
|
84
|
-
COMMAND : One of the commands mentioned below. It's possible to
|
85
|
-
type only part of the command as long as it's not ambiguous.
|
86
|
-
<path> : A space-separated list of names (e.g, one two three) that
|
87
|
-
leads to a host definition. For available completions
|
88
|
-
append " ?" to the end of path.
|
89
|
-
|
90
|
-
Available commands:
|
91
|
-
* shell : Open ssh shell on remote host
|
92
|
-
* add : Add host definition
|
93
|
-
* del : Delete host definition
|
94
|
-
* update : Update host definition
|
95
|
-
* print : Print host definition
|
96
|
-
* import : Import configuration
|
97
|
-
* export : Export configuration
|
98
|
-
|
99
|
-
For help on commands run:
|
100
|
-
runssh help COMMAND
|
101
|
-
|
102
|
-
Global options:
|
103
|
-
EOS
|
101
|
+
banner MAIN_HELP
|
104
102
|
opt :config_file, "alternate config file",
|
105
103
|
:type => :string, :short => :f
|
106
104
|
opt :update_config, "update configuration from previous version." +
|
@@ -111,8 +109,8 @@ EOS
|
|
111
109
|
end
|
112
110
|
end
|
113
111
|
|
114
|
-
#
|
115
|
-
# invalid or
|
112
|
+
# Extracts the subcommand from args. Throws InvalidSubCommandError if
|
113
|
+
# invalid or ambiguous subcommand
|
116
114
|
def extract_subcommand(args)
|
117
115
|
cmd = args.shift
|
118
116
|
if COMMAND.include? cmd
|
@@ -127,14 +125,34 @@ EOS
|
|
127
125
|
raise InvalidSubCommandError, 'invalid command'
|
128
126
|
end
|
129
127
|
|
130
|
-
#
|
131
|
-
# any logic, nor does it handle errors. It just parses the
|
132
|
-
# arguments and put the result into @options.
|
128
|
+
# route argument parsing for all subcommand.
|
133
129
|
def parse_subcommand(cmd, args)
|
134
130
|
case cmd
|
135
131
|
when 'shell'
|
136
|
-
|
137
|
-
|
132
|
+
parse_shell args
|
133
|
+
when 'add', 'update'
|
134
|
+
parse_add_update cmd, args
|
135
|
+
when 'del'
|
136
|
+
parse_del args
|
137
|
+
when 'print'
|
138
|
+
parse_print args
|
139
|
+
when 'import'
|
140
|
+
parse_import args
|
141
|
+
when 'export'
|
142
|
+
parse_export args
|
143
|
+
when 'cpid'
|
144
|
+
parse_cpid args
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def init_config
|
149
|
+
config = @global_options[:config_file] || DEFAULT_CONFIG
|
150
|
+
ConfigFile.new(config)
|
151
|
+
end
|
152
|
+
|
153
|
+
def parse_shell(args)
|
154
|
+
options = Trollop::options(args) do
|
155
|
+
banner <<-EOS
|
138
156
|
Usage: runssh [global_options] shell [options] <path> [-- <remote command>]
|
139
157
|
|
140
158
|
Connect to the specified host using ssh.
|
@@ -145,68 +163,128 @@ If you only want to run remote command instead of full shell, you can
|
|
145
163
|
append "-- <remote command>" to the regular command. To list /tmp on a host
|
146
164
|
bookmarked as "some host" run:
|
147
165
|
runssh shell some host -- ls -l /tmp
|
166
|
+
Remote command enables pseudo terminal (ssh -t) by default. To disable
|
167
|
+
use -T.
|
148
168
|
|
149
169
|
(Local) tunneling can be enabled with the -L options (correspond to
|
150
170
|
ssh -L option). An abbreviated syntax could be used as the requested
|
151
171
|
port if both ports are identical and host is localhost.
|
152
172
|
e.g. -L 7070 is converted to -L 7070:localhost:7070
|
153
173
|
|
174
|
+
In case of conflicting host key (e.g, when reinstalling a server), ssh refuses
|
175
|
+
to connect and tells you which line has the conflicting host key. ONLY if you
|
176
|
+
know for sure why you have a conflicting key, you can add the
|
177
|
+
--insecure-host-key option with the conflicting line as an argument. DON'T
|
178
|
+
DO THAT UNLESS YOU KNOW WHY THE KEY HAS CHANGED!
|
179
|
+
|
154
180
|
Options:
|
155
181
|
EOS
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
182
|
+
opt :login, "Override the login in the configuration.",
|
183
|
+
:type => :string
|
184
|
+
opt :host_name, 'Override the name or address of the host.',
|
185
|
+
:short => :n, :type => :string
|
186
|
+
opt :local_tunnel, "Tunnel definition (see description above).",
|
187
|
+
:short => :L, :type => :string
|
188
|
+
opt :no_pseudo_terminal, 'Disable pseudo terminal ' \
|
189
|
+
'(effective only with remote command).', :short => :T
|
190
|
+
opt :insecure_host_key, 'delete the specified line form known hosts ' \
|
191
|
+
'file. EXPERIMENTAL and DANGEROUS!.', :type => :int, :short => :I
|
192
|
+
opt :option, 'Ssh option. Appended to saved ssh options. ' \
|
193
|
+
'Can be used multiple times.',
|
194
|
+
:short => :o, :type => :string, :multi => true
|
195
|
+
stop_on "--"
|
196
|
+
end
|
197
|
+
# handle the case of remote command (indicated by --)
|
198
|
+
if ind = args.index("--")
|
199
|
+
rmt = args.slice!(ind, args.size - ind)
|
200
|
+
rmt.delete_at(0) # remove --
|
201
|
+
options[:remote_cmd] = rmt.join(" ")
|
202
|
+
end
|
203
|
+
options
|
204
|
+
end
|
205
|
+
|
206
|
+
def parse_cpid(args)
|
207
|
+
Trollop::options(args) do
|
208
|
+
banner <<-EOH
|
209
|
+
Usage: runssh [global_options] cpid [options] <path>
|
210
|
+
|
211
|
+
Copy ssh public key to authorized_keys on remote host. If no id file is
|
212
|
+
specified, It copies all the keys in your ssh-agent.
|
213
|
+
|
214
|
+
Requires the `ssh-copy-id` command to be in your path.
|
215
|
+
See manpage for ssh-copy-id for more details.
|
216
|
+
|
217
|
+
<path> : See main help for description of path.
|
218
|
+
|
219
|
+
Options:
|
220
|
+
EOH
|
221
|
+
opt :identity_file, "Full path to identity file.",
|
222
|
+
:short => :i, :type => :string
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def parse_add_update(cmd, args)
|
227
|
+
case cmd
|
228
|
+
when "add"
|
229
|
+
help = <<-EOH
|
174
230
|
Usage: runssh [global_options] add [options] <path>
|
175
231
|
|
176
232
|
Add a new host definition at the supplied <path>. <path> must not exist!
|
177
|
-
A host definition
|
178
|
-
|
233
|
+
A host definition must have a hostname. All other options (see below)
|
234
|
+
are optional.
|
179
235
|
|
180
236
|
<path> : See main help for description of path.
|
181
237
|
|
238
|
+
(Local) tunneling can be added with the -L options (correspond to
|
239
|
+
ssh -L option). An abbreviated syntax could be used as the requested
|
240
|
+
port if both ports are identical and host is localhost.
|
241
|
+
e.g. -L 7070 is converted to -L 7070:localhost:7070
|
242
|
+
|
182
243
|
Options:
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
opt :login, 'The user to connect as (optional)',
|
187
|
-
:type => :string
|
188
|
-
end
|
189
|
-
when 'update'
|
190
|
-
Trollop::options(args) do
|
191
|
-
banner <<-EOS
|
244
|
+
EOH
|
245
|
+
when "update"
|
246
|
+
help = <<-EOH
|
192
247
|
Usage: runssh [global_options] update [options] <path>
|
193
248
|
|
194
249
|
Update host definition specified by <path> with new settings. The host
|
195
250
|
definition is completely replaced by the new definition (e.g, You can
|
196
|
-
not specify only new host and expect the
|
251
|
+
not specify only new host and expect the login to remain the existing one).
|
197
252
|
|
198
253
|
<path> : See main help for description of path.
|
199
254
|
|
255
|
+
(Local) tunneling can be added with the -L options (correspond to
|
256
|
+
ssh -L option). An abbreviated syntax could be used as the requested
|
257
|
+
port if both ports are identical and host is localhost.
|
258
|
+
e.g. -L 7070 is converted to -L 7070:localhost:7070
|
259
|
+
|
200
260
|
Options:
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
261
|
+
EOH
|
262
|
+
end
|
263
|
+
options = Trollop::options(args) do
|
264
|
+
banner help
|
265
|
+
opt :host_name, 'The name or address of the host (e.g, host.example.com).',
|
266
|
+
:short => :n, :type => :string, :required => true
|
267
|
+
opt :login, 'The login to connect as.',
|
268
|
+
:type => :string
|
269
|
+
opt :local_tunnel, "Tunnel definition (see description above).",
|
270
|
+
:short => :L, :type => :string
|
271
|
+
opt :option, 'Ssh option (corresponds to ssh -o <option>). ' \
|
272
|
+
'Can be used multiple times.',
|
273
|
+
:short => :o, :multi => true, :type => :string
|
274
|
+
opt :no_host_key_checking, "DANGEROUS! Don't verify host key when " \
|
275
|
+
"connecting to this host. Shortcut for '-o UserKnownHostsFile=" \
|
276
|
+
"/dev/null -o StrictHostKeyChecking=no'",
|
277
|
+
:short => :N, :type => :boolean
|
278
|
+
end
|
279
|
+
if options[:no_host_key_checking_given]
|
280
|
+
options[:option] << 'UserKnownHostsFile=/dev/null' << 'StrictHostKeyChecking=no'
|
281
|
+
end
|
282
|
+
options
|
283
|
+
end
|
284
|
+
|
285
|
+
def parse_del(args)
|
286
|
+
Trollop::options(args) do
|
287
|
+
banner <<-EOS
|
210
288
|
Usage: runssh [global_options] del [options] <path>
|
211
289
|
|
212
290
|
Delete host definitions or `empty` groups (e.g, groups that contained
|
@@ -217,11 +295,13 @@ verification.
|
|
217
295
|
|
218
296
|
Options:
|
219
297
|
EOS
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
298
|
+
opt :yes, 'Delete without verification.'
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def parse_print(args)
|
303
|
+
Trollop::options(args) do
|
304
|
+
banner <<-EOS
|
225
305
|
Usage: runssh [global_options] print [options] <path>
|
226
306
|
|
227
307
|
Print host configuration to the console.
|
@@ -230,52 +310,63 @@ Print host configuration to the console.
|
|
230
310
|
|
231
311
|
Options:
|
232
312
|
EOS
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def parse_import(args)
|
317
|
+
Trollop::options(args) do
|
318
|
+
banner <<-EOS
|
237
319
|
Usage: runssh [global_options] import [options]
|
238
320
|
|
239
|
-
Imports a
|
321
|
+
Imports a configuration (The configuration must be in YAML format).
|
240
322
|
CAREFULL: This completely overrides the current configuration!
|
241
323
|
|
242
324
|
Options:
|
243
325
|
EOS
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
326
|
+
opt :input_file, 'The yaml file to import from.',
|
327
|
+
:type => :string, :required => true
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def parse_export(args)
|
332
|
+
Trollop::options(args) do
|
333
|
+
banner <<-EOS
|
250
334
|
Usage runssh [global_options] export [options]
|
251
335
|
|
252
336
|
Exports the configuration to a YAML file.
|
253
337
|
|
254
338
|
Options
|
255
339
|
EOS
|
256
|
-
|
257
|
-
|
258
|
-
end
|
340
|
+
opt :output_file, 'The output file.',
|
341
|
+
:type => :string, :required => true
|
259
342
|
end
|
260
343
|
end
|
261
344
|
|
262
|
-
def init_config
|
263
|
-
config = @global_options[:config_file] || DEFAULT_CONFIG
|
264
|
-
ConfigFile.new(config)
|
265
|
-
end
|
266
|
-
|
267
345
|
def run_shell(path)
|
346
|
+
verify_and_delete_conflicting_host_key(@options[:insecure_host_key]) if
|
347
|
+
@options[:insecure_host_key_given]
|
348
|
+
|
268
349
|
host = @c.get_host(path)
|
269
350
|
# only override if value exist
|
270
351
|
# TODO: this works only for some types (e.g, not boolean) but
|
271
352
|
# currently this is all we need. We may need to make it better
|
272
353
|
# later.
|
273
354
|
definition = host.definition.merge(@options) do |key, this, other|
|
274
|
-
|
355
|
+
case key
|
356
|
+
when :option
|
357
|
+
this + other
|
358
|
+
else
|
359
|
+
other ? other : this
|
360
|
+
end
|
275
361
|
end
|
276
362
|
SshBackend.shell(definition)
|
277
363
|
end
|
278
364
|
|
365
|
+
def run_cpid(path)
|
366
|
+
host = @c.get_host(path)
|
367
|
+
SshBackend.copy_id(host.definition.merge(@options))
|
368
|
+
end
|
369
|
+
|
279
370
|
def run_add(path)
|
280
371
|
# extract the host definition name
|
281
372
|
host = path.pop
|
@@ -288,13 +379,12 @@ EOS
|
|
288
379
|
end
|
289
380
|
|
290
381
|
def run_del(path)
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
@
|
295
|
-
else
|
296
|
-
puts 'canceled'
|
382
|
+
unless @options[:yes]
|
383
|
+
question = %Q(Are you sure you want to delete "#{path.join(':')}") +
|
384
|
+
"? [yes/no] "
|
385
|
+
@options[:yes] = agree_or_abort(question, "Cancelled")
|
297
386
|
end
|
387
|
+
@c.delete_path(path)
|
298
388
|
end
|
299
389
|
|
300
390
|
def run_print(path)
|
@@ -307,11 +397,8 @@ EOS
|
|
307
397
|
def run_import(path)
|
308
398
|
question = "Importing a file OVERWRITES existing configuration. " +
|
309
399
|
"Are you sure? [yes/no] "
|
310
|
-
|
311
|
-
|
312
|
-
else
|
313
|
-
puts 'canceled'
|
314
|
-
end
|
400
|
+
agree_or_abort(question, 'Cancelled')
|
401
|
+
@c.import(@options[:input_file])
|
315
402
|
end
|
316
403
|
|
317
404
|
# we don't use path here, it's just for easier invocation
|
@@ -321,7 +408,7 @@ EOS
|
|
321
408
|
|
322
409
|
# extract keys relevant for definition of SshHostDef
|
323
410
|
def extract_definition options
|
324
|
-
valid_definition = [:host_name, :login]
|
411
|
+
valid_definition = [:host_name, :login, :local_tunnel, :option]
|
325
412
|
options.reject do |key, value|
|
326
413
|
! valid_definition.include?(key)
|
327
414
|
end
|
@@ -344,5 +431,46 @@ EOM
|
|
344
431
|
HighLine.new.say(message)
|
345
432
|
end
|
346
433
|
end
|
434
|
+
|
435
|
+
# help needed out of trollop::parse loop
|
436
|
+
def exit_with_help
|
437
|
+
p = Trollop::Parser.new do
|
438
|
+
banner MAIN_HELP
|
439
|
+
end
|
440
|
+
Trollop::with_standard_exception_handling p do
|
441
|
+
raise Trollop::HelpNeeded
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
def verify_and_delete_conflicting_host_key(line_number)
|
446
|
+
khu = RunSSHLib::SshBackend::KnownHostsUtils.new
|
447
|
+
host = IO.readlines(khu.known_hosts_file)[line_number - 1].split[0]
|
448
|
+
question = "Are you sure you want to delete the key for host: " \
|
449
|
+
"'<%= color(\"#{host}\", :red) %>'? " \
|
450
|
+
"Conflicting key could indicate compromised host! [yes/no] "
|
451
|
+
agree_or_abort(question, "Cancelled")
|
452
|
+
khu.delete_line_from_known_hosts_file line_number
|
453
|
+
end
|
454
|
+
|
455
|
+
# Prompts you with the supplied question and aborts with the supplied
|
456
|
+
# error unless confirmed the prompt.
|
457
|
+
def agree_or_abort(question, error_message)
|
458
|
+
raise(AbortError, error_message) unless HighLine.new.agree(question)
|
459
|
+
end
|
460
|
+
|
461
|
+
# Construct update_config message with correct config file.
|
462
|
+
# The current_version is the number of the existing config version.
|
463
|
+
def construct_update_config_message(current_version)
|
464
|
+
config_string = @global_options[:config_file] ?
|
465
|
+
"-f #{@global_options[:config_file]}" : ''
|
466
|
+
message = <<-EOM
|
467
|
+
You seem to use older configuration version. Did you upgrade runssh?
|
468
|
+
If so, please run <%= color("runssh #{config_string} --update-config", :blue) %>
|
469
|
+
in order to update your configuration to the current version.
|
470
|
+
|
471
|
+
Your old configuration will be saved with the suffix \
|
472
|
+
<%= color(".#{current_version}", :underline) %>
|
473
|
+
EOM
|
474
|
+
end
|
347
475
|
end
|
348
476
|
end
|