sauce 0.1.0 → 0.2.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.
data/README.markdown ADDED
@@ -0,0 +1,57 @@
1
+ sauce
2
+ =====
3
+
4
+ Ruby access to SauceLab's features
5
+
6
+ Features
7
+ --------
8
+ Current:
9
+
10
+ * Treat tunnels like ruby objects. Find/create/destroy
11
+
12
+ Planned:
13
+
14
+ * ActiveRecord-like interface for video recordings, jobs logs, reverse tunnels
15
+ * Start/stop local instances of Sauce RC
16
+
17
+ Install
18
+ -------
19
+ Make sure you're pulling in gems from gemcutter, and then:
20
+
21
+ `gem install sauce`
22
+
23
+ Example
24
+ -------
25
+ Here's how to start a tunnel using the Sauce gem
26
+ require 'rubygems'
27
+ require 'sauce'
28
+
29
+ account = YAML.load_file "live_account.yml"
30
+ client = Sauce::Client.new(:username => account["username"],
31
+ :access_key => account["access_key"])
32
+ tunnel = client.create_tunnel('DomainNames' => ["123.456.789.123"])
33
+ tunnel.refresh!
34
+
35
+ client.tunnels # => [#<Sauce::Tunnel:0x1250e6c
36
+ @host=nil,
37
+ @status="running",
38
+ @id="389a1c2a3d49861474c512e9a904be9e",
39
+ @client=#<RestClient::Resource:0x1257fb4
40
+ @block=nil,
41
+ @url="https://username:access_key@saucelabs.com/rest/username/",
42
+ @options={}>,
43
+ @owner="username">]
44
+
45
+ tunnel.destroy # or client.destroy_all_tunnels
46
+
47
+ Note on Patches/Pull Requests
48
+ -----------------------------
49
+ * Fork the project.
50
+ * Make your feature addition or bug fix.
51
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
52
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
53
+ * Send me a pull request. Bonus points for topic branches.
54
+
55
+ Copyright
56
+ ---------
57
+ Copyright (c) 2010 Sauce Labs Inc. See LICENSE for details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
data/lib/rest.rb CHANGED
@@ -2,11 +2,13 @@ require 'rest_client'
2
2
  require 'json'
3
3
 
4
4
  module Sauce
5
+ # The module that brokers most communication with Sauce Labs' REST API
5
6
  class Client
6
7
  class BadAccessError < StandardError; end #nodoc
7
8
  class MisconfiguredError < StandardError; end #nodoc
8
9
 
9
10
  attr_accessor :username, :access_key, :client, :api_url
11
+ attr_accessor :tunnels
10
12
 
11
13
  def initialize(options)
12
14
  @username = options[:username]
@@ -15,23 +17,9 @@ module Sauce
15
17
  raise MisconfiguredError if @username.nil? or @access_key.nil?
16
18
  @api_url = "https://#{@username}:#{@access_key}@saucelabs.com/rest/#{@username}/"
17
19
  @client = RestClient::Resource.new @api_url
18
- end
19
-
20
- def create_tunnel(options)
21
- response = JSON.parse @client[:tunnels].post(options.to_json, :content_type => 'application/json')
22
- raise Sauce::Client::BadAccessError unless response["error"].nil?
23
- Sauce::Tunnel.new(@client, response)
24
- end
25
-
26
- def tunnels(options = {})
27
- response = JSON.parse @client[:tunnels].get
28
- return response.collect{|r| Sauce::Tunnel.new(@client, r)}
29
- end
30
20
 
31
- def destroy_all_tunnels(options={})
32
- tunnels.each do |t|
33
- t.destroy
34
- end
21
+ @tunnels = Sauce::Tunnel
22
+ @tunnels.client = @client
35
23
  end
36
24
  end
37
25
  end
data/lib/tunnel.rb CHANGED
@@ -3,26 +3,81 @@ module Sauce
3
3
  class Tunnel
4
4
  class NoIDError < StandardError; end #nodoc
5
5
 
6
- attr_accessor :client, :application_address, :username, :access_key, :timeout
6
+ attr_accessor :application_address, :username, :access_key, :timeout
7
7
  attr_accessor :status, :owner, :creation_time, :id, :host
8
8
 
9
- def initialize(client, options)
10
- @client = client
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)
11
15
  build!(options)
12
16
  end
13
17
 
18
+ # Get the class @@client.
19
+ # TODO: Consider metaprogramming this away
20
+ def self.client
21
+ @@client
22
+ end
23
+
24
+ # Set the class @@client.
25
+ # TODO: Consider metaprogramming this away
26
+ def self.client=(client)
27
+ @@client = client
28
+ end
29
+
30
+ def self.first
31
+ self.all.first
32
+ end
33
+
34
+ def self.last
35
+ self.all.last
36
+ end
37
+
38
+ def self.all
39
+ responses = JSON.parse @@client[:tunnels].get
40
+ return responses.collect{|response| Sauce::Tunnel.new(response)}
41
+ end
42
+
43
+ def self.destroy
44
+ self.all.each { |tunnel| tunnel.destroy }
45
+ end
46
+
14
47
  # Hits the destroy url for this tunnel, and then refreshes. Keep in mind it takes some time to completely teardown a tunnel.
15
48
  def destroy
16
- response = @client["tunnels/#{@id}"].delete
49
+ response = @@client["tunnels/#{@id}"].delete
17
50
  refresh!
18
51
  end
19
52
 
20
53
  # Retrieves the latest information on this tunnel from the Sauce Labs' server
21
54
  def refresh!
22
- response = JSON.parse @client["tunnels/#{@id}"].get
55
+ response = JSON.parse @@client["tunnels/#{@id}"].get
23
56
  build! response
24
57
  end
25
58
 
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.
60
+ def says_hello?(options={})
61
+ # TODO: Read from options if necessary
62
+ connection = {}
63
+ connection[:host] = @host
64
+ connection[:port] = 22
65
+ connection[:prompt] = /[$%#>] \z/n
66
+ connection[:telnet_mode] = true
67
+ connection[:timeout] = 10
68
+
69
+ host = Net::Telnet::new("Host" => connection[:host],
70
+ "Port" => connection[:port],
71
+ "Prompt" => connection[:prompt],
72
+ "Telnetmode" => connection[:telnet_mode],
73
+ "Timeout" => connection[:timeout])
74
+ line = host.lines.first.chomp
75
+
76
+ # Temporary workaround port 1025 problem
77
+ prefix = "SSH-2.0-Twisted"
78
+ line[0,prefix.length] == prefix
79
+ end
80
+
26
81
  protected
27
82
 
28
83
  # Sets all internal variables from a hash
data/test/debug.rb CHANGED
@@ -1,7 +1,11 @@
1
1
  require 'helper'
2
2
 
3
- require 'helper'
4
-
5
3
  c = Sauce::Client.new(:username => "sgrove",
6
4
  :access_key => "4c592ce3-8f45-4cd6-8e3e-65b9f0b173d0")
5
+
6
+ c.destroy_all_tunnels
7
7
  t = c.create_tunnel('DomainNames' => ["111.111.111.111"])
8
+
9
+ puts c.tunnels.inspect
10
+
11
+ c.destroy_all_tunnels
data/test/helper.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
3
  require 'shoulda'
4
+ require 'net/telnet'
4
5
 
5
6
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
data/test/test_sauce.rb CHANGED
@@ -5,9 +5,12 @@ class TestSauce < Test::Unit::TestCase
5
5
  setup do
6
6
  # Create this file and put in your details to run the tests
7
7
  account = YAML.load_file "live_account.yml"
8
- @client = Sauce::Client.new(:username => account["username"],
9
- :access_key => account["access_key"])
10
- @client.destroy_all_tunnels
8
+ @username = account["username"]
9
+ @access_key = account["access_key"]
10
+ @ip = account["ip"]
11
+ @client = Sauce::Client.new(:username => @username,
12
+ :access_key => @access_key)
13
+ @client.tunnels.destroy
11
14
  end
12
15
 
13
16
  should "initialize with passed variables" do
@@ -17,41 +20,55 @@ class TestSauce < Test::Unit::TestCase
17
20
  end
18
21
 
19
22
  should "create a tunnel with the current user" do
20
- tunnel = @client.create_tunnel('DomainNames' => ["123.456.789.123"])
23
+ tunnel = @client.tunnels.create('DomainNames' => ["192.168.0.110"])
21
24
  tunnel.refresh!
22
25
  assert_not_nil tunnel
23
- assert_equal "sgrove", tunnel.owner
26
+ assert_equal @username, tunnel.owner
24
27
  end
25
28
 
26
29
  should "list current tunnels" do
27
- @client.create_tunnel('DomainNames' => ["111.111.111.111"])
28
- @client.create_tunnel('DomainNames' => ["111.111.111.112"])
29
- @client.create_tunnel('DomainNames' => ["111.111.111.113"])
30
+ @client.tunnels.create('DomainNames' => ["192.168.0.111"])
31
+ @client.tunnels.create('DomainNames' => ["192.168.0.112"])
32
+ @client.tunnels.create('DomainNames' => ["192.168.0.113"])
30
33
 
31
- tunnels = @client.tunnels
34
+ tunnels = @client.tunnels.all
32
35
  assert_equal 3, tunnels.select{|t| t.status != "halting"}.count
33
36
  end
34
37
 
35
38
  should "destroy a tunnel" do
36
- tunnel = @client.create_tunnel('DomainNames' => ["111.111.111.114"])
39
+ tunnel = @client.tunnels.create('DomainNames' => ["192.168.0.114"])
37
40
  tunnel.destroy
38
41
  assert_equal "halting", tunnel.status
39
42
  end
40
43
 
41
44
  should "destroy all tunnels" do
42
- @client.create_tunnel('DomainNames' => ["111.111.111.115"])
43
- @client.create_tunnel('DomainNames' => ["111.111.111.116"])
44
- @client.create_tunnel('DomainNames' => ["111.111.111.117"])
45
+ tunnel = @client.tunnels.create('DomainNames' => ["192.168.0.115"])
46
+ tunnel = @client.tunnels.create('DomainNames' => ["192.168.0.116"])
47
+ tunnel = @client.tunnels.create('DomainNames' => ["192.168.0.117"])
45
48
 
46
- @client.destroy_all_tunnels
49
+ @client.tunnels.destroy
47
50
 
48
- @client.tunnels.each do |tunnel|
51
+ @client.tunnels.all.each do |tunnel|
49
52
  assert_equal "halting", tunnel.status
50
53
  end
51
54
  end
52
55
 
56
+ should "say hello on port 1025 if healthy" do
57
+ tunnel = @client.tunnels.create('DomainNames' => [@ip])
58
+
59
+ max_retries = 30
60
+ retries = 0
61
+ until tunnel.status == "running" or retries >= max_retries
62
+ sleep 5
63
+ retries += 1
64
+ tunnel.refresh!
65
+ end
66
+
67
+ assert_equal true, tunnel.says_hello?
68
+ end
69
+
53
70
  def teardown
54
- @client.destroy_all_tunnels
71
+ @client.tunnels.destroy
55
72
  end
56
73
  end
57
74
  end
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.1.0
4
+ version: 0.2.0
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-16 00:00:00 -08:00
12
+ date: 2010-02-18 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -40,12 +40,12 @@ extensions: []
40
40
 
41
41
  extra_rdoc_files:
42
42
  - LICENSE
43
- - README.rdoc
43
+ - README.markdown
44
44
  files:
45
45
  - .document
46
46
  - .gitignore
47
47
  - LICENSE
48
- - README.rdoc
48
+ - README.markdown
49
49
  - Rakefile
50
50
  - VERSION
51
51
  - lib/rest.rb
data/README.rdoc DELETED
@@ -1,17 +0,0 @@
1
- = sauce
2
-
3
- Ruby access to SauceLab's features
4
-
5
- == Note on Patches/Pull Requests
6
-
7
- * Fork the project.
8
- * Make your feature addition or bug fix.
9
- * Add tests for it. This is important so I don't break it in a
10
- future version unintentionally.
11
- * Commit, do not mess with rakefile, version, or history.
12
- (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
- * Send me a pull request. Bonus points for topic branches.
14
-
15
- == Copyright
16
-
17
- Copyright (c) 2010 Sauce Labs Inc. See LICENSE for details.