here_or_there 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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