shexy 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ # 0.3
2
+
3
+ * Bug fixes
4
+ * Shexy should be thread safe now
5
+ * Added some more methods (see README)
6
+ - Shexy.permit_root_login
7
+ - Shexy.copy_ssh_pubkey
8
+ - Shexy.batch
9
+
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
@@ -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
- VERSION = '0.2'
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
- @flags = {}
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.password=(password); flags[:password] = password; end
40
- def self.password; flags[:password]; end
41
- def self.key=(key); flags[:keys] = [File.expand_path(key)]; end
42
- def self.key; flags[:keys]; end
43
- def self.use_sudo(v=true); @sudo = v; end
44
- def self.sudo?; @sudo ||= false; end
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
- class << self
47
- attr_accessor :host, :user, :flags
48
- attr_reader :cmd
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
- @host = args[0]
55
- if @host =~ /@/
56
- @user, @host = @host.split '@'
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
- @cmd = args[0]
90
+ self.cmd = args[0]
61
91
  end
62
- Net::SSH.start(host, user, flags) do |sh|
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
- @cmd = "sudo #{@cmd}"
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
- @host = args[0]
117
- if @host =~ /@/
118
- @user, @host = @host.split '@'
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
@@ -0,0 +1,2 @@
1
+ $: << File.join(File.dirname(__FILE__), '../lib')
2
+ require 'shexy'
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.2'
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-09-17 00:00:00.000000000 Z
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
- - test/helper.rb
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: 2098135952916495063
157
+ hash: 130145655460833176
158
158
  required_rubygems_version: !ruby/object:Gem::Requirement
159
159
  none: false
160
160
  requirements:
@@ -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
@@ -1,7 +0,0 @@
1
- require 'helper'
2
-
3
- class TestShexy < Test::Unit::TestCase
4
- should "probably rename this file and start testing for real" do
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
6
- end
7
- end