sauce 0.2.0 → 0.2.1

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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
data/lib/rest.rb CHANGED
@@ -7,12 +7,13 @@ module Sauce
7
7
  class BadAccessError < StandardError; end #nodoc
8
8
  class MisconfiguredError < StandardError; end #nodoc
9
9
 
10
- attr_accessor :username, :access_key, :client, :api_url
10
+ attr_accessor :username, :access_key, :client, :ip, :api_url
11
11
  attr_accessor :tunnels
12
12
 
13
13
  def initialize(options)
14
14
  @username = options[:username]
15
15
  @access_key = options[:access_key]
16
+ @ip = options[:ip]
16
17
 
17
18
  raise MisconfiguredError if @username.nil? or @access_key.nil?
18
19
  @api_url = "https://#{@username}:#{@access_key}@saucelabs.com/rest/#{@username}/"
@@ -20,6 +21,9 @@ module Sauce
20
21
 
21
22
  @tunnels = Sauce::Tunnel
22
23
  @tunnels.client = @client
24
+ @tunnels.account = {:username => @username,
25
+ :access_key => @access_key,
26
+ :ip => @ip}
23
27
  end
24
28
  end
25
29
  end
data/lib/sauce.rb CHANGED
@@ -1,2 +1,3 @@
1
- require 'rest'
2
1
  require 'tunnel'
2
+ require 'rest'
3
+
data/lib/tunnel.rb CHANGED
@@ -1,19 +1,20 @@
1
+ require 'net/telnet'
2
+ require 'net/ssh'
3
+ require 'net/ssh/gateway'
4
+ require 'gateway_ext'
5
+
1
6
  module Sauce
2
7
  # Interact with a Sauce Labs tunnel as if it were a ruby object
3
8
  class Tunnel
4
9
  class NoIDError < StandardError; end #nodoc
5
10
 
6
- attr_accessor :application_address, :username, :access_key, :timeout
7
- attr_accessor :status, :owner, :creation_time, :id, :host
11
+ #{"Status": "running", "CreationTime": 1266545716, "ModificationTime": 1266545793, "Host": "ec2-184-73-16-13.compute-1.amazonaws.com", "LaunchTime": 1266545726, "Owner": "sgrove", "_id": "1090b4b0b50477cf40fc44c146b408a4", "Type": "tunnel", "id": "1090b4b0b50477cf40fc44c146b408a4", "DomainNames": ["sgrove.tst"]}
8
12
 
9
- def self.create(options)
10
- response = JSON.parse @@client[:tunnels].post(options.to_json, :content_type => 'application/json')
11
- Tunnel.new response
12
- end
13
-
14
- def initialize(options)
15
- build!(options)
16
- end
13
+ attr_accessor :owner, :access_key, :status
14
+ attr_accessor :id, :host, :domain_names, :timeout
15
+ attr_accessor :launch_time, :created_at, :updated_at
16
+
17
+ attr_accessor :application_address, :gateway, :port, :thread
17
18
 
18
19
  # Get the class @@client.
19
20
  # TODO: Consider metaprogramming this away
@@ -27,6 +28,18 @@ module Sauce
27
28
  @@client = client
28
29
  end
29
30
 
31
+ # Get the class @@client.
32
+ # TODO: Consider metaprogramming this away
33
+ def self.account
34
+ @@account
35
+ end
36
+
37
+ # Set the class @@client.
38
+ # TODO: Consider metaprogramming this away
39
+ def self.account=(account)
40
+ @@account = account
41
+ end
42
+
30
43
  def self.first
31
44
  self.all.first
32
45
  end
@@ -44,8 +57,21 @@ module Sauce
44
57
  self.all.each { |tunnel| tunnel.destroy }
45
58
  end
46
59
 
60
+ # Creates a new tunnel machine
61
+ def self.create(options)
62
+ response = JSON.parse @@client[:tunnels].post(options.to_json, :content_type => 'application/json')
63
+ #puts response.inspect
64
+ Tunnel.new response
65
+ end
66
+
67
+ # Creates an instance representing a machine, but does not actually create the machine. See #create for that.
68
+ def initialize(options)
69
+ build!(options)
70
+ end
71
+
47
72
  # Hits the destroy url for this tunnel, and then refreshes. Keep in mind it takes some time to completely teardown a tunnel.
48
73
  def destroy
74
+ close_gateway
49
75
  response = @@client["tunnels/#{@id}"].delete
50
76
  refresh!
51
77
  end
@@ -53,11 +79,47 @@ module Sauce
53
79
  # Retrieves the latest information on this tunnel from the Sauce Labs' server
54
80
  def refresh!
55
81
  response = JSON.parse @@client["tunnels/#{@id}"].get
82
+ #puts "\Tunnel refresh with: #{response.inspect}"
56
83
  build! response
84
+ self
85
+ end
86
+
87
+ # Shortcut method to find out if a tunnel is marked as dead
88
+ def preparing?
89
+ refresh!
90
+ status == "new" or status == "booting"
91
+ end
92
+
93
+ # Shortcut method to find out if a tunnel is marked as dead
94
+ def halting?
95
+ refresh!
96
+ status == "halting"
97
+ end
98
+
99
+ # Shortcut method to find out if a tunnel is marked as dead
100
+ def terminated?
101
+ refresh!
102
+ status == "terminated"
103
+ end
104
+
105
+ # A tunnel is healthy if 1.) its status is "running" and 2.) It says hello
106
+ def healthy?
107
+ # TODO: Implement 3.) We can reach through and touch a service on the reverse end of the tunnel
108
+ refresh!
109
+ =begin
110
+ puts "\tRunning? #{self.status == 'running'}"
111
+ puts "\tSays hello? #{self.says_hello?}"
112
+ puts "\tThread running? #{self.still_running?}"
113
+ =end
114
+ return true if not self.terminated? and self.status == "running" and self.says_hello? and self.still_running?
115
+ return false
57
116
  end
58
117
 
59
- # Sauce Labs' server will say hello on port 1025 as a sanity check. If no hello, something is wrong. TODO: Make it say hello on port 1025. Currently a hack.
118
+ # Sauce Labs' server will say hello on port 1025 as a sanity check. If no hello, something is wrong.
119
+ # TODO: Make it say hello on port 1025. Currently a hack.
60
120
  def says_hello?(options={})
121
+ return false unless self.status == "running"
122
+
61
123
  # TODO: Read from options if necessary
62
124
  connection = {}
63
125
  connection[:host] = @host
@@ -66,6 +128,8 @@ module Sauce
66
128
  connection[:telnet_mode] = true
67
129
  connection[:timeout] = 10
68
130
 
131
+ #puts "telnet: #{connection.inspect}"
132
+
69
133
  host = Net::Telnet::new("Host" => connection[:host],
70
134
  "Port" => connection[:port],
71
135
  "Prompt" => connection[:prompt],
@@ -78,15 +142,77 @@ module Sauce
78
142
  line[0,prefix.length] == prefix
79
143
  end
80
144
 
145
+ # Debug method
146
+ def mini_repair
147
+ refresh!
148
+ if not self.terminated? and self.status == "running" and self.says_hello?
149
+ if not self.still_running?
150
+ open_gateway
151
+ return true if self.still_running?
152
+ end
153
+ end
154
+ return false
155
+ end
156
+
157
+ # Opens a reverse ssh tunnel to the tunnel machine
158
+ def open_gateway
159
+ # TODO: Make ports configurable
160
+
161
+ @gateway = Net::SSH::Gateway.new(host, owner, {:password => @@account[:access_key]})
162
+
163
+ # Notes for anyone looking at this method
164
+ # gateway.open_remote(3000, # Port where your local application is running. Usually 3000 for rails dev
165
+ # "localhost", # Hostname/local ip your rails app is running on. Usually "localhost" for rails dev
166
+ # 80, # This is the port to run your selenium tests against, i.e. :url => "http://<some_host>:80"
167
+ # # <some_host> is the DomainNames you started the tunnel machine with.
168
+ # "0.0.0.0") # Do not change this unless your god has commanded you to do so. I'm serious.
169
+ port = 3000
170
+ local_host = "localhost"
171
+ remote_port = 80
172
+ #puts "This is the experimental non-blocking gateway_open. Well done."
173
+ #puts "gateway.open_remote(#{port}, #{local_host}, #{remote_port}, 0.0.0.0)"
174
+ #gateway.open_remote(pair[0], local_host, pair[1], "0.0.0.0") do |rp, rh|
175
+
176
+ @thread = Thread.new(host, port, remote_port, owner, @@account[:access_key]) do
177
+ Thread.pass
178
+ @gateway.open_remote(3000,
179
+ "localhost",
180
+ 80,
181
+ "0.0.0.0") do |rp, rh|
182
+ while true do
183
+ sleep 10
184
+ end
185
+ end
186
+ end
187
+
188
+ @thread.run
189
+ end
190
+
191
+ # Closes the reverse ssh tunnel if open
192
+ def close_gateway
193
+ @thread.kill if @thread
194
+ end
195
+
196
+ # Returns true if this tunnel has a thread that needs to be monitored
197
+ def still_running?
198
+ #puts "\t\t#{@thread.inspect}.nil?"
199
+ not @thread.nil?
200
+ end
201
+
81
202
  protected
82
203
 
83
204
  # Sets all internal variables from a hash
84
205
  def build!(options)
206
+ #puts "\tBuild with #{options.inspect}"
85
207
  @status = options["Status"]
86
- @owner = options["Owner"]
87
- @id = options["_id"]
88
- @id = options["id"] if @id.nil? or @id.empty?
89
- @host = options["Host"]
208
+ @owner = options["Owner"]
209
+ @id = options["_id"]
210
+ @id = options["id"] if @id.nil? or @id.empty?
211
+ @host = options["Host"]
212
+ @created_at = options["CreationTime"]
213
+ @updated_at = options["ModificationTime"]
214
+ @launch_time = options["LaunchTime"]
215
+ @domain_names = options["DomainNames"]
90
216
 
91
217
  raise NoIDError if @id.nil? or @id.empty?
92
218
  end
data/test/debug.rb CHANGED
@@ -1,11 +1,34 @@
1
- require 'helper'
1
+ require 'irb_boot'
2
2
 
3
3
  c = Sauce::Client.new(:username => "sgrove",
4
- :access_key => "4c592ce3-8f45-4cd6-8e3e-65b9f0b173d0")
4
+ :access_key => "733a44e2-4226-488f-8126-6f6d59bdf386",
5
+ :ip => "67.188.3.10")
6
+
7
+ =begin
8
+ t = c.tunnels.first
5
9
 
6
10
  c.destroy_all_tunnels
7
11
  t = c.create_tunnel('DomainNames' => ["111.111.111.111"])
8
12
 
9
13
  puts c.tunnels.inspect
14
+ =end
10
15
 
11
- c.destroy_all_tunnels
16
+ t = c.tunnels.first
17
+
18
+ if t.nil?
19
+ puts "Starting a new tunnel"
20
+ t = c.tunnels.create("DomainNames" => ["sgrove.tst"])
21
+ until t.status == "running"
22
+ print "." ; STDOUT.flush
23
+ t.refresh!
24
+ sleep 1
25
+ end
26
+ end
27
+
28
+ puts t.inspect
29
+ t.open_gateway
30
+
31
+ puts "Checking to see if thread is running."
32
+ puts t.still_running?
33
+ puts t.thread.inspect
34
+ t.thread.join if t.still_running?
data/test/irb_boot.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'net/telnet'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+
7
+ require 'sauce'
data/test/test_sauce.rb CHANGED
@@ -65,6 +65,15 @@ class TestSauce < Test::Unit::TestCase
65
65
  end
66
66
 
67
67
  assert_equal true, tunnel.says_hello?
68
+
69
+ tunnel.destroy # cleanup
70
+ end
71
+
72
+ should "not attempt to telnet if status is not running" do
73
+ tunnel = @client.tunnels.create('DomainNames' => [@ip])
74
+
75
+ tunnel.status = "booting"
76
+ assert_equal false, tunnel.says_hello?
68
77
  end
69
78
 
70
79
  def teardown
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sauce
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Grove
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-18 00:00:00 -08:00
12
+ date: 2010-02-22 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -84,4 +84,5 @@ summary: Ruby access to Sauce Labs' features
84
84
  test_files:
85
85
  - test/debug.rb
86
86
  - test/helper.rb
87
+ - test/irb_boot.rb
87
88
  - test/test_sauce.rb