From fb5de052e88ea4450613faeced93031b2d677cd2 Mon Sep 17 00:00:00 2001 From: Adam Wiggins Date: Sun, 9 Mar 2008 21:47:43 -0700 Subject: [PATCH 1/8] credits --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index c225aeb..4fde8db 100644 --- a/README +++ b/README @@ -57,7 +57,7 @@ For more details on syntax and commands, see: rush was written by Adam Wiggins (adam at heroku dot com) -Patches contributed by: Chihiro Ito +Patches contributed by: Chihiro Ito, Gabriel Ware rush is released under the MIT License: http://www.opensource.org/licenses/mit-license.php From 3296c8dc8838deeb900bc976d4ff732ba9546d00 Mon Sep 17 00:00:00 2001 From: Adam Wiggins Date: Wed, 12 Mar 2008 13:46:54 -0700 Subject: [PATCH 2/8] purge kills hidden dotfiles --- lib/rush/local.rb | 5 ++++- spec/local_spec.rb | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/rush/local.rb b/lib/rush/local.rb index d9a9a7d..845b93b 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -38,7 +38,10 @@ def destroy(full_path) # Purge the contents of a dir. def purge(full_path) raise "No." if full_path == '/' - FileUtils.rm_rf Dir.glob("#{full_path}/*") + Dir.chdir(full_path) do + all = Dir.glob("*", File::FNM_DOTMATCH).reject { |f| f == '.' or f == '..' } + FileUtils.rm_rf all + end true end diff --git a/spec/local_spec.rb b/spec/local_spec.rb index 72a087e..a747bfe 100644 --- a/spec/local_spec.rb +++ b/spec/local_spec.rb @@ -128,6 +128,13 @@ Dir.glob("#{@sandbox_dir}/*").should == [] end + it "purge kills hidden (dotfile) entries too" do + system "cd #{@sandbox_dir}; touch .killme" + @con.purge(@sandbox_dir) + File.exists?(@sandbox_dir).should be_true + `cd #{@sandbox_dir}; ls -lA | wc -l`.to_i.should == 0 + end + it "create_dir creates a directory" do fname = "#{@sandbox_dir}/a/b/c/" @con.create_dir(fname) From a8819d034bbf96e54abc7a8955b0fd8a82d0091d Mon Sep 17 00:00:00 2001 From: Adam Wiggins Date: Wed, 12 Mar 2008 13:55:09 -0700 Subject: [PATCH 3/8] readme update for new process syntax --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index c225aeb..f8cbe2c 100644 --- a/README +++ b/README @@ -18,7 +18,7 @@ How about killing those pesky stray mongrels? Before: After: - processes.each { |p| p.kill if p.command == "mongrel_rails" } + processes.find_all_by_cmdline(/mongrel_rails/).each { |p| p.kill } But rush is more than just an interactive shell and a library: it can also control any number of remote machines from a single location. Copy files or directories between servers as seamlessly as if it was all local. From e8c942486a8c7f4960762520ee917a00ae14b823 Mon Sep 17 00:00:00 2001 From: Adam Wiggins Date: Wed, 12 Mar 2008 17:57:45 -0700 Subject: [PATCH 4/8] drop through to default method_missing when find_by doesn't match --- lib/rush/find_by.rb | 2 ++ spec/find_by_spec.rb | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/rush/find_by.rb b/lib/rush/find_by.rb index c4959f1..175c410 100644 --- a/lib/rush/find_by.rb +++ b/lib/rush/find_by.rb @@ -12,6 +12,8 @@ def method_missing(meth, *args) find_by(m[1], args.first) elsif m = meth.to_s.match(/^find_all_by_([a-z_]+)$/) find_all_by(m[1], args.first) + else + super end end diff --git a/spec/find_by_spec.rb b/spec/find_by_spec.rb index 2825454..6c9743e 100644 --- a/spec/find_by_spec.rb +++ b/spec/find_by_spec.rb @@ -7,8 +7,7 @@ class Foo def initialize(bar) @bar = bar - end - end + end end @one = Foo.new('one') @two = Foo.new('two') @@ -52,4 +51,8 @@ def initialize(bar) it "find_by_ with field not recognized by objects raises no errors" do @list.find_by_nothing('x') end + + it "raises NoMethodError for things other than find_by" do + lambda { @list.does_not_exist }.should raise_error(NoMethodError) + end end From e4e8026b3f7c5a9c51a3e5815dff500cc7fa6348 Mon Sep 17 00:00:00 2001 From: Adam Wiggins Date: Thu, 13 Mar 2008 00:01:00 -0700 Subject: [PATCH 5/8] gem version 0.2 --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 23c6b1c..e4dce14 100644 --- a/Rakefile +++ b/Rakefile @@ -31,7 +31,7 @@ require 'rake/rdoctask' require 'fileutils' include FileUtils -version = "0.1" +version = "0.2" name = "rush" spec = Gem::Specification.new do |s| From 369ce612801c8ae7662b3489fae71856c6ecca5a Mon Sep 17 00:00:00 2001 From: Adam Wiggins Date: Fri, 14 Mar 2008 22:34:02 -0700 Subject: [PATCH 6/8] Box#bash sudo to another user with :user optional param --- lib/rush/box.rb | 11 +++++++---- lib/rush/dir.rb | 6 +++--- lib/rush/local.rb | 12 +++++++++--- lib/rush/remote.rb | 4 ++-- spec/box_spec.rb | 7 ++++++- spec/dir_spec.rb | 6 ++++++ spec/local_spec.rb | 8 ++++++-- spec/remote_spec.rb | 4 ++-- 8 files changed, 41 insertions(+), 17 deletions(-) diff --git a/lib/rush/box.rb b/lib/rush/box.rb index 02cbe81..1371fd7 100644 --- a/lib/rush/box.rb +++ b/lib/rush/box.rb @@ -49,10 +49,13 @@ def processes end end - # Execute a command in the standard unix shell. Until the day when it's no - # longer needed... - def bash(command) - connection.bash(command) + # Execute a command in the standard unix shell. Optional parameter :user + # switches to that user via sudo first, if you have permission. Example: + # + # box.bash '/etc/init.d/mysql restart', :user => 'root' + # + def bash(command, options={}) + connection.bash(command, options[:user]) end # Returns true if the box is responding to commands. diff --git a/lib/rush/dir.rb b/lib/rush/dir.rb index da3d0ed..aabffd9 100644 --- a/lib/rush/dir.rb +++ b/lib/rush/dir.rb @@ -118,9 +118,9 @@ def nonhidden_files end end - # Run a bash command starting in this directory. - def bash(command) - box.bash "cd #{full_path} && #{command}" + # Run a bash command starting in this directory. Options are the same as Rush::Box#bash. + def bash(command, options={}) + box.bash "cd #{full_path} && #{command}", options end # Destroy all of the contents of the directory, leaving it fresh and clean. diff --git a/lib/rush/local.rb b/lib/rush/local.rb index d9a9a7d..03f6325 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -250,11 +250,17 @@ def kill_process(pid) ::Process.kill('KILL', pid) rescue nil end - def bash(command) + def bash(command, user=nil) require 'session' sh = Session::Bash.new - out, err = sh.execute command + + if user and user != "" + out, err = sh.execute "sudo -u #{user} bash", :stdin => command + else + out, err = sh.execute command + end + retval = sh.status sh.close! @@ -289,7 +295,7 @@ def receive(params) when 'processes' then YAML.dump(processes) when 'process_alive' then process_alive(params[:pid]) ? '1' : '0' when 'kill_process' then kill_process(params[:pid].to_i) - when 'bash' then bash(params[:payload]) + when 'bash' then bash(params[:payload], params[:user]) else raise UnknownAction end diff --git a/lib/rush/remote.rb b/lib/rush/remote.rb index e179ef6..ce3b41e 100644 --- a/lib/rush/remote.rb +++ b/lib/rush/remote.rb @@ -74,8 +74,8 @@ def kill_process(pid) transmit(:action => 'kill_process', :pid => pid) end - def bash(command) - transmit(:action => 'bash', :payload => command) + def bash(command, user) + transmit(:action => 'bash', :payload => command, :user => user) end # Given a hash of parameters (converted by the method call on the connection diff --git a/spec/box_spec.rb b/spec/box_spec.rb index bb885cb..7d424c0 100644 --- a/spec/box_spec.rb +++ b/spec/box_spec.rb @@ -22,10 +22,15 @@ end it "executes bash commands" do - @box.connection.should_receive(:bash).with('cmd').and_return('output') + @box.connection.should_receive(:bash).with('cmd', nil).and_return('output') @box.bash('cmd').should == 'output' end + it "executes bash commands with an optional user" do + @box.connection.should_receive(:bash).with('cmd', 'user') + @box.bash('cmd', :user => 'user') + end + it "checks the connection to determine if it is alive" do @box.connection.should_receive(:alive?).and_return(true) @box.should be_alive diff --git a/spec/dir_spec.rb b/spec/dir_spec.rb index d4f1d28..1c95008 100644 --- a/spec/dir_spec.rb +++ b/spec/dir_spec.rb @@ -150,4 +150,10 @@ system "echo test > #{@dir.full_path}/file" @dir.bash("cat file").should == "test\n" end + + it "passes bash options (e.g., :user) through to the box bash command" do + @box.should_receive(:bash).with('cmd', 'options') + @box.bash('cmd', 'options') + end + end diff --git a/spec/local_spec.rb b/spec/local_spec.rb index 72a087e..f461080 100644 --- a/spec/local_spec.rb +++ b/spec/local_spec.rb @@ -88,8 +88,8 @@ end it "receive -> bash" do - @con.should_receive(:bash).with('cmd').and_return('output') - @con.receive(:action => 'bash', :payload => 'cmd').should == 'output' + @con.should_receive(:bash).with('cmd', 'user').and_return('output') + @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user').should == 'output' end it "receive -> unknown action exception" do @@ -246,6 +246,10 @@ lambda { @con.bash("no_such_bin") }.should raise_error(Rush::BashFailed, /command not found/) end + it "executes a bash command as another user using sudo" do + @con.bash("echo test2", ENV['user']).should == "test2\n" + end + it "ensure_tunnel to match with remote connection" do @con.ensure_tunnel end diff --git a/spec/remote_spec.rb b/spec/remote_spec.rb index d4bf707..a61d27c 100644 --- a/spec/remote_spec.rb +++ b/spec/remote_spec.rb @@ -88,8 +88,8 @@ end it "transmits bash" do - @con.should_receive(:transmit).with(:action => 'bash', :payload => 'cmd').and_return('output') - @con.bash('cmd').should == 'output' + @con.should_receive(:transmit).with(:action => 'bash', :payload => 'cmd', :user => 'user').and_return('output') + @con.bash('cmd', 'user').should == 'output' end it "an http result code of 401 raises NotAuthorized" do From f49801d5fa9e465174dc185612b04728b13c2ab5 Mon Sep 17 00:00:00 2001 From: Adam Wiggins Date: Fri, 14 Mar 2008 22:35:39 -0700 Subject: [PATCH 7/8] spec fix on linux --- spec/local_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/local_spec.rb b/spec/local_spec.rb index 217f010..b1ba7ad 100644 --- a/spec/local_spec.rb +++ b/spec/local_spec.rb @@ -132,7 +132,7 @@ system "cd #{@sandbox_dir}; touch .killme" @con.purge(@sandbox_dir) File.exists?(@sandbox_dir).should be_true - `cd #{@sandbox_dir}; ls -lA | wc -l`.to_i.should == 0 + `cd #{@sandbox_dir}; ls -lA | grep -v total | wc -l`.to_i.should == 0 end it "create_dir creates a directory" do From dcb3f8ebabb489c8df27891c5a5fa2cb8ae8a3ce Mon Sep 17 00:00:00 2001 From: Adam Wiggins Date: Sat, 15 Mar 2008 00:11:24 -0700 Subject: [PATCH 8/8] Box#bash takes an :env option with a hash of environment variables --- lib/rush/box.rb | 20 +++++++++++++++++--- spec/box_spec.rb | 10 ++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/rush/box.rb b/lib/rush/box.rb index 1371fd7..cc5a0c9 100644 --- a/lib/rush/box.rb +++ b/lib/rush/box.rb @@ -49,13 +49,27 @@ def processes end end - # Execute a command in the standard unix shell. Optional parameter :user - # switches to that user via sudo first, if you have permission. Example: + # Execute a command in the standard unix shell. Options: + # + # :user => unix username to become via sudo + # :env => hash of environment variables + # + # Example: # # box.bash '/etc/init.d/mysql restart', :user => 'root' + # box.bash 'rake db:migrate', :user => 'www', :env => { :RAILS_ENV => 'production' } # def bash(command, options={}) - connection.bash(command, options[:user]) + connection.bash(command_with_environment(command, options[:env]), options[:user]) + end + + def command_with_environment(command, env) # :nodoc: + return command unless env + + vars = env.map do |key, value| + "export #{key}='#{value}'" + end + vars.push(command).join("\n") end # Returns true if the box is responding to commands. diff --git a/spec/box_spec.rb b/spec/box_spec.rb index 7d424c0..11be94c 100644 --- a/spec/box_spec.rb +++ b/spec/box_spec.rb @@ -31,6 +31,16 @@ @box.bash('cmd', :user => 'user') end + it "builds a script of environment variables to prefix the bash command" do + @box.command_with_environment('cmd', { :a => 'b' }).should == "export a='b'\ncmd" + end + + it "sets the environment variables from the provided hash" do + @box.connection.stub!(:bash) + @box.should_receive(:command_with_environment).with('cmd', { 1 => 2 }) + @box.bash('cmd', :env => { 1 => 2 }) + end + it "checks the connection to determine if it is alive" do @box.connection.should_receive(:alive?).and_return(true) @box.should be_alive