shexy 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +9 -0
- data/README.md +38 -0
- data/lib/shexy.rb +112 -22
- data/spec/spec_helper.rb +2 -0
- metadata +5 -5
- data/test/helper.rb +0 -18
- data/test/test_shexy.rb +0 -7
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -26,10 +26,48 @@ and added them to the SSH agent (ssh-add ~/.ssh/my-priv-key):
|
|
26
26
|
end
|
27
27
|
Shexy.exe 'echo hello' # no need to add host/user again
|
28
28
|
|
29
|
+
|
30
|
+
Specify key to use:
|
31
|
+
|
32
|
+
Shexy.key = '~/.ssh/id_rsa'
|
33
|
+
out, err, exit_code = Shexy.exe 'test@test-host', 'ls -la'
|
34
|
+
|
29
35
|
Copying files (local -> remote):
|
30
36
|
|
31
37
|
Shexy.copy_to 'test@test-host', '/home/rubiojr/my-uber-file', '/tmp/'
|
32
38
|
|
39
|
+
Batch mode:
|
40
|
+
|
41
|
+
Shexy.batch do
|
42
|
+
script <<-EOH
|
43
|
+
echo > /tmp/foo
|
44
|
+
ls -la
|
45
|
+
sed -i s/foo/bar/ /etc/foo
|
46
|
+
EOH
|
47
|
+
end
|
48
|
+
|
49
|
+
Use sudo:
|
50
|
+
|
51
|
+
Shexy.use_sudo
|
52
|
+
# sudo will be used to run the commands
|
53
|
+
Shexy.exe 'test@foobar.com', 'echo | cat' # => sudo echo | sudo cat
|
54
|
+
|
55
|
+
Query the server OS:
|
56
|
+
|
57
|
+
Shexy.distro # => :ubuntu, :redhat, :centos, :fedora, :debian
|
58
|
+
Shexy.distro_release # => 12.04, 5.8, etc.
|
59
|
+
|
60
|
+
More helpers:
|
61
|
+
|
62
|
+
Shexy.user = 'rubiojr'
|
63
|
+
Shexy.password = 'secret'
|
64
|
+
Shexy.host = 'foo.com'
|
65
|
+
# Copy the key to /home/rubiojr/.ssh/authorized_keys in remote server
|
66
|
+
Shexy.copy_ssh_pubkey '~/.ssh/id_rsa.pub'
|
67
|
+
# Set PermitRootLogin to without-password in /etc/ssh/sshd_config
|
68
|
+
# requires sudo in remote server (unless using root user).
|
69
|
+
Shexy.permit_root_login 'without-password'
|
70
|
+
|
33
71
|
## Caution
|
34
72
|
|
35
73
|
I was bored writing net-ssh boilerplate, so I created this highly
|
data/lib/shexy.rb
CHANGED
@@ -30,40 +30,76 @@ require 'net/scp'
|
|
30
30
|
#
|
31
31
|
# Shexy.copy_to 'test@test-host', '/home/rubiojr/my-uber-file', '/tmp/'
|
32
32
|
#
|
33
|
+
|
33
34
|
module Shexy
|
35
|
+
|
36
|
+
VERSION = '0.3'
|
34
37
|
|
35
|
-
|
38
|
+
[:user, :password, :key, :cmd, :host].each do |n|
|
39
|
+
instance_eval %{
|
40
|
+
def #{n}; Thread.current[:shexy_#{n}]; end
|
41
|
+
def #{n}=(v); Thread.current[:shexy_#{n}] = v; end
|
42
|
+
}
|
43
|
+
end
|
36
44
|
|
37
|
-
|
45
|
+
def self.flags=(f);Thread.current[:shexy_flags] = f;end
|
46
|
+
def self.flags; Thread.current[:shexy_flags] ||= {} ; end
|
47
|
+
def self.sudo?;Thread.current[:shexy_use_sudo]; end
|
48
|
+
def self.use_sudo(v = true); Thread.current[:shexy_use_sudo] = v; end
|
38
49
|
|
39
|
-
def self.
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
50
|
+
def self.wait_for_ssh(timeout = 60)
|
51
|
+
Timeout.timeout(timeout) do
|
52
|
+
begin
|
53
|
+
sleep(1) until tcp_test_ssh(host) do
|
54
|
+
end
|
55
|
+
rescue Errno::ECONNRESET
|
56
|
+
# safe to ignore, we need to retry all the time.
|
57
|
+
end
|
58
|
+
end
|
59
|
+
true
|
60
|
+
rescue Exception => e
|
61
|
+
$stderr.puts e.message
|
62
|
+
false
|
63
|
+
end
|
45
64
|
|
46
|
-
|
47
|
-
|
48
|
-
|
65
|
+
def self.tcp_test_ssh
|
66
|
+
tcp_socket = TCPSocket.new(host, 22)
|
67
|
+
readable = IO.select([tcp_socket], nil, nil, 5)
|
68
|
+
if readable
|
69
|
+
yield
|
70
|
+
true
|
71
|
+
else
|
72
|
+
false
|
73
|
+
end
|
74
|
+
rescue Errno::ETIMEDOUT, Errno::EPERM
|
75
|
+
false
|
76
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH
|
77
|
+
sleep 2
|
78
|
+
false
|
79
|
+
ensure
|
80
|
+
tcp_socket && tcp_socket.close
|
49
81
|
end
|
50
82
|
|
51
83
|
def self.exe(*args)
|
52
84
|
args.flatten!
|
53
85
|
if args.size > 1
|
54
|
-
|
55
|
-
if
|
56
|
-
|
57
|
-
end
|
58
|
-
@cmd = args[1]
|
86
|
+
self.host = args[0]
|
87
|
+
self.user, self.host = self.host.split '@' if self.host =~ /@/
|
88
|
+
self.cmd = args[1]
|
59
89
|
else
|
60
|
-
|
90
|
+
self.cmd = args[0]
|
61
91
|
end
|
62
|
-
|
92
|
+
self.flags[:password] = self.password if self.password
|
93
|
+
self.flags[:keys] = [self.key] if self.key
|
94
|
+
Net::SSH.start(self.host, self.user, self.flags) do |sh|
|
63
95
|
sh.open_channel do |ch|
|
64
96
|
if sudo?
|
65
97
|
ch.request_pty
|
66
|
-
|
98
|
+
if cmd =~ /(&&|\|\||&|\|)/
|
99
|
+
self.cmd = cmd.gsub(/(&&|\|\||&|\|)/, $1 + 'sudo')
|
100
|
+
end
|
101
|
+
self.cmd = "sudo #{cmd}"
|
102
|
+
puts "SHEXY: #{cmd}" if $DEBUG
|
67
103
|
end
|
68
104
|
ch.exec cmd do
|
69
105
|
# FIXME: I don't think it's a good idea
|
@@ -95,6 +131,25 @@ module Shexy
|
|
95
131
|
end
|
96
132
|
end
|
97
133
|
|
134
|
+
def self.batch(&block)
|
135
|
+
require 'tempfile'
|
136
|
+
out, err = nil,nil
|
137
|
+
def self.script(script)
|
138
|
+
f = Tempfile.new 'shexy'
|
139
|
+
begin
|
140
|
+
f.puts script
|
141
|
+
f.flush
|
142
|
+
copy_to f.path, "#{f.path}.remote"
|
143
|
+
out, err = exe "/bin/bash #{f.path}.remote"
|
144
|
+
ensure
|
145
|
+
f.close
|
146
|
+
f.unlink
|
147
|
+
end
|
148
|
+
return out, err
|
149
|
+
end
|
150
|
+
instance_eval &block
|
151
|
+
end
|
152
|
+
|
98
153
|
#
|
99
154
|
# Shexy.copy_to 'root@foobar.com', 'source_file', 'dest_file'
|
100
155
|
#
|
@@ -113,9 +168,9 @@ module Shexy
|
|
113
168
|
|
114
169
|
if args.size > 2
|
115
170
|
# First arg assumed to be foo@host.net
|
116
|
-
|
117
|
-
if
|
118
|
-
|
171
|
+
self.host = args[0]
|
172
|
+
if self.host =~ /@/
|
173
|
+
self.user, self.host = self.host.split '@'
|
119
174
|
end
|
120
175
|
from = args[1]
|
121
176
|
to = args[2]
|
@@ -125,6 +180,7 @@ module Shexy
|
|
125
180
|
from = args[0]
|
126
181
|
to = args[1]
|
127
182
|
end
|
183
|
+
self.flags[:password] = self.password if self.password
|
128
184
|
from = File.expand_path from
|
129
185
|
Net::SCP.start(host, user, flags) do |scp|
|
130
186
|
scp.upload! from, to, opts
|
@@ -165,4 +221,38 @@ module Shexy
|
|
165
221
|
issue
|
166
222
|
end
|
167
223
|
|
224
|
+
def self.copy_ssh_pubkey(path, dest_dir = '~/.ssh')
|
225
|
+
path = File.expand_path path
|
226
|
+
raise ArgumentError.new("Invalid key file") unless File.exist?(path)
|
227
|
+
key = File.read path
|
228
|
+
batch do
|
229
|
+
script <<-EOH
|
230
|
+
mkdir -p #{dest_dir} && chmod 700 #{dest_dir}
|
231
|
+
echo '#{key}' >> #{dest_dir}/authorized_keys
|
232
|
+
EOH
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
#
|
237
|
+
# Set PermitRootLogin to value in /etc/ssh/sshd_config
|
238
|
+
# value accepted: yes,now, without-password
|
239
|
+
#
|
240
|
+
def self.permit_root_login(value)
|
241
|
+
value = value.to_s
|
242
|
+
unless value =~ /^(yes|no|without-password)$/
|
243
|
+
raise ArgumentError.new "Argument should be yes|no|without-password"
|
244
|
+
end
|
245
|
+
using_sudo = Shexy.sudo?
|
246
|
+
Shexy.use_sudo
|
247
|
+
out, err = batch do
|
248
|
+
script <<-EOH
|
249
|
+
sed -i 's/^#\?PermitRootLogin.*$/PermitRootLogin\ #{value}/' /etc/ssh/sshd_config
|
250
|
+
test -f /etc/init.d/ssh && /etc/init.d/ssh restart
|
251
|
+
test -f /etc/init.d/sshd && /etc/init.d/sshd restart
|
252
|
+
EOH
|
253
|
+
end
|
254
|
+
Shexy.use_sudo(false) unless using_sudo
|
255
|
+
return out, err
|
256
|
+
end
|
257
|
+
|
168
258
|
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shexy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.3'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: net-ssh
|
@@ -132,13 +132,13 @@ extra_rdoc_files:
|
|
132
132
|
- README.md
|
133
133
|
files:
|
134
134
|
- .document
|
135
|
+
- CHANGELOG.md
|
135
136
|
- Gemfile
|
136
137
|
- LICENSE.txt
|
137
138
|
- README.md
|
138
139
|
- Rakefile
|
139
140
|
- lib/shexy.rb
|
140
|
-
-
|
141
|
-
- test/test_shexy.rb
|
141
|
+
- spec/spec_helper.rb
|
142
142
|
homepage: http://github.com/rubiojr/shexy
|
143
143
|
licenses:
|
144
144
|
- MIT
|
@@ -154,7 +154,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
154
154
|
version: '0'
|
155
155
|
segments:
|
156
156
|
- 0
|
157
|
-
hash:
|
157
|
+
hash: 130145655460833176
|
158
158
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
159
|
none: false
|
160
160
|
requirements:
|
data/test/helper.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler'
|
3
|
-
begin
|
4
|
-
Bundler.setup(:default, :development)
|
5
|
-
rescue Bundler::BundlerError => e
|
6
|
-
$stderr.puts e.message
|
7
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
-
exit e.status_code
|
9
|
-
end
|
10
|
-
require 'test/unit'
|
11
|
-
require 'shoulda'
|
12
|
-
|
13
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
-
require 'shexy'
|
16
|
-
|
17
|
-
class Test::Unit::TestCase
|
18
|
-
end
|