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 +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.
|