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 CHANGED
@@ -13,3 +13,4 @@ doc
13
13
 
14
14
  # jeweler generated
15
15
  pkg
16
+ *.gem
@@ -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 :session
9
+ attr_accessor :attach_command
9
10
  attr_accessor :key_file_path
10
11
 
11
- def initialize(member_keys = {}, session)
12
- self.member_keys = member_keys
13
- self.session = session
14
- self.key_file_path = File.expand_path("~/.ssh/authorized_keys")
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 nil if self.member_keys.values.empty? || self.member_keys.values.map { |k,v| v }.empty?
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
- in_file.puts "command=\"#{command(session, of_type)}\",#{key_options.join(',')} #{key["content"]} #id:#{key["id"]}"
84
- end
85
- in_file.puts ""
86
- end
82
+ command = attach_command[read_only].join(' ').inspect
87
83
 
88
- def command(session, type)
89
- options = ["-S /tmp/pairmill/tmux-#{session.name} attach"]
90
- options << "-r" if type == 'viewer'
84
+ options = ["command=#{command}", *key_options].join(',')
85
+ content = key["content"]
86
+ comment = "# id: #{key["id"]}"
91
87
 
92
- "/usr/local/bin/tmux #{options.join(' ')}"
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, :tmux, :response
5
- private :tunnel=, :authorized_keys_file=, :tmux=, :response=
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
- puts "There was a problem starting the host session %s" % (name && name.inspect)
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 any key to continue..."
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
- if self.response
72
- self.name = response["name"]
73
- self.tunnel = Tunnel.new(self.response["tunnel"].merge(:host => true))
74
- self.authorized_keys_file = AuthorizedKeysFile.new(self.response["member_keys"], self)
94
+ if response
95
+ self.name = response['name']
96
+ true
97
+ else
98
+ false
75
99
  end
76
100
  end
77
101
 
78
- def stop!
79
- tunnel.close
80
- tmux.stop
81
- cleanup_authorized_keys
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
 
@@ -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
- TMP_PATH = "/tmp"
7
+ attr_accessor :session_name
8
+ private :session_name=
7
9
 
8
- attr_accessor :session
9
- private :session=
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 "tmux", *args
16
+ system tmux, *args
17
+ end
27
18
 
28
- at_exit { stop }
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 window(command)
38
- args = %W[
39
- -S #{socket_path}
40
- new-window
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 socket_directory
62
- File.join TMP_PATH, app_path
36
+ def tmux
37
+ @tmux ||= `which tmux`.chomp
63
38
  end
64
39
 
65
40
  def socket_path
66
- File.join socket_directory, socket_name
41
+ File.join(Dir.tmpdir, socket_name)
67
42
  end
68
43
 
69
44
  def socket_name
70
- "tmux-#{session.name}"
45
+ "pair-#{session_name.gsub(/[^\w_-]+/,'')}.tmux"
71
46
  end
72
47
  end
73
48
  end
@@ -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
- at_exit { close }
28
- yield if block_given?
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
- if self.options[:host]
45
- options << "-nq"
46
- options << "-l #{bastion["host_user"]}"
47
- options << "-p #{bastion["ssh_port"]}" unless bastion["ssh_port"] == 22
48
- options << "-R #{port}:localhost:#{host_port}"
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
@@ -1,3 +1,3 @@
1
1
  module Pair
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -2,10 +2,10 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
2
 
3
3
  require 'fileutils'
4
4
 
5
- describe Pairmill::Session::AuthorizedKeysFile do
5
+ describe Pair::Session::AuthorizedKeysFile do
6
6
  let(:member_keys) { FixtureHelper.user_keys }
7
7
  let(:session) { "testing-session" }
8
- let(:subject) { Pairmill::Session::AuthorizedKeysFile.new(member_keys, session) }
8
+ let(:subject) { Pair::Session::AuthorizedKeysFile.new(member_keys, session) }
9
9
 
10
10
  before do
11
11
  subject.stub!(:backup_authorized_keys)
@@ -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 'pairmill'
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.1
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-26 00:00:00.000000000Z
13
+ date: 2011-11-27 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty
17
- requirement: &70280668289560 !ruby/object:Gem::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: *70280668289560
25
+ version_requirements: *70298620262940
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rspec
28
- requirement: &70280668276060 !ruby/object:Gem::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: *70280668276060
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/pairmill/session/authorized_keys_file_spec.rb
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/pairmill/session/authorized_keys_file_spec.rb
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