rye 0.7.6 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.txt CHANGED
@@ -7,6 +7,18 @@ TODO
7
7
  * Add S3 support for Rye::Box.upload / download
8
8
 
9
9
 
10
+ #### 0.8.0 (2009-06-21) #############################
11
+
12
+ * FIXED: safely and unsafely (Rye::Box) now return the block return value
13
+ * ADDED: Rye::Set#parallel and Rye::Set#parallel= methods
14
+ * ADDED: Rye::Box#quietly
15
+ * CHANGE: Updated Rye::Set#inspect and Rye::Set#to_s
16
+ * CHANGE: Exception hook now receives: ex, cmd, user, host, nickname
17
+ * CHANGE: Increased Kernel.sleep to 0.03 in Rye::Set.run_command_parallel
18
+ * CHANGE: Renamed Rye::Cmd#upload and Rye::Cmd#download to file_upload and file_download
19
+ * CHANGE: Rye::Cmd#file_exists? now executes quietly (doesn't pollute logging)
20
+
21
+
10
22
  #### 0.7.6 (2009-06-19) #############################
11
23
 
12
24
  * FIXED: Raise Rye::NoPty exception when Net::SSH returns message
data/README.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- = Rye - v0.7
1
+ = Rye - v0.8
2
2
 
3
3
  Safely run SSH commands on a bunch of machines at the same time (from Ruby).
4
4
 
@@ -42,7 +42,7 @@ You can specify environment variables
42
42
  rbox.env # => ['HOME=/home/rye', 'RYE=Forty Creek', ...]
43
43
 
44
44
 
45
- == EXAMPLE 3 -- Accessing Multiple Machines
45
+ == EXAMPLE 3a -- Accessing Multiple Machines
46
46
 
47
47
  rset = Rye::Set.new
48
48
  rbox = Rye::Box.new
@@ -54,25 +54,33 @@ Calling methods on Rye::Set objects is very similar to calling them on Rye::Box
54
54
  p rset.uptime # => [[14:19:02 up 32 days, 19:35 ...], [14:19:02 up 30 days, 01:35]]
55
55
  p rset['/etc'].ls # => [['file1', 'file2', ...], ['life1', 'life2', ...]]
56
56
 
57
-
58
- == EXAMPLE 4 -- FILE TRANSFERS
57
+ == EXAMPLE 3b -- Accessing Multiple Machines -- In Parallel
58
+
59
+ By default, Rye:Set connects to each machine sequentially in the order they were added to the set. Commands can also be run in parallel:
60
+
61
+ rset = Rye::Set.new('set-name', :parallel => true)
62
+ OR
63
+ rset.parallel = true
64
+
65
+
66
+ == EXAMPLE 4 -- File Transfers
59
67
 
60
68
  rbox = Rye::Box.new("localhost", :info => true)
61
69
 
62
70
  dir_upload = "#{Rye.sysinfo.tmpdir}/rye-upload/"
63
71
  dir_download = "#{Rye.sysinfo.tmpdir}/rye-download/"
64
72
 
65
- rbox.upload("#{RYE_HOME}/README.rdoc",
73
+ rbox.file_upload("#{RYE_HOME}/README.rdoc",
66
74
  "#{RYE_HOME}/LICENSE.txt", dir_upload)
67
75
 
68
76
  applejack = StringIO.new("Some in-memory content")
69
- rbox.upload(applejack, "#{dir_upload}/applejack.txt")
77
+ rbox.file_upload(applejack, "#{dir_upload}/applejack.txt")
70
78
 
71
79
  p rbox.ls(dir_upload) # => [README.rdoc, LICENSE.txt, applejack.txt]
72
80
  p rbox.cat("#{dir_upload}/applejack.txt") # => "Some in-memory content"
73
81
 
74
82
  filecontent = StringIO.new
75
- rbox.download("#{dir_upload}/applejack.txt", filecontent)
83
+ rbox.file_download("#{dir_upload}/applejack.txt", filecontent)
76
84
 
77
85
  p filecontent.read
78
86
 
data/lib/rye/box.rb CHANGED
@@ -34,20 +34,24 @@ module Rye
34
34
  def safe; @rye_safe; end
35
35
  def user; (@rye_opts || {})[:user]; end
36
36
 
37
- # A storage area +@rye_stash+
37
+ # Returns the current value of the stash +@rye_stash+
38
38
  def stash; @rye_stash; end
39
+ def quiet; @rye_quiet; end
39
40
  def nickname; @rye_nickname || host; end
40
-
41
+
41
42
  def host=(val); @rye_host = val; end
42
43
  def opts=(val); @rye_opts = val; end
43
44
 
44
- # Returns the storage area +@rye_stash+
45
+ # Store a value to the stash +@rye_stash+
45
46
  def stash=(val); @rye_stash = val; end
46
47
  def nickname=(val); @rye_nickname = val; end
47
48
 
48
49
  def enable_safe_mode; @rye_safe = true; end
49
50
  def disable_safe_mode; @rye_safe = false; end
50
51
 
52
+ def enable_quiet_mode; @rye_quiet = true; end
53
+ def disable_quiet_mode; @rye_quiet = false; end
54
+
51
55
  # The most recent value from Box.cd or Box.[]
52
56
  def current_working_directory; @rye_current_working_directory; end
53
57
 
@@ -94,6 +98,7 @@ module Rye
94
98
  :debug => nil,
95
99
  :error => STDERR,
96
100
  :getenv => true,
101
+ :quiet => false
97
102
  }.merge(opts)
98
103
 
99
104
  # Close the SSH session before Ruby exits. This will do nothing
@@ -105,6 +110,7 @@ module Rye
105
110
  @rye_safe, @rye_debug = @rye_opts.delete(:safe), @rye_opts.delete(:debug)
106
111
  @rye_info, @rye_error = @rye_opts.delete(:info), @rye_opts.delete(:error)
107
112
  @rye_getenv = {} if @rye_opts.delete(:getenv) # Enable getenv with a hash
113
+ @rye_quiet = @rye_opts.delete(:quiet)
108
114
 
109
115
  # Just in case someone sends a true value rather than IO object
110
116
  @rye_debug = STDERR if @rye_debug == true
@@ -279,11 +285,11 @@ module Rye
279
285
  def to_s; '%s@rye_%s' % [user, @rye_host]; end
280
286
 
281
287
  def inspect
282
- %q{#<%s:%s name=%s cwd=%s umask=%s env=%s safe=%s opts=%s>} %
288
+ %q{#<%s:%s name=%s cwd=%s umask=%s env=%s safe=%s opts=%s keys=%s>} %
283
289
  [self.class.to_s, self.host, self.nickname,
284
290
  @rye_current_working_directory, @rye_current_umask,
285
291
  (@rye_current_environment_variables || '').inspect,
286
- self.safe, self.opts.inspect]
292
+ self.safe, self.opts.inspect, self.keys.inspect]
287
293
  end
288
294
 
289
295
  # Compares itself with the +other+ box. If the hostnames
@@ -372,7 +378,7 @@ module Rye
372
378
  if self.file_exists?(akey_path)
373
379
  # TODO: Make Rye::Cmd.incremental_backup
374
380
  self.cp(akey_path, "#{akey_path}-previous")
375
- authorized_keys = self.download("#{homedir}/#{akey_path}")
381
+ authorized_keys = self.file_download("#{homedir}/#{akey_path}")
376
382
  end
377
383
  authorized_keys ||= StringIO.new
378
384
 
@@ -391,7 +397,7 @@ module Rye
391
397
  authorized_keys.rewind
392
398
 
393
399
  self.mkdir(:p, :m, '700', File.dirname(akey_path))
394
- self.upload(authorized_keys, "#{homedir}/#{akey_path}")
400
+ self.file_upload(authorized_keys, "#{homedir}/#{akey_path}")
395
401
  self.chmod('0600', akey_path)
396
402
  self.chown(:R, this_user.to_s, File.dirname(akey_path))
397
403
  end
@@ -490,18 +496,35 @@ module Rye
490
496
  def unsafely(*args, &block)
491
497
  previous_state = @rye_safe
492
498
  disable_safe_mode
493
- self.instance_exec *args, &block
499
+ ret = self.instance_exec *args, &block
494
500
  @rye_safe = previous_state
501
+ ret
495
502
  end
496
503
 
497
504
  # See unsafely (except in reverse)
498
505
  def safely(*args, &block)
499
506
  previous_state = @rye_safe
500
507
  enable_safe_mode
501
- self.instance_exec *args, &block
508
+ ret = self.instance_exec *args, &block
502
509
  @rye_safe = previous_state
510
+ ret
503
511
  end
504
512
 
513
+ # Like batch, except it enables quiet mode before executing the block.
514
+ # After executing the block, quiet mode is returned back to whichever
515
+ # state it was previously in. In other words, this method won't enable
516
+ # quiet mode if it was already disabled.
517
+ #
518
+ # In quiet mode, the pre and post command hooks are not called. This
519
+ # is used internally when calling commands like +ls+ to check whether
520
+ # a file path exists (to prevent polluting the logs).
521
+ def quietly(*args, &block)
522
+ previous_state = @rye_quiet
523
+ enable_quiet_mode
524
+ ret = self.instance_exec *args, &block
525
+ @rye_quiet = previous_state
526
+ ret
527
+ end
505
528
 
506
529
  # instance_exec for Ruby 1.8 written by Mauricio Fernandez
507
530
  # http://eigenclass.org/hiki/instance_exec
@@ -646,22 +669,24 @@ module Rye
646
669
  raise Rye::NotConnected, @rye_host unless @rye_ssh && !@rye_ssh.closed?
647
670
 
648
671
  cmd_clean = Rye.escape(@rye_safe, cmd, args)
649
- cmd_clean = prepend_env(cmd_clean)
672
+
673
+ # This following is the command we'll actually execute. cmd_clean
674
+ # can be used for logging, otherwise the output is confusing.
675
+ cmd_internal = prepend_env(cmd_clean)
650
676
 
651
677
  # Add the current working directory before the command if supplied.
652
678
  # The command will otherwise run in the user's home directory.
653
679
  if @rye_current_working_directory
654
680
  cwd = Rye.escape(@rye_safe, 'cd', @rye_current_working_directory)
655
- cmd_clean = [cwd, cmd_clean].join(' && ')
681
+ cmd_internal = [cwd, cmd_internal].join(' && ')
656
682
  end
657
683
 
658
684
  # ditto (same explanation as cwd)
659
685
  if @rye_current_umask
660
686
  cwd = Rye.escape(@rye_safe, 'umask', @rye_current_umask)
661
- cmd_clean = [cwd, cmd_clean].join(' && ')
687
+ cmd_internal = [cwd, cmd_internal].join(' && ')
662
688
  end
663
689
 
664
-
665
690
  ## NOTE: Do not raise a CommandNotFound exception in this method.
666
691
  # We want it to be possible to define methods to a single instance
667
692
  # of Rye::Box. i.e. def rbox.rm()...
@@ -673,20 +698,21 @@ module Rye
673
698
 
674
699
  begin
675
700
  info "COMMAND: #{cmd_clean}"
676
- debug "Executing: %s" % cmd_clean
701
+ debug "Executing: #{cmd_internal}"
677
702
 
678
- if @rye_pre_command_hook.is_a?(Proc)
703
+ if !@rye_quiet && @rye_pre_command_hook.is_a?(Proc)
679
704
  @rye_pre_command_hook.call(cmd_clean, user, host, nickname)
680
705
  end
681
706
 
682
- stdout, stderr, ecode, esignal = net_ssh_exec!(cmd_clean)
683
-
684
707
  rap = Rye::Rap.new(self)
708
+ rap.cmd = cmd_clean
709
+
710
+ stdout, stderr, ecode, esignal = net_ssh_exec!(cmd_internal)
711
+
685
712
  rap.add_stdout(stdout || '')
686
713
  rap.add_stderr(stderr || '')
687
714
  rap.add_exit_code(ecode)
688
715
  rap.exit_signal = esignal
689
- rap.cmd = cmd
690
716
 
691
717
  #info "stdout: #{rap.stdout}"
692
718
  #info "stderr: #{rap.stderr}"
@@ -699,10 +725,11 @@ module Rye
699
725
  raise Rye::CommandError.new(rap) if ecode != 0
700
726
 
701
727
  rescue Exception => ex
728
+ return rap if @rye_quiet
702
729
  choice = nil
703
730
  @rye_exception_hook.each_pair do |klass,act|
704
731
  next unless ex.kind_of? klass
705
- choice = @rye_exception_hook[klass].call(ex)
732
+ choice = act.call(ex, cmd_clean, user, host, nickname)
706
733
  break
707
734
  end
708
735
  if choice == :retry
@@ -714,7 +741,9 @@ module Rye
714
741
  end
715
742
  end
716
743
 
717
- @rye_post_command_hook.call(rap) if @rye_post_command_hook.is_a?(Proc)
744
+ if !@rye_quiet && @rye_post_command_hook.is_a?(Proc)
745
+ @rye_post_command_hook.call(rap)
746
+ end
718
747
 
719
748
  rap
720
749
  end
@@ -775,7 +804,7 @@ module Rye
775
804
  raise Rye::NoPty if data =~ /Pseudo-terminal will not/
776
805
 
777
806
  end
778
-
807
+
779
808
  end
780
809
 
781
810
  channel = @rye_ssh.exec(command, &block)
data/lib/rye/cmd.rb CHANGED
@@ -92,7 +92,7 @@ module Rye;
92
92
  # Always return nil.
93
93
  #
94
94
  # NOTE: Changes to current working directory with +cd+ or +[]+ are ignored.
95
- def upload(*files); net_scp_transfer!(:upload, *files); end
95
+ def file_upload(*files); net_scp_transfer!(:upload, *files); end
96
96
 
97
97
  # Transfer files from a machine via Net::SCP.
98
98
  # * +files+ is an Array of files to download. The last element must be the
@@ -105,7 +105,7 @@ module Rye;
105
105
  # Return nil or a StringIO object, if specified as the target.
106
106
  #
107
107
  # NOTE: Changes to current working directory with +cd+ or +[]+ are ignored.
108
- def download(*files); net_scp_transfer!(:download, *files); end
108
+ def file_download(*files); net_scp_transfer!(:download, *files); end
109
109
 
110
110
  # Append +newcontent+ to remote +filepath+. If the file doesn't exist
111
111
  # it will be created. If +backup+ is specified, +filepath+ will be
@@ -113,7 +113,7 @@ module Rye;
113
113
  def file_append(filepath, newcontent, backup=false)
114
114
  if self.file_exists?(filepath)
115
115
  self.cp filepath, "#{filepath}-previous" if backup
116
- file_content = self.download filepath
116
+ file_content = self.file_download filepath
117
117
  end
118
118
 
119
119
  file_content ||= StringIO.new
@@ -124,13 +124,13 @@ module Rye;
124
124
  file_content.puts newcontent
125
125
  end
126
126
 
127
- self.upload file_content, filepath
127
+ self.file_upload file_content, filepath
128
128
  end
129
129
 
130
130
  # Does +path+ from the current working directory?
131
131
  def file_exists?(path)
132
132
  begin
133
- ret = self.ls(path)
133
+ ret = self.quietly { ls(path) }
134
134
  rescue Rye::CommandError => ex
135
135
  ret = ex.rap
136
136
  end
data/lib/rye/set.rb CHANGED
@@ -8,6 +8,10 @@ module Rye
8
8
  attr_reader :boxes
9
9
  attr_reader :opts
10
10
 
11
+ # Run commands in parallel? A Boolean value. Default: false.
12
+ attr_accessor :parallel
13
+
14
+
11
15
  # * +name+ The name of the set of machines
12
16
  # * +opts+ a hash of optional arguments
13
17
  #
@@ -83,11 +87,12 @@ module Rye
83
87
  end
84
88
 
85
89
  def to_s
86
- "%s: %s" % [self.name, ]
90
+ "%s:%s" % [self.class.to_s, @name]
87
91
  end
88
92
 
89
93
  def inspect
90
- %q{#<%s:%s boxes=%s opts=%s>} % [self.class.to_s, self.name, self.boxes.join(','), self.opts.inspect]
94
+ a = [self.class.to_s, @name, @parallel, @opts.inspect, @boxes.inspect]
95
+ %q{#<%s:%s parallel=%s opts=%s boxes=%s>} % a
91
96
  end
92
97
 
93
98
  # See Rye::Box.[]
@@ -142,12 +147,11 @@ module Rye
142
147
  end
143
148
  end
144
149
 
145
- # Should it bubble up the exception for a single box?
146
- # socket errors?
150
+ # Should it bubble up the exception for a single box? socket errors?
147
151
  threads.each do |t|
148
- sleep 0.01 # Give the thread some breathing room
149
- t.join # Wait for the thread to finish
150
- raps << t[:rap] # Grab the result
152
+ Kernel.sleep 0.03 # Give the thread some breathing room
153
+ t.join # Wait for the thread to finish
154
+ raps << t[:rap] # Grab the result
151
155
  end
152
156
 
153
157
  raps
data/lib/rye.rb CHANGED
@@ -1,8 +1,4 @@
1
1
 
2
- # Temporary fix before Highline 1.5.1 is release. This fixes
3
- # a the issue with Ruby 1.9 that causes the prompts to hang.
4
- $:.unshift File.join(File.dirname(__FILE__), '..', 'vendor', 'highline-1.5.1', 'lib')
5
-
6
2
  require 'logger'
7
3
  require 'thread'
8
4
  require 'base64'
@@ -47,7 +43,7 @@ module Rye
47
43
  extend self
48
44
 
49
45
  unless defined?(SYSINFO)
50
- VERSION = 0.7.freeze
46
+ VERSION = "0.8.0".freeze
51
47
  SYSINFO = SysInfo.new.freeze
52
48
  end
53
49
 
@@ -205,7 +201,8 @@ module Rye
205
201
  # NOTE: shell is a bit paranoid so it escapes every argument. This means
206
202
  # you can only use literal values. That means no asterisks too.
207
203
  #
208
- # Returns a Rye::Rap object containing the
204
+ # Returns a Rye::Rap object.
205
+ #
209
206
  def shell(cmd, *args)
210
207
  args = args.flatten.compact
211
208
  cmd = cmd.to_s if cmd.is_a?(Symbol)
data/rye.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  @spec = Gem::Specification.new do |s|
2
2
  s.name = "rye"
3
3
  s.rubyforge_project = "rye"
4
- s.version = "0.7.6"
4
+ s.version = "0.8.0"
5
5
  s.summary = "Rye: Safely run SSH commands on a bunch of machines at the same time (from Ruby)."
6
6
  s.description = s.summary
7
7
  s.author = "Delano Mandelbaum"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rye
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.6
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-19 00:00:00 -04:00
12
+ date: 2009-06-21 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency