sauce 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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.