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 +57 -0
- data/VERSION +1 -1
- data/lib/rest.rb +4 -16
- data/lib/tunnel.rb +60 -5
- data/test/debug.rb +6 -2
- data/test/helper.rb +1 -0
- data/test/test_sauce.rb +33 -16
- metadata +4 -4
- data/README.rdoc +0 -17
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.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
|
-
|
32
|
-
tunnels.
|
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 :
|
6
|
+
attr_accessor :application_address, :username, :access_key, :timeout
|
7
7
|
attr_accessor :status, :owner, :creation_time, :id, :host
|
8
8
|
|
9
|
-
def
|
10
|
-
|
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 =
|
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
|
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
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
|
-
@
|
9
|
-
|
10
|
-
@
|
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.
|
23
|
+
tunnel = @client.tunnels.create('DomainNames' => ["192.168.0.110"])
|
21
24
|
tunnel.refresh!
|
22
25
|
assert_not_nil tunnel
|
23
|
-
assert_equal
|
26
|
+
assert_equal @username, tunnel.owner
|
24
27
|
end
|
25
28
|
|
26
29
|
should "list current tunnels" do
|
27
|
-
@client.
|
28
|
-
@client.
|
29
|
-
@client.
|
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.
|
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.
|
43
|
-
@client.
|
44
|
-
@client.
|
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.
|
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.
|
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.
|
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-
|
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.
|
43
|
+
- README.markdown
|
44
44
|
files:
|
45
45
|
- .document
|
46
46
|
- .gitignore
|
47
47
|
- LICENSE
|
48
|
-
- README.
|
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.
|