pair 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/lib/pair/session/authorized_keys_file.rb +16 -18
- data/lib/pair/session/hosted_session.rb +48 -27
- data/lib/pair/session/tmux.rb +18 -43
- data/lib/pair/session/tunnel.rb +10 -11
- data/lib/pair/version.rb +1 -1
- data/spec/{pairmill → pair}/session/authorized_keys_file_spec.rb +2 -2
- data/spec/spec_helper.rb +3 -3
- metadata +8 -12
- data/spec/pairmill/session/joined_session_spec.rb +0 -16
- data/spec/pairmill/session/session_spec.rb +0 -40
data/.gitignore
CHANGED
@@ -1,21 +1,22 @@
|
|
1
1
|
module Pair
|
2
2
|
class Session
|
3
|
+
# TODO replace with authorized_keys gem
|
3
4
|
class AuthorizedKeysFile
|
4
5
|
ACCESS_TYPE = "type"
|
5
6
|
KEYS = "keys"
|
6
7
|
|
7
8
|
attr_accessor :member_keys
|
8
|
-
attr_accessor :
|
9
|
+
attr_accessor :attach_command
|
9
10
|
attr_accessor :key_file_path
|
10
11
|
|
11
|
-
def initialize(member_keys = {},
|
12
|
-
self.member_keys
|
13
|
-
self.
|
14
|
-
self.key_file_path
|
12
|
+
def initialize(member_keys = {}, attach_command)
|
13
|
+
self.member_keys = member_keys
|
14
|
+
self.attach_command = attach_command
|
15
|
+
self.key_file_path = File.expand_path("~/.ssh/authorized_keys")
|
15
16
|
end
|
16
17
|
|
17
18
|
def install
|
18
|
-
return
|
19
|
+
return if member_keys.empty?
|
19
20
|
|
20
21
|
backup_authorized_keys if key_file_exists?
|
21
22
|
create_authorized_keys
|
@@ -45,10 +46,6 @@ module Pair
|
|
45
46
|
FileUtils.mv(backup_key_file_path, self.key_file_path)
|
46
47
|
end
|
47
48
|
|
48
|
-
def line_numbers_of(key)
|
49
|
-
`grep -ns ".*#{key}.*" #{self.key_file_path} | sed 's/\:.*//'`.split('\n').map(&:strip)
|
50
|
-
end
|
51
|
-
|
52
49
|
def key_file_exists?
|
53
50
|
File.exists? self.key_file_path
|
54
51
|
end
|
@@ -79,17 +76,18 @@ module Pair
|
|
79
76
|
end
|
80
77
|
|
81
78
|
def write_rows_for keys, of_type, in_file
|
79
|
+
read_only = of_type == 'viewer'
|
80
|
+
|
82
81
|
keys.each do |key|
|
83
|
-
|
84
|
-
end
|
85
|
-
in_file.puts ""
|
86
|
-
end
|
82
|
+
command = attach_command[read_only].join(' ').inspect
|
87
83
|
|
88
|
-
|
89
|
-
|
90
|
-
|
84
|
+
options = ["command=#{command}", *key_options].join(',')
|
85
|
+
content = key["content"]
|
86
|
+
comment = "# id: #{key["id"]}"
|
91
87
|
|
92
|
-
|
88
|
+
in_file.puts [options, content, comment].join(' ')
|
89
|
+
end
|
90
|
+
in_file.puts ""
|
93
91
|
end
|
94
92
|
|
95
93
|
def key_options
|
@@ -1,8 +1,16 @@
|
|
1
1
|
module Pair
|
2
2
|
class Session
|
3
|
+
def self.host(options)
|
4
|
+
session = HostedSession.new(options)
|
5
|
+
at_exit { session.stop! }
|
6
|
+
|
7
|
+
session.start!
|
8
|
+
session.stop!
|
9
|
+
end
|
10
|
+
|
3
11
|
class HostedSession < self
|
4
|
-
attr_accessor :tunnel, :authorized_keys_file, :
|
5
|
-
private :tunnel=, :authorized_keys_file=, :
|
12
|
+
attr_accessor :tunnel, :authorized_keys_file, :response
|
13
|
+
private :tunnel=, :authorized_keys_file=, :response=
|
6
14
|
|
7
15
|
def initialize(options = {})
|
8
16
|
puts "Hosting session #{"called #{options[:name].inspect} " if options[:name]}..."
|
@@ -10,7 +18,6 @@ module Pair
|
|
10
18
|
self.name = options.delete(:name)
|
11
19
|
self.viewers = options.delete(:viewers) || []
|
12
20
|
self.participants = options.delete(:participants) || []
|
13
|
-
self.tmux = Tmux.new(self)
|
14
21
|
|
15
22
|
super(options)
|
16
23
|
end
|
@@ -24,25 +31,48 @@ module Pair
|
|
24
31
|
tmux.attach
|
25
32
|
end
|
26
33
|
else
|
27
|
-
|
28
|
-
puts "response: #{response.inspect}" if response && $-d
|
29
|
-
puts ""
|
34
|
+
connection_error!
|
30
35
|
end
|
31
36
|
|
32
37
|
exit
|
33
38
|
end
|
34
39
|
|
40
|
+
def stop!
|
41
|
+
tmux.stop
|
42
|
+
tunnel.close
|
43
|
+
cleanup_authorized_keys
|
44
|
+
rescue
|
45
|
+
raise if $-d
|
46
|
+
end
|
47
|
+
|
48
|
+
def host_login
|
49
|
+
`whoami`.chomp
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def tmux
|
55
|
+
@tmux ||= Tmux.new(name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def tunnel
|
59
|
+
@tunnel ||= Tunnel.new(response["tunnel"])
|
60
|
+
end
|
61
|
+
|
62
|
+
def authorized_keys_file
|
63
|
+
@key_file ||= AuthorizedKeysFile.new(response["member_keys"], tmux.method(:attach_command))
|
64
|
+
end
|
65
|
+
|
35
66
|
def display_startup_message
|
36
67
|
puts "Your pairs can connect to this session using the following command:"
|
37
68
|
puts ""
|
38
69
|
puts " #{connect_command}"
|
39
70
|
puts ""
|
40
|
-
print "Press
|
71
|
+
print "Press [Enter] to continue..."
|
41
72
|
|
42
73
|
gets
|
43
74
|
end
|
44
75
|
|
45
|
-
# TODO: this should get host/user from response
|
46
76
|
def connect_command
|
47
77
|
"ssh-add; ssh -tqA -l#{bastion["join_user"]} #{bastion["host"]} #{name}"
|
48
78
|
end
|
@@ -51,40 +81,31 @@ module Pair
|
|
51
81
|
response["tunnel"]["bastion"]
|
52
82
|
end
|
53
83
|
|
54
|
-
def host_login
|
55
|
-
`whoami`.chomp
|
56
|
-
end
|
57
|
-
|
58
84
|
def cleanup_authorized_keys
|
59
85
|
authorized_keys_file.cleanup
|
60
86
|
end
|
61
87
|
|
62
|
-
private
|
63
|
-
|
64
88
|
def setup
|
65
89
|
create_session_on_server && authorized_keys_file.install
|
66
90
|
end
|
67
91
|
|
68
92
|
def create_session_on_server
|
69
93
|
self.response = Api.create_session(self)["session"]
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
94
|
+
if response
|
95
|
+
self.name = response['name']
|
96
|
+
true
|
97
|
+
else
|
98
|
+
false
|
75
99
|
end
|
76
100
|
end
|
77
101
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
|
102
|
+
def connection_error!
|
103
|
+
puts "There was a problem starting the host session %s" % (name && name.inspect)
|
104
|
+
puts "response: #{response.inspect}" if response && $-d
|
105
|
+
puts ""
|
106
|
+
abort
|
82
107
|
end
|
83
108
|
end
|
84
|
-
|
85
|
-
def self.host(options)
|
86
|
-
HostedSession.new(options).start!
|
87
|
-
end
|
88
109
|
end
|
89
110
|
end
|
90
111
|
|
data/lib/pair/session/tmux.rb
CHANGED
@@ -1,73 +1,48 @@
|
|
1
1
|
require 'fileutils'
|
2
|
+
require 'tmpdir'
|
2
3
|
|
3
4
|
module Pair
|
4
5
|
class Session
|
5
6
|
class Tmux
|
6
|
-
|
7
|
+
attr_accessor :session_name
|
8
|
+
private :session_name=
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def initialize(session)
|
12
|
-
self.session = session
|
13
|
-
create_socket_directory
|
14
|
-
end
|
15
|
-
|
16
|
-
def app_path
|
17
|
-
self.class.to_s.split('::').first.downcase
|
18
|
-
end
|
19
|
-
|
20
|
-
def unique
|
21
|
-
@unique ||= srand.to_s[0,5]
|
10
|
+
def initialize(session_name)
|
11
|
+
self.session_name = session_name
|
22
12
|
end
|
23
13
|
|
24
14
|
def start
|
25
15
|
args = %W[-S #{socket_path} new-session -d]
|
26
|
-
system
|
16
|
+
system tmux, *args
|
17
|
+
end
|
27
18
|
|
28
|
-
|
19
|
+
def attach(read_only = false)
|
20
|
+
system(*attach_command(read_only))
|
29
21
|
end
|
30
22
|
|
31
23
|
def stop
|
32
|
-
`lsof -t #{socket_path}/ | xargs kill -9`
|
24
|
+
`lsof -t #{socket_path}/ 2>/dev/null | xargs kill -9`
|
33
25
|
FileUtils.rm_f(socket_path)
|
34
|
-
self.session.cleanup_authorized_keys if self.session.respond_to?(:cleanup_authorized_keys)
|
35
26
|
end
|
36
27
|
|
37
|
-
def
|
38
|
-
args = %W[
|
39
|
-
|
40
|
-
|
41
|
-
-t #{session.name}:0
|
42
|
-
-n 'Pairing'
|
43
|
-
'ssh pair@bastion.pairmill.com -A'
|
44
|
-
]
|
45
|
-
|
46
|
-
system "tmux", *args
|
47
|
-
end
|
48
|
-
|
49
|
-
def attach(read_only = false)
|
50
|
-
args = %W[-S #{socket_path} attach]
|
51
|
-
args += " -r" if read_only
|
52
|
-
|
53
|
-
system "tmux", *args
|
28
|
+
def attach_command(read_only = false)
|
29
|
+
args = %W[#{tmux} -S #{socket_path} attach]
|
30
|
+
args << " -r" if read_only
|
31
|
+
args
|
54
32
|
end
|
55
33
|
|
56
34
|
private
|
57
|
-
def create_socket_directory
|
58
|
-
FileUtils.mkdir_p(socket_directory, :mode => 0700)
|
59
|
-
end
|
60
35
|
|
61
|
-
def
|
62
|
-
|
36
|
+
def tmux
|
37
|
+
@tmux ||= `which tmux`.chomp
|
63
38
|
end
|
64
39
|
|
65
40
|
def socket_path
|
66
|
-
File.join
|
41
|
+
File.join(Dir.tmpdir, socket_name)
|
67
42
|
end
|
68
43
|
|
69
44
|
def socket_name
|
70
|
-
"
|
45
|
+
"pair-#{session_name.gsub(/[^\w_-]+/,'')}.tmux"
|
71
46
|
end
|
72
47
|
end
|
73
48
|
end
|
data/lib/pair/session/tunnel.rb
CHANGED
@@ -24,8 +24,11 @@ module Pair
|
|
24
24
|
def open
|
25
25
|
self.tunnel = IO.popen(ssh_command)
|
26
26
|
puts "SSH tunnel started (PID = #{tunnel.pid})" if $-d
|
27
|
-
|
28
|
-
|
27
|
+
|
28
|
+
if block_given?
|
29
|
+
yield
|
30
|
+
close
|
31
|
+
end
|
29
32
|
end
|
30
33
|
|
31
34
|
def close
|
@@ -41,15 +44,11 @@ module Pair
|
|
41
44
|
|
42
45
|
def ssh_command
|
43
46
|
options = []
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
else
|
50
|
-
options << "-tAq"
|
51
|
-
options << "-l #{bastion["join_user"]}"
|
52
|
-
end
|
47
|
+
|
48
|
+
options << "-nqT" # Run no command, be quiet, don't allocate pseudo-terminal
|
49
|
+
options << "-l #{bastion["host_user"]}"
|
50
|
+
options << "-p #{bastion["ssh_port"]}" unless bastion["ssh_port"] == 22
|
51
|
+
options << "-R #{port}:localhost:#{host_port}"
|
53
52
|
|
54
53
|
"ssh #{bastion["host"]} #{options.join(" ")}"
|
55
54
|
end
|
data/lib/pair/version.rb
CHANGED
@@ -2,10 +2,10 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
|
2
2
|
|
3
3
|
require 'fileutils'
|
4
4
|
|
5
|
-
describe
|
5
|
+
describe Pair::Session::AuthorizedKeysFile do
|
6
6
|
let(:member_keys) { FixtureHelper.user_keys }
|
7
7
|
let(:session) { "testing-session" }
|
8
|
-
let(:subject) {
|
8
|
+
let(:subject) { Pair::Session::AuthorizedKeysFile.new(member_keys, session) }
|
9
9
|
|
10
10
|
before do
|
11
11
|
subject.stub!(:backup_authorized_keys)
|
data/spec/spec_helper.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
2
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
3
4
|
require 'rspec'
|
4
5
|
require 'json'
|
5
6
|
require 'fixture_helper'
|
6
|
-
require '
|
7
|
-
require 'pairmill/session'
|
7
|
+
require 'pair'
|
8
8
|
|
9
9
|
# Requires supporting files with custom matchers and macros, etc,
|
10
10
|
# in ./support/ and its subdirectories.
|
11
11
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
12
12
|
|
13
13
|
RSpec.configure do |config|
|
14
|
-
|
14
|
+
|
15
15
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pair
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2011-11-
|
13
|
+
date: 2011-11-27 00:00:00.000000000Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: httparty
|
17
|
-
requirement: &
|
17
|
+
requirement: &70298620262940 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: 0.8.1
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70298620262940
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: rspec
|
28
|
-
requirement: &
|
28
|
+
requirement: &70298620258860 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
version: 2.3.0
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70298620258860
|
37
37
|
description: Effortless remote pairing
|
38
38
|
email:
|
39
39
|
- me@bjeanes.com
|
@@ -66,9 +66,7 @@ files:
|
|
66
66
|
- spec/fixtures/api_hosted_session.yml
|
67
67
|
- spec/fixtures/api_joined_session.yml
|
68
68
|
- spec/fixtures/user_keys.yml
|
69
|
-
- spec/
|
70
|
-
- spec/pairmill/session/joined_session_spec.rb
|
71
|
-
- spec/pairmill/session/session_spec.rb
|
69
|
+
- spec/pair/session/authorized_keys_file_spec.rb
|
72
70
|
- spec/spec_helper.rb
|
73
71
|
homepage: http://www.pairmill.com
|
74
72
|
licenses: []
|
@@ -99,7 +97,5 @@ test_files:
|
|
99
97
|
- spec/fixtures/api_hosted_session.yml
|
100
98
|
- spec/fixtures/api_joined_session.yml
|
101
99
|
- spec/fixtures/user_keys.yml
|
102
|
-
- spec/
|
103
|
-
- spec/pairmill/session/joined_session_spec.rb
|
104
|
-
- spec/pairmill/session/session_spec.rb
|
100
|
+
- spec/pair/session/authorized_keys_file_spec.rb
|
105
101
|
- spec/spec_helper.rb
|
@@ -1,16 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
-
|
3
|
-
describe Pairmill::Session::JoinedSession do
|
4
|
-
let(:host) { "chad.pry@gmail.com" }
|
5
|
-
let(:name) { "testing-conference" }
|
6
|
-
|
7
|
-
describe "when instantiating an object" do
|
8
|
-
it "sets the host" do
|
9
|
-
Pairmill::Session::JoinedSession.new(host, name).host.should == host
|
10
|
-
end
|
11
|
-
|
12
|
-
it "sets the name" do
|
13
|
-
Pairmill::Session::JoinedSession.new(host, name).name.should == name
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
-
|
3
|
-
describe Pairmill::Session do
|
4
|
-
let(:host) { "chad.pry@gmail.com" }
|
5
|
-
let(:name) { "testing-conference" }
|
6
|
-
|
7
|
-
# describe "when joining a session" do
|
8
|
-
# describe "the returned session" do
|
9
|
-
# let(:joined_session) { Pairmill::Session::JoinedSession.new(host, name) }
|
10
|
-
#
|
11
|
-
# before do
|
12
|
-
# Pairmill::Session::JoinedSession.stub!(:new).and_return(joined_session)
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# it "starts a joined session" do
|
16
|
-
# joined_session.should_receive(:start!)
|
17
|
-
# Pairmill::Session.join(:host => host, :session_name => name)
|
18
|
-
# end
|
19
|
-
# end
|
20
|
-
# end
|
21
|
-
|
22
|
-
describe "starting a session" do
|
23
|
-
let(:session_json) { FixtureHelper.api_joined_session }
|
24
|
-
|
25
|
-
before do
|
26
|
-
Pairmill::Api.stub!(:join_session).and_return(session_json)
|
27
|
-
end
|
28
|
-
|
29
|
-
it "fetches a session from the api" do
|
30
|
-
session = Pairmill::Session::JoinedSession.new(host, name)
|
31
|
-
session.should_receive(:fetch_session_details)
|
32
|
-
session.start!
|
33
|
-
end
|
34
|
-
|
35
|
-
it "returns a joined session instance" do
|
36
|
-
session = Pairmill::Session::JoinedSession.new(host, name)
|
37
|
-
session.should be_a(Pairmill::Session::JoinedSession)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|