here_or_there 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 028df86fad5122406b9777794a31fd0b3618ad5d
4
+ data.tar.gz: 8b492ae31ae4df58236da68b478e99ece562e4a3
5
+ SHA512:
6
+ metadata.gz: fcaeba290157c9d4b63fc0058849b4b9a2d9939aa5f04e51fbc1be8d97db6c8d55f145e5ddd842838f7bd17e19280ee0b46ae1a7a6b9b6bae653c4d498e5d6ad
7
+ data.tar.gz: 02b3273e0e0e7bab4eedee8af30d2b917c75a5b185d2662993d13bebba5e42c3afb65a6bf93087693a946cd4f2dc17e8ad4f8e37805e18839750914d94f5d5ac
@@ -0,0 +1,14 @@
1
+ module HereOrThere
2
+ class Local
3
+
4
+ def run command, &block
5
+ stdout, stderr, status = Open3.capture3(command)
6
+ response = Response.new( stdout, stderr, status.success? )
7
+
8
+ yield response if block_given?
9
+
10
+ return response
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,71 @@
1
+ module HereOrThere
2
+ module Remote
3
+ class << self
4
+
5
+ def session options={}
6
+ sessions.fetch(options) { add_session(options) }
7
+ end
8
+
9
+ private
10
+
11
+ def sessions
12
+ @_sessions ||= {}
13
+ end
14
+
15
+ def add_session options
16
+ sessions[options] = SSH.new( options )
17
+ end
18
+
19
+ end
20
+
21
+ class SSH
22
+ attr_reader :hostname, :user, :options
23
+ attr_reader :session
24
+
25
+ def initialize options
26
+ @options = options.dup
27
+ @hostname = @options.delete(:hostname)
28
+ @user = @options.delete(:user)
29
+ end
30
+
31
+ def run command
32
+ stdout, stderr, status = [ '', '', false ]
33
+
34
+ open_session
35
+
36
+ session.exec! command do |channel, response_type, response_data|
37
+
38
+ if response_type == :stdout
39
+ stdout = response_data
40
+ status = true
41
+ else
42
+ stderr = response_data
43
+ end
44
+
45
+ return Response.new( stdout, stderr, status )
46
+ end
47
+
48
+ # catch that odd state where no data is returned
49
+ # but the execution is successful
50
+ return Response.new( '', '', true )
51
+
52
+ rescue Net::SSH::AuthenticationFailed
53
+ close_session
54
+ return Response.new( '', 'Authentication failed when connecting to remote', false )
55
+ end
56
+
57
+ private
58
+
59
+ def open_session
60
+ unless session && !session.closed?
61
+ @session = Net::SSH.start hostname, user, options
62
+ end
63
+ end
64
+
65
+ def close_session
66
+ session.close unless !session || session.closed?
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,29 @@
1
+ module HereOrThere
2
+ class Response
3
+ attr_reader :stdout, :stderr, :status
4
+
5
+ def initialize stdout, stderr, status
6
+ @stdout = stdout
7
+ @stderr = stderr
8
+ @status = status
9
+ end
10
+
11
+ def [] key
12
+ case key
13
+ when 0 then stdout
14
+ when 1 then stderr
15
+ when 2 then status
16
+ else
17
+ nil
18
+ end
19
+ end
20
+
21
+ def success?
22
+ if status.respond_to? :success?
23
+ status.success?
24
+ else
25
+ status
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module HereOrThere
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,21 @@
1
+ # util
2
+ require 'open3'
3
+ require 'net/ssh'
4
+
5
+ # gem
6
+ require 'here_or_there/version'
7
+ require 'here_or_there/response'
8
+ require 'here_or_there/local'
9
+ require 'here_or_there/remote'
10
+
11
+ module HereOrThere
12
+
13
+ def run_local command
14
+ Local.new.run command
15
+ end
16
+
17
+ def run_remote command, options={}
18
+ Remote.session( options ).run( command )
19
+ end
20
+
21
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe HereOrThere::Local do
4
+ context "when not given a block" do
5
+
6
+ it "returns a Response instance" do
7
+ ret = HereOrThere::Local.new.run( 'spec/fixtures/hello_stdout')
8
+ expect( ret.is_a? HereOrThere::Response ).to be_true
9
+ end
10
+ it "returns stdout as return.stdout" do
11
+ ret = HereOrThere::Local.new.run( 'spec/fixtures/hello_stdout' )
12
+ expect( ret.stdout ).to eq "Hello Stdout\n"
13
+ end
14
+
15
+ it "returns stderr as return.stderr" do
16
+ ret = HereOrThere::Local.new.run( 'spec/fixtures/hello_stderr' )
17
+ expect( ret.stderr ).to eq "Hello Stderr\n"
18
+ end
19
+
20
+ it "returns a successful status for a successful command" do
21
+ ret_succ = HereOrThere::Local.new.run( 'spec/fixtures/hello_stdout' )
22
+ expect( ret_succ.success? ).to be_true
23
+ end
24
+
25
+ it "returns an unsuccessful status for an unsuccessful command" do
26
+ ret_err = HereOrThere::Local.new.run( 'spec/fixtures/hello_stderr' )
27
+ expect( ret_err.success? ).to be_false
28
+ end
29
+ end
30
+
31
+ context "when given a block" do
32
+ it "yields Response" do
33
+ ret = ""
34
+ HereOrThere::Local.new.run( 'spec/fixtures/hello_stdout' ) do |response|
35
+ ret = response
36
+ end
37
+
38
+ expect( ret.class ).to eq HereOrThere::Response
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+
3
+ describe HereOrThere::Remote do
4
+
5
+ before :each do
6
+ Net::SSH.stub( start: StubbedSession.new )
7
+ end
8
+
9
+ describe "::session" do
10
+
11
+ before :each do
12
+ if HereOrThere::Remote.send(:instance_variable_defined?, :@_sessions)
13
+ HereOrThere::Remote.send(:remove_instance_variable, :@_sessions)
14
+ end
15
+ end
16
+
17
+ it "creates an SSH object" do
18
+ expect(
19
+ HereOrThere::Remote.session( hostname: 'foo', user: 'bar' ).is_a?(HereOrThere::Remote::SSH)
20
+ ).to be_true
21
+ end
22
+
23
+ it "doesn't recreate an instance" do
24
+ first_instance = HereOrThere::Remote.session( hostname: 'foo', user: 'bar' )
25
+ expect( HereOrThere::Remote.session( hostname: 'foo', user: 'bar' ) ).to eq first_instance
26
+ end
27
+
28
+ it "creates a new instance for uniq options" do
29
+ first_instance = HereOrThere::Remote.session( hostname: 'foo', user: 'bar' )
30
+ expect( HereOrThere::Remote.session( hostname: 'wu', user: 'tang' ) ).not_to eq first_instance
31
+ end
32
+
33
+ end
34
+
35
+ describe HereOrThere::Remote::SSH do
36
+
37
+ before :each do
38
+ @ssh = HereOrThere::Remote::SSH.new( hostname: 'foo', user: 'bar' )
39
+ end
40
+
41
+ describe "#run" do
42
+ it "returns a response object" do
43
+ expect( @ssh.run("foo").is_a? HereOrThere::Response ).to be_true
44
+ end
45
+
46
+ context "when response is stdout" do
47
+
48
+ before :each do
49
+ StubbedSession.any_instance.stub(:exec!).and_yield("foo", :stdout, "hello stdout")
50
+ end
51
+
52
+ it "assigns response_data to Response.stdout" do
53
+ expect( @ssh.run("foo").stdout ).to eq "hello stdout"
54
+ end
55
+
56
+ it "responds with a successful response object" do
57
+ expect( @ssh.run("foo") ).to be_success
58
+ end
59
+
60
+ end
61
+
62
+ context "when response is stderr" do
63
+
64
+ before :each do
65
+ StubbedSession.any_instance.stub(:exec!).and_yield("foo", :stderr, "hello stderr")
66
+ end
67
+
68
+ it "assigns response_data to Response.stderr" do
69
+ expect( @ssh.run("foo").stderr ).to eq "hello stderr"
70
+ end
71
+
72
+ it "responds with an unsuccessful response object" do
73
+ expect( @ssh.run("foo") ).not_to be_success
74
+ end
75
+
76
+ end
77
+
78
+ context "when the block isn't called" do
79
+ # this happens when there is an empty response
80
+ # and no error from the remote
81
+
82
+ before :each do
83
+ StubbedSession.any_instance.stub(:exec!).and_return(nil)
84
+ end
85
+
86
+ it "returns an empty successful response" do
87
+ resp = @ssh.run('foo')
88
+
89
+ expect( resp ).to be_success
90
+ expect( resp.stdout ).to eq ''
91
+ expect( resp.stderr ).to eq ''
92
+ end
93
+ end
94
+
95
+ context "when raises Net::SSH::AuthenticationFailed" do
96
+
97
+ before :each do
98
+ StubbedSession.any_instance.stub(:exec!).and_raise(Net::SSH::AuthenticationFailed)
99
+ end
100
+
101
+ it "returns an unsucessful response with err as stderr" do
102
+ resp = @ssh.run("foo")
103
+ expect( resp ).not_to be_success
104
+ expect( resp.stderr ).to eq "Authentication failed when connecting to remote"
105
+ end
106
+
107
+ it "closes the session" do
108
+ this_session = @ssh.session
109
+ @ssh.run("foo")
110
+ expect( @ssh.session ).not_to eq this_session
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe HereOrThere::Response do
4
+ let(:response) { HereOrThere::Response.new( 'stdout', 'stderr', 'status' ) }
5
+
6
+ describe "#stdout" do
7
+ it "returns the stdout value" do
8
+ expect( response.stdout ).to eq 'stdout'
9
+ end
10
+ end
11
+
12
+ describe "#stderr" do
13
+ it "returns the stderr value" do
14
+ expect( response.stderr ).to eq 'stderr'
15
+ end
16
+ end
17
+
18
+ describe "#status" do
19
+ it "returns the status value" do
20
+ expect( response.status ).to eq 'status'
21
+ end
22
+ end
23
+
24
+ describe "#[]" do
25
+ it "returns stdout for [0]" do
26
+ expect( response[0] ).to eq 'stdout'
27
+ end
28
+ it "returns stderr for [1]" do
29
+ expect( response[1] ).to eq 'stderr'
30
+ end
31
+ it "returns status for [2]" do
32
+ expect( response[2] ).to eq 'status'
33
+ end
34
+ end
35
+
36
+ describe "#success?" do
37
+ context "when status responds to success? (an open3 response)" do
38
+ it "is truthy when process is success" do
39
+ ret_succ = HereOrThere::Local.new.run( 'spec/fixtures/hello_stdout' )
40
+ expect( ret_succ ).to be_success
41
+ end
42
+ it "is falsy when status returns err" do
43
+ ret_err = HereOrThere::Local.new.run( 'spec/fixtures/hello_stderr' )
44
+ expect( ret_err ).not_to be_success
45
+ end
46
+ end
47
+ context "when status is a boolean (a ssh response)" do
48
+ it "is truthy when status is true" do
49
+ expect( HereOrThere::Response.new( 'stdout', 'stderr', true ) ).to be_success
50
+ end
51
+ it "is falsy when status is false" do
52
+ expect( HereOrThere::Response.new( 'stdout', 'stderr', false ) ).not_to be_success
53
+ end
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe HereOrThere do
4
+ include HereOrThere
5
+
6
+ describe "#run_local" do
7
+ it "passes the command to a Local instance" do
8
+ HereOrThere::Local.any_instance.should_receive(:run).with("foo")
9
+
10
+ run_local('foo')
11
+ end
12
+ it "returns a response object" do
13
+ expect( run_local('spec/fixtures/hello_stdout').is_a? HereOrThere::Response ).to be_true
14
+ end
15
+ end
16
+
17
+ describe "#run_remote" do
18
+ it "passes the command to a SSH instance" do
19
+ HereOrThere::Remote::SSH.any_instance.should_receive(:run).with('ls')
20
+
21
+ run_remote( 'ls', hostname: 'foo', user: 'bar' )
22
+ end
23
+ it "returns a response object" do
24
+ HereOrThere::Remote::SSH.any_instance.stub( session: StubbedSession.new )
25
+ expect( run_remote( 'ls', hostname: 'foo', user: 'bar').is_a? HereOrThere::Response ).to be_true
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,60 @@
1
+ # utils
2
+ # nice to have for debugging -- uncomment in gemfile to use
3
+ # require 'pry'
4
+ # require 'byebug'
5
+
6
+ # rspec support
7
+ require 'support/capture'
8
+ require 'support/stubbed_session'
9
+
10
+ # library
11
+ require 'here_or_there'
12
+
13
+ describe "support" do
14
+
15
+ describe Capture do
16
+ describe "#stdout" do
17
+ it "returns a string representation fo what is sent to stdout inside the given block" do
18
+ out = Capture.stdout { $stdout.puts "hello"; $stdout.puts "world" }
19
+ expect( out ).to eq "hello\nworld\n"
20
+ end
21
+ end
22
+
23
+ describe "#stderr" do
24
+ it "returns a string representation fo what is sent to stderr inside the given block" do
25
+ out = Capture.stderr { $stderr.puts "hello"; $stderr.puts "world" }
26
+ expect( out ).to eq "hello\nworld\n"
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+
34
+ describe "fixtures" do
35
+
36
+ describe "hello_stdout" do
37
+ it "puts hello stdout to stdout" do
38
+ stdout, stderr, status = Open3.capture3('spec/fixtures/hello_stdout')
39
+ expect( stdout ).to eq "Hello Stdout\n"
40
+ end
41
+
42
+ it "returns success code" do
43
+ stdout, stderr, status = Open3.capture3('spec/fixtures/hello_stdout')
44
+ expect( status.success? ).to be_true
45
+ end
46
+ end
47
+
48
+ describe "hello_stderr" do
49
+ it "puts hello stderr to stderr" do
50
+ stdout, stderr, status = Open3.capture3('spec/fixtures/hello_stderr')
51
+ expect( stderr ).to eq "Hello Stderr\n"
52
+ end
53
+
54
+ it "returns error code" do
55
+ stdout, stderr, status = Open3.capture3('spec/fixtures/hello_stderr')
56
+ expect( status.success? ).to be_false
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,59 @@
1
+ module Capture
2
+
3
+ class << self
4
+ def stdout &block
5
+ StdOut.new.capture(&block).read
6
+ end
7
+
8
+ def stderr &block
9
+ StdErr.new.capture(&block).read
10
+ end
11
+ end
12
+
13
+ class StdOut
14
+ attr_reader :orig_stdout
15
+ attr_reader :new_stdout
16
+
17
+ def initialize
18
+ @orig_stdout = $stdout
19
+ @new_stdout = StringIO.open('','w+')
20
+ end
21
+
22
+ def capture &block
23
+ $stdout = new_stdout
24
+ yield
25
+ $stdout = orig_stdout
26
+
27
+ return self
28
+ end
29
+
30
+ def read
31
+ new_stdout.rewind
32
+ new_stdout.read
33
+ end
34
+ end
35
+
36
+ class StdErr
37
+ attr_reader :orig_stderr
38
+ attr_reader :new_stderr
39
+
40
+ def initialize
41
+ @orig_stderr = $stderr
42
+ @new_stderr = StringIO.open('','w+')
43
+ end
44
+
45
+ def capture &block
46
+ $stderr = new_stderr
47
+ yield
48
+ $stderr = orig_stderr
49
+
50
+ return self
51
+ end
52
+
53
+ def read
54
+ new_stderr.rewind
55
+ new_stderr.read
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,17 @@
1
+ class StubbedSession
2
+ def initialize
3
+ @closed = false
4
+ end
5
+
6
+ def close
7
+ @closed = true
8
+ end
9
+
10
+ def closed?
11
+ @closed
12
+ end
13
+
14
+ def exec! command, &block
15
+ yield ['foo', :stdout, "woop woop"]
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: here_or_there
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Steven Sloan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: net-ssh
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '2.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '2.6'
27
+ description: A unified interface for running local or remote commands. Provides a
28
+ dependable & identical response from both types of command.
29
+ email:
30
+ - stevenosloan@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - lib/here_or_there/local.rb
36
+ - lib/here_or_there/remote.rb
37
+ - lib/here_or_there/response.rb
38
+ - lib/here_or_there/version.rb
39
+ - lib/here_or_there.rb
40
+ - spec/here_or_there/local_spec.rb
41
+ - spec/here_or_there/remote_spec.rb
42
+ - spec/here_or_there/response_spec.rb
43
+ - spec/here_or_there_spec.rb
44
+ - spec/spec_helper.rb
45
+ - spec/support/capture.rb
46
+ - spec/support/stubbed_session.rb
47
+ homepage: http://github.com/stevenosloan/here_or_there
48
+ licenses:
49
+ - MIT
50
+ metadata: {}
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 2.0.5
68
+ signing_key:
69
+ specification_version: 4
70
+ summary: Unified interface for running local and remote commands
71
+ test_files:
72
+ - spec/here_or_there/local_spec.rb
73
+ - spec/here_or_there/remote_spec.rb
74
+ - spec/here_or_there/response_spec.rb
75
+ - spec/here_or_there_spec.rb
76
+ - spec/spec_helper.rb
77
+ - spec/support/capture.rb
78
+ - spec/support/stubbed_session.rb