From c1fb492e5da8fca2c279d7eaa144f98e6c23eb47 Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Wed, 11 Sep 2013 10:30:22 -0300 Subject: [PATCH 01/10] added --ssh option to snapshot --- lib/rhc/commands/snapshot.rb | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/rhc/commands/snapshot.rb b/lib/rhc/commands/snapshot.rb index b55d7d4f8..292174f86 100644 --- a/lib/rhc/commands/snapshot.rb +++ b/lib/rhc/commands/snapshot.rb @@ -1,4 +1,5 @@ require 'rhc/commands/base' +require 'rhc/ssh_helpers' module RHC::Commands class Snapshot < Base @@ -18,17 +19,22 @@ class Snapshot < Base default_action :help summary "Save a snapshot of your app to disk" - syntax " [--filepath FILE]" + syntax " [--filepath FILE] [--ssh path_to_ssh_executable]" option ["-n", "--namespace NAME"], "Namespace of the application you are saving a snapshot", :context => :namespace_context, :required => true option ["-f", "--filepath FILE"], "Local path to save tarball (default: ./$APPNAME.tar.gz)" + option ["--ssh PATH"], "Path to your SSH executable or additional options" argument :app, "Application you are saving a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot save", :root_command => true, :deprecated => true def save(app) + raise OptionParser::InvalidOption, "No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH." unless options.ssh or has_ssh? + raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh) + raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh) rest_app = rest_client.find_application(options.namespace, app) ssh_uri = URI.parse(rest_app.ssh_url) filename = options.filepath ? options.filepath : "#{app}.tar.gz" - ssh_cmd = "ssh #{ssh_uri.user}@#{ssh_uri.host} 'snapshot' > #{filename}" + ssh = options.ssh || 'ssh' + ssh_cmd = "#{ssh} #{ssh_uri.user}@#{ssh_uri.host} 'snapshot' > #{filename}" debug ssh_cmd say "Pulling down a snapshot to #{filename}..." @@ -69,6 +75,9 @@ def save(app) argument :app, "Application of which you are restoring a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot restore", :root_command => true, :deprecated => true def restore(app) + raise OptionParser::InvalidOption, "No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH." unless options.ssh or has_ssh? + raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh) + raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh) filename = options.filepath ? options.filepath : "#{app}.tar.gz" @@ -78,7 +87,8 @@ def restore(app) rest_app = rest_client.find_application(options.namespace, app) ssh_uri = URI.parse(rest_app.ssh_url) - ssh_cmd = "cat #{filename} | ssh #{ssh_uri.user}@#{ssh_uri.host} 'restore#{include_git ? ' INCLUDE_GIT' : ''}'" + ssh = options.ssh || 'ssh' + ssh_cmd = "cat #{filename} | #{ssh} #{ssh_uri.user}@#{ssh_uri.host} 'restore#{include_git ? ' INCLUDE_GIT' : ''}'" say "Restoring from snapshot #{filename}..." debug ssh_cmd @@ -125,5 +135,7 @@ def restore(app) 0 end + protected + include RHC::SSHHelpers end end From be2ef0c56371e81b4f35c360d49a21daedd3ab30 Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Wed, 11 Sep 2013 23:19:48 -0300 Subject: [PATCH 02/10] accepting ssh options + tests --- lib/rhc/commands/snapshot.rb | 12 ++++++------ spec/rhc/commands/snapshot_spec.rb | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/rhc/commands/snapshot.rb b/lib/rhc/commands/snapshot.rb index 292174f86..ddd5d3bd4 100644 --- a/lib/rhc/commands/snapshot.rb +++ b/lib/rhc/commands/snapshot.rb @@ -1,5 +1,4 @@ require 'rhc/commands/base' -require 'rhc/ssh_helpers' module RHC::Commands class Snapshot < Base @@ -27,8 +26,8 @@ class Snapshot < Base alias_action :"app snapshot save", :root_command => true, :deprecated => true def save(app) raise OptionParser::InvalidOption, "No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH." unless options.ssh or has_ssh? - raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh) - raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh) + raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh.split(' ').first) + raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh.split(' ').first) rest_app = rest_client.find_application(options.namespace, app) ssh_uri = URI.parse(rest_app.ssh_url) filename = options.filepath ? options.filepath : "#{app}.tar.gz" @@ -69,15 +68,16 @@ def save(app) end summary "Restores a previously saved snapshot" - syntax " [--filepath FILE]" + syntax " [--filepath FILE] [--ssh path_to_ssh_executable]" option ["-n", "--namespace NAME"], "Namespace of the application you are restoring a snapshot", :context => :namespace_context, :required => true option ["-f", "--filepath FILE"], "Local path to restore tarball" + option ["--ssh PATH"], "Path to your SSH executable or additional options" argument :app, "Application of which you are restoring a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot restore", :root_command => true, :deprecated => true def restore(app) raise OptionParser::InvalidOption, "No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH." unless options.ssh or has_ssh? - raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh) - raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh) + raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh.split(' ').first) + raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh.split(' ').first) filename = options.filepath ? options.filepath : "#{app}.tar.gz" diff --git a/spec/rhc/commands/snapshot_spec.rb b/spec/rhc/commands/snapshot_spec.rb index 059aa0614..aa9bd7dea 100644 --- a/spec/rhc/commands/snapshot_spec.rb +++ b/spec/rhc/commands/snapshot_spec.rb @@ -28,12 +28,12 @@ end describe 'snapshot save' do - let(:arguments) {['snapshot', 'save', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp']} + let(:arguments) {['snapshot', 'save', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', '/usr/bin/ssh -oSomeOpt=1']} context 'when saving a snapshot' do before(:each) do `(exit 0)` - Kernel.should_receive(:`).with("ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz") + Kernel.should_receive(:`).with("/usr/bin/ssh -oSomeOpt=1 #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz") end it { expect { run }.to exit_with_code(0) } end @@ -41,7 +41,7 @@ context 'when failing to save a snapshot' do before(:each) do `(exit 1)` - Kernel.should_receive(:`).with("ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz") + Kernel.should_receive(:`).with("/usr/bin/ssh -oSomeOpt=1 #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz") end it { expect { run }.to exit_with_code(130) } end @@ -73,14 +73,14 @@ end describe 'snapshot restore' do - let(:arguments) {['snapshot', 'restore', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp']} + let(:arguments) {['snapshot', 'restore', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', '/usr/bin/ssh -oSomeOpt=1']} context 'when restoring a snapshot' do before(:each) do File.stub(:exists?).and_return(true) RHC::TarGz.stub(:contains).and_return(true) `(exit 0)` - Kernel.should_receive(:`).with("cat #{@app.name}.tar.gz | ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'") + Kernel.should_receive(:`).with("cat #{@app.name}.tar.gz | /usr/bin/ssh -oSomeOpt=1 #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'") end it { expect { run }.to exit_with_code(0) } end @@ -89,7 +89,7 @@ before(:each) do File.stub(:exists?).and_return(true) RHC::TarGz.stub(:contains).and_return(true) - Kernel.should_receive(:`).with("cat #{@app.name}.tar.gz | ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'") + Kernel.should_receive(:`).with("cat #{@app.name}.tar.gz | /usr/bin/ssh -oSomeOpt=1 #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'") $?.stub(:exitstatus) { 1 } end it { expect { run }.to exit_with_code(130) } From 21c00331146f386a48ab0ee1e3864fb7649a25a4 Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Thu, 12 Sep 2013 08:48:00 -0300 Subject: [PATCH 03/10] better help mesg --- lib/rhc/commands/snapshot.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rhc/commands/snapshot.rb b/lib/rhc/commands/snapshot.rb index ddd5d3bd4..b31764e17 100644 --- a/lib/rhc/commands/snapshot.rb +++ b/lib/rhc/commands/snapshot.rb @@ -21,7 +21,7 @@ class Snapshot < Base syntax " [--filepath FILE] [--ssh path_to_ssh_executable]" option ["-n", "--namespace NAME"], "Namespace of the application you are saving a snapshot", :context => :namespace_context, :required => true option ["-f", "--filepath FILE"], "Local path to save tarball (default: ./$APPNAME.tar.gz)" - option ["--ssh PATH"], "Path to your SSH executable or additional options" + option ["--ssh PATH"], "Full path to your SSH executable with additional options" argument :app, "Application you are saving a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot save", :root_command => true, :deprecated => true def save(app) @@ -71,7 +71,7 @@ def save(app) syntax " [--filepath FILE] [--ssh path_to_ssh_executable]" option ["-n", "--namespace NAME"], "Namespace of the application you are restoring a snapshot", :context => :namespace_context, :required => true option ["-f", "--filepath FILE"], "Local path to restore tarball" - option ["--ssh PATH"], "Path to your SSH executable or additional options" + option ["--ssh PATH"], "Full path to your SSH executable with additional options" argument :app, "Application of which you are restoring a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot restore", :root_command => true, :deprecated => true def restore(app) From 2bd191635f77d9c6197185aaef792be6b502338f Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Wed, 18 Sep 2013 20:03:06 -0300 Subject: [PATCH 04/10] dont need to test for installed ssh --- lib/rhc/commands/snapshot.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/rhc/commands/snapshot.rb b/lib/rhc/commands/snapshot.rb index 239166bf9..e35bb604f 100644 --- a/lib/rhc/commands/snapshot.rb +++ b/lib/rhc/commands/snapshot.rb @@ -25,7 +25,6 @@ class Snapshot < Base argument :app, "Application you are saving a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot save", :root_command => true, :deprecated => true def save(app) - raise OptionParser::InvalidOption, "No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH." unless options.ssh or has_ssh? raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh.split(' ').first) raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh.split(' ').first) rest_app = rest_client.find_application(options.namespace, app) @@ -75,7 +74,6 @@ def save(app) argument :app, "Application of which you are restoring a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot restore", :root_command => true, :deprecated => true def restore(app) - raise OptionParser::InvalidOption, "No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH." unless options.ssh or has_ssh? raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh.split(' ').first) raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh.split(' ').first) @@ -135,7 +133,5 @@ def restore(app) 0 end - protected - include RHC::SSHHelpers end end From 4b596aafdee5a8bea17428df27ac334552dd1862 Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Thu, 19 Sep 2013 20:54:55 -0300 Subject: [PATCH 05/10] added snapshot ssh exception --- lib/rhc/commands/snapshot.rb | 8 +++--- lib/rhc/exceptions.rb | 6 +++++ spec/rhc/commands/snapshot_spec.rb | 39 ++++++++++++++++++++++-------- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/lib/rhc/commands/snapshot.rb b/lib/rhc/commands/snapshot.rb index e35bb604f..94dddf809 100644 --- a/lib/rhc/commands/snapshot.rb +++ b/lib/rhc/commands/snapshot.rb @@ -25,8 +25,8 @@ class Snapshot < Base argument :app, "Application you are saving a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot save", :root_command => true, :deprecated => true def save(app) - raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh.split(' ').first) - raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh.split(' ').first) + raise RHC::InvalidSSHExecutableException.new "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh.split(' ').first) + raise RHC::InvalidSSHExecutableException.new "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh.split(' ').first) rest_app = rest_client.find_application(options.namespace, app) ssh_uri = URI.parse(rest_app.ssh_url) filename = options.filepath ? options.filepath : "#{app}.tar.gz" @@ -74,8 +74,8 @@ def save(app) argument :app, "Application of which you are restoring a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot restore", :root_command => true, :deprecated => true def restore(app) - raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh.split(' ').first) - raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh.split(' ').first) + raise RHC::InvalidSSHExecutableException.new "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh.split(' ').first) + raise RHC::InvalidSSHExecutableException.new "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh.split(' ').first) filename = options.filepath ? options.filepath : "#{app}.tar.gz" diff --git a/lib/rhc/exceptions.rb b/lib/rhc/exceptions.rb index 04a8d8c7e..e2eeddc79 100644 --- a/lib/rhc/exceptions.rb +++ b/lib/rhc/exceptions.rb @@ -181,4 +181,10 @@ def initialize(uri) super "Invalid URI specified: #{uri}" end end + + class InvalidSSHExecutableException < Exception + def initialize(message="Invalid or missing SSH executable") + super message + end + end end diff --git a/spec/rhc/commands/snapshot_spec.rb b/spec/rhc/commands/snapshot_spec.rb index a86a06077..5d3ae3b59 100644 --- a/spec/rhc/commands/snapshot_spec.rb +++ b/spec/rhc/commands/snapshot_spec.rb @@ -13,13 +13,13 @@ user_config @app = rest_client.add_domain("mockdomain").add_application APP_NAME, 'mock-1.0' @ssh_uri = URI.parse @app.ssh_url - filename = APP_NAME + '.tar.gz' - FileUtils.cp(File.expand_path('../../assets/targz_sample.tar.gz', __FILE__), filename) + @targz_filename = APP_NAME + '.tar.gz' + FileUtils.cp(File.expand_path('../../assets/targz_sample.tar.gz', __FILE__), @targz_filename) + File.chmod 0644, @targz_filename unless File.executable? @targz_filename end after do - filename = APP_NAME + '.tar.gz' - File.delete filename if File.exist? filename + File.delete @targz_filename if File.exist? @targz_filename end describe 'snapshot without an action' do @@ -28,12 +28,12 @@ end describe 'snapshot save' do - let(:arguments) {['snapshot', 'save', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', '/usr/bin/ssh -oSomeOpt=1']} + let(:arguments) {['snapshot', 'save', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp']} context 'when saving a snapshot' do before(:each) do `(exit 0)` - Kernel.should_receive(:`).with("/usr/bin/ssh -oSomeOpt=1 #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz") + Kernel.should_receive(:`).with("ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz") end it { expect { run }.to exit_with_code(0) } end @@ -41,7 +41,7 @@ context 'when failing to save a snapshot' do before(:each) do `(exit 1)` - Kernel.should_receive(:`).with("/usr/bin/ssh -oSomeOpt=1 #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz") + Kernel.should_receive(:`).with("ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz") end it { expect { run }.to exit_with_code(130) } end @@ -72,15 +72,25 @@ end + describe 'snapshot save with invalid ssh executable' do + let(:arguments) {['snapshot', 'save', '--trace', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', 'path_to_ssh']} + it('should raise') { expect{ run }.to raise_error(RHC::InvalidSSHExecutableException, /SSH executable 'path_to_ssh' does not exist./) } + end + + describe 'snapshot save when ssh is not executable' do + let(:arguments) {['snapshot', 'save', '--trace', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', @targz_filename]} + it('should raise') { expect{ run }.to raise_error(RHC::InvalidSSHExecutableException, /SSH executable '#{@targz_filename}' is not executable./) } + end + describe 'snapshot restore' do - let(:arguments) {['snapshot', 'restore', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', '/usr/bin/ssh -oSomeOpt=1']} + let(:arguments) {['snapshot', 'restore', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp']} context 'when restoring a snapshot' do before(:each) do File.stub(:exists?).and_return(true) RHC::TarGz.stub(:contains).and_return(true) `(exit 0)` - Kernel.should_receive(:`).with("cat '#{@app.name}.tar.gz' | /usr/bin/ssh -oSomeOpt=1 #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'") + Kernel.should_receive(:`).with("cat '#{@app.name}.tar.gz' | ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'") end it { expect { run }.to exit_with_code(0) } end @@ -89,7 +99,7 @@ before(:each) do File.stub(:exists?).and_return(true) RHC::TarGz.stub(:contains).and_return(true) - Kernel.should_receive(:`).with("cat '#{@app.name}.tar.gz' | /usr/bin/ssh -oSomeOpt=1 #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'") + Kernel.should_receive(:`).with("cat '#{@app.name}.tar.gz' | ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'") $?.stub(:exitstatus) { 1 } end it { expect { run }.to exit_with_code(130) } @@ -142,5 +152,14 @@ end end + describe 'snapshot restore with invalid ssh executable' do + let(:arguments) {['snapshot', 'restore', '--trace', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', 'path_to_ssh']} + it('should raise') { expect{ run }.to raise_error(RHC::InvalidSSHExecutableException, /SSH executable 'path_to_ssh' does not exist./) } + end + + describe 'snapshot save when ssh is not executable' do + let(:arguments) {['snapshot', 'restore', '--trace', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', @targz_filename]} + it('should raise') { expect{ run }.to raise_error(RHC::InvalidSSHExecutableException, /SSH executable '#{@targz_filename}' is not executable./) } + end end From 23cb32acde21736c7956a9aafc770be0cc341efd Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Sun, 22 Sep 2013 21:54:04 -0300 Subject: [PATCH 06/10] add --ssh to snapshot --- lib/rhc/commands/snapshot.rb | 16 +++++++++++----- lib/rhc/commands/ssh.rb | 8 +++----- lib/rhc/exceptions.rb | 6 ++++++ lib/rhc/ssh_helpers.rb | 14 ++++++++++++++ spec/rhc/commands/snapshot_spec.rb | 27 +++++++++++++++++++++++---- spec/rhc/commands/ssh_spec.rb | 4 ++-- 6 files changed, 59 insertions(+), 16 deletions(-) diff --git a/lib/rhc/commands/snapshot.rb b/lib/rhc/commands/snapshot.rb index a93fc9ff9..05bf95265 100644 --- a/lib/rhc/commands/snapshot.rb +++ b/lib/rhc/commands/snapshot.rb @@ -18,17 +18,19 @@ class Snapshot < Base default_action :help summary "Save a snapshot of your app to disk" - syntax " [--filepath FILE]" + syntax " [--filepath FILE] [--ssh path_to_ssh_executable]" option ["-n", "--namespace NAME"], "Namespace of the application you are saving a snapshot", :context => :namespace_context, :required => true option ["-f", "--filepath FILE"], "Local path to save tarball (default: ./$APPNAME.tar.gz)" + option ["--ssh PATH"], "Full path to your SSH executable with additional options" argument :app, "Application you are saving a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot save", :root_command => true, :deprecated => true def save(app) + ssh = check_ssh_executable! options.ssh rest_app = rest_client.find_application(options.namespace, app) ssh_uri = URI.parse(rest_app.ssh_url) filename = options.filepath ? options.filepath : "#{app}.tar.gz" - ssh_cmd = "ssh #{ssh_uri.user}@#{ssh_uri.host} 'snapshot' > #{filename}" + ssh_cmd = "#{ssh} #{ssh_uri.user}@#{ssh_uri.host} 'snapshot' > #{filename}" debug ssh_cmd say "Pulling down a snapshot to #{filename}..." @@ -63,13 +65,14 @@ def save(app) end summary "Restores a previously saved snapshot" - syntax " [--filepath FILE]" + syntax " [--filepath FILE] [--ssh path_to_ssh_executable]" option ["-n", "--namespace NAME"], "Namespace of the application you are restoring a snapshot", :context => :namespace_context, :required => true option ["-f", "--filepath FILE"], "Local path to restore tarball" + option ["--ssh PATH"], "Full path to your SSH executable with additional options" argument :app, "Application of which you are restoring a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot restore", :root_command => true, :deprecated => true def restore(app) - + ssh = check_ssh_executable! options.ssh filename = options.filepath ? options.filepath : "#{app}.tar.gz" if File.exists? filename @@ -78,7 +81,7 @@ def restore(app) rest_app = rest_client.find_application(options.namespace, app) ssh_uri = URI.parse(rest_app.ssh_url) - ssh_cmd = "cat '#{filename}' | ssh #{ssh_uri.user}@#{ssh_uri.host} 'restore#{include_git ? ' INCLUDE_GIT' : ''}'" + ssh_cmd = "cat '#{filename}' | #{ssh} #{ssh_uri.user}@#{ssh_uri.host} 'restore#{include_git ? ' INCLUDE_GIT' : ''}'" say "Restoring from snapshot #{filename}..." debug ssh_cmd @@ -125,5 +128,8 @@ def restore(app) 0 end + protected + include RHC::SSHHelpers + end end diff --git a/lib/rhc/commands/ssh.rb b/lib/rhc/commands/ssh.rb index 17a1db45d..dba7a66a4 100644 --- a/lib/rhc/commands/ssh.rb +++ b/lib/rhc/commands/ssh.rb @@ -29,9 +29,8 @@ def run(app_name, command) raise ArgumentError, "No application specified" unless app_name.present? raise ArgumentError, "--gears requires a command" if options.gears && command.blank? raise ArgumentError, "--limit must be an integer greater than zero" if options.limit && options.limit < 1 - raise OptionParser::InvalidOption, "No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH." unless options.ssh or has_ssh? - raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' does not exist." if options.ssh and not File.exist?(options.ssh) - raise OptionParser::InvalidOption, "SSH executable '#{options.ssh}' is not executable." if options.ssh and not File.executable?(options.ssh) + + ssh = check_ssh_executable! options.ssh if options.gears groups = rest_client.find_application_gear_groups(options.namespace, app_name) @@ -41,10 +40,9 @@ def run(app_name, command) rest_app = rest_client.find_application(options.namespace, app_name) $stderr.puts "Connecting to #{rest_app.ssh_string.to_s} ..." unless command.present? - ssh = options.ssh || 'ssh' debug "Using user specified SSH: #{options.ssh}" if options.ssh - command_line = [ssh, rest_app.ssh_string.to_s, command].compact.flatten + command_line = ssh.split + [rest_app.ssh_string.to_s, command].compact.flatten debug "Invoking Kernel.exec with #{command_line.inspect}" Kernel.send(:exec, *command_line) end diff --git a/lib/rhc/exceptions.rb b/lib/rhc/exceptions.rb index 04a8d8c7e..e2eeddc79 100644 --- a/lib/rhc/exceptions.rb +++ b/lib/rhc/exceptions.rb @@ -181,4 +181,10 @@ def initialize(uri) super "Invalid URI specified: #{uri}" end end + + class InvalidSSHExecutableException < Exception + def initialize(message="Invalid or missing SSH executable") + super message + end + end end diff --git a/lib/rhc/ssh_helpers.rb b/lib/rhc/ssh_helpers.rb index 9c6a925b0..8220e8316 100644 --- a/lib/rhc/ssh_helpers.rb +++ b/lib/rhc/ssh_helpers.rb @@ -290,6 +290,20 @@ def has_ssh? end end + # return supplied ssh executable, if valid (executable, searches $PATH). + # if none was supplied, return installed ssh, if any. + def check_ssh_executable!(path) + if not path + raise RHC::InvalidSSHExecutableException.new("No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH.") unless has_ssh? + 'ssh' + else + bin_path = path.split(' ').first + raise RHC::InvalidSSHExecutableException.new("SSH executable '#{bin_path}' does not exist.") unless File.exist?(bin_path) or exe?(bin_path) + raise RHC::InvalidSSHExecutableException.new("SSH executable '#{bin_path}' is not executable.") unless File.executable?(bin_path) or exe?(bin_path) + path + end + end + private def ssh_add diff --git a/spec/rhc/commands/snapshot_spec.rb b/spec/rhc/commands/snapshot_spec.rb index 884dba4d4..5d3ae3b59 100644 --- a/spec/rhc/commands/snapshot_spec.rb +++ b/spec/rhc/commands/snapshot_spec.rb @@ -13,13 +13,13 @@ user_config @app = rest_client.add_domain("mockdomain").add_application APP_NAME, 'mock-1.0' @ssh_uri = URI.parse @app.ssh_url - filename = APP_NAME + '.tar.gz' - FileUtils.cp(File.expand_path('../../assets/targz_sample.tar.gz', __FILE__), filename) + @targz_filename = APP_NAME + '.tar.gz' + FileUtils.cp(File.expand_path('../../assets/targz_sample.tar.gz', __FILE__), @targz_filename) + File.chmod 0644, @targz_filename unless File.executable? @targz_filename end after do - filename = APP_NAME + '.tar.gz' - File.delete filename if File.exist? filename + File.delete @targz_filename if File.exist? @targz_filename end describe 'snapshot without an action' do @@ -72,6 +72,16 @@ end + describe 'snapshot save with invalid ssh executable' do + let(:arguments) {['snapshot', 'save', '--trace', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', 'path_to_ssh']} + it('should raise') { expect{ run }.to raise_error(RHC::InvalidSSHExecutableException, /SSH executable 'path_to_ssh' does not exist./) } + end + + describe 'snapshot save when ssh is not executable' do + let(:arguments) {['snapshot', 'save', '--trace', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', @targz_filename]} + it('should raise') { expect{ run }.to raise_error(RHC::InvalidSSHExecutableException, /SSH executable '#{@targz_filename}' is not executable./) } + end + describe 'snapshot restore' do let(:arguments) {['snapshot', 'restore', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp']} @@ -142,5 +152,14 @@ end end + describe 'snapshot restore with invalid ssh executable' do + let(:arguments) {['snapshot', 'restore', '--trace', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', 'path_to_ssh']} + it('should raise') { expect{ run }.to raise_error(RHC::InvalidSSHExecutableException, /SSH executable 'path_to_ssh' does not exist./) } + end + + describe 'snapshot save when ssh is not executable' do + let(:arguments) {['snapshot', 'restore', '--trace', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--ssh', @targz_filename]} + it('should raise') { expect{ run }.to raise_error(RHC::InvalidSSHExecutableException, /SSH executable '#{@targz_filename}' is not executable./) } + end end diff --git a/spec/rhc/commands/ssh_spec.rb b/spec/rhc/commands/ssh_spec.rb index cea6f7dbe..d06e062af 100644 --- a/spec/rhc/commands/ssh_spec.rb +++ b/spec/rhc/commands/ssh_spec.rb @@ -94,7 +94,7 @@ @domain.add_application("app1", "mock_type") RHC::Commands::Ssh.any_instance.should_receive(:has_ssh?).and_return(false) end - it { run_output.should match("Please use the --ssh option to specify the path to your SSH executable, or install SSH.") } + it { run_output.should match("No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH.") } it { expect { run }.to exit_with_code(1) } end end @@ -119,7 +119,7 @@ @domain.add_application("app1", "mock_type") RHC::Commands::Ssh.any_instance.should_not_receive(:has_ssh?) File.should_receive(:exist?).with("path_to_ssh").once.and_return(true) - File.should_receive(:executable?).with("path_to_ssh").once.and_return(false) + File.should_receive(:executable?).with(/.*path_to_ssh/).at_least(1).and_return(false) end it { run_output.should match("SSH executable 'path_to_ssh' is not executable.") } it { expect { run }.to exit_with_code(1) } From ed6765dcd45252fd5a1a03bc148a480be1dbece0 Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Mon, 23 Sep 2013 13:59:13 -0300 Subject: [PATCH 07/10] Building branch ssh-option --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index b3fc12371..f3320785e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,3 +12,6 @@ notifications: # - dev@lists.openshift.redhat.com irc: - "irc.freenode.org#openshift-dev" +branches: + only: + - ssh-option From 7466ca88828c2cc2904eff9e0f4f68abe7dd8e8a Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Mon, 23 Sep 2013 14:46:21 -0300 Subject: [PATCH 08/10] removed travis branch --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f3320785e..b3fc12371 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,3 @@ notifications: # - dev@lists.openshift.redhat.com irc: - "irc.freenode.org#openshift-dev" -branches: - only: - - ssh-option From 7bc4d5ba3acd8d96657b2cd0ce17ac388628642a Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Tue, 24 Sep 2013 16:36:18 -0300 Subject: [PATCH 09/10] stub has_ssh? --- spec/rhc/commands/snapshot_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/rhc/commands/snapshot_spec.rb b/spec/rhc/commands/snapshot_spec.rb index 5d3ae3b59..ac43af7c3 100644 --- a/spec/rhc/commands/snapshot_spec.rb +++ b/spec/rhc/commands/snapshot_spec.rb @@ -41,6 +41,7 @@ context 'when failing to save a snapshot' do before(:each) do `(exit 1)` + subject.class.any_instance.should_receive(:has_ssh?).and_return(true) Kernel.should_receive(:`).with("ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz") end it { expect { run }.to exit_with_code(130) } @@ -99,6 +100,7 @@ before(:each) do File.stub(:exists?).and_return(true) RHC::TarGz.stub(:contains).and_return(true) + subject.class.any_instance.should_receive(:has_ssh?).and_return(true) Kernel.should_receive(:`).with("cat '#{@app.name}.tar.gz' | ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'") $?.stub(:exitstatus) { 1 } end From 2ae2785782a9ab82b12a8ecf3b1104cbbb0f200e Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Tue, 24 Sep 2013 17:03:47 -0300 Subject: [PATCH 10/10] flatten than compact ssh cmd --- lib/rhc/commands/ssh.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rhc/commands/ssh.rb b/lib/rhc/commands/ssh.rb index dba7a66a4..7445cbf38 100644 --- a/lib/rhc/commands/ssh.rb +++ b/lib/rhc/commands/ssh.rb @@ -42,7 +42,7 @@ def run(app_name, command) debug "Using user specified SSH: #{options.ssh}" if options.ssh - command_line = ssh.split + [rest_app.ssh_string.to_s, command].compact.flatten + command_line = [ ssh.split, rest_app.ssh_string.to_s, command].flatten.compact debug "Invoking Kernel.exec with #{command_line.inspect}" Kernel.send(:exec, *command_line) end