sauce_connect 3.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/sauce/connect.rb +242 -0
  3. metadata +70 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b56c2f924091bac3a7f5aca719edd393812043d6
4
+ data.tar.gz: e50ee142b53516ce21bf448a12981241b484e345
5
+ SHA512:
6
+ metadata.gz: a2dbef3999ab736761308dd400f22ee5fe0f2ef67c2cd9e6dbf19f3ffff7c363119d2aa9560beba4bc302621f348e7364db2dbb21cf20cc0b8b865ad7118e066
7
+ data.tar.gz: 43e4d418185295b57f69a84806a69c768c6ff762f04d528e9550299cb8651ec8c0351007bebe9e4c981e479e8ed0a6454519285e95c600a846dddccea370e9af
@@ -0,0 +1,242 @@
1
+ require 'sauce/config'
2
+ require 'net/http'
3
+ require 'socket'
4
+
5
+ module Sauce
6
+ class Connect
7
+ class TunnelNotPossibleException < StandardError
8
+ end
9
+
10
+ TIMEOUT = 90
11
+
12
+ attr_reader :status, :error
13
+
14
+ def initialize(options={})
15
+ @ready = false
16
+ @status = "uninitialized"
17
+ @error = nil
18
+ @quiet = options[:quiet]
19
+ @timeout = options.fetch(:timeout) { TIMEOUT }
20
+ @config = Sauce::Config.new(options)
21
+ @skip_connection_test = @config[:skip_connection_test]
22
+ @cli_options = @config[:connect_options]
23
+ @sc4_executable = @config[:sauce_connect_4_executable]
24
+
25
+ if @config.username.nil?
26
+ raise ArgumentError, "Username required to launch Sauce Connect. Please set the environment variable $SAUCE_USERNAME"
27
+ end
28
+
29
+ if @config.access_key.nil?
30
+ raise ArgumentError, "Access key required to launch Sauce Connect. Please set the environment variable $SAUCE_ACCESS_KEY"
31
+ end
32
+
33
+ if @sc4_executable.nil?
34
+ raise TunnelNotPossibleException, Sauce::Connect.plzGetSC4
35
+ end
36
+ end
37
+
38
+ def ensure_connection_is_possible
39
+ $stderr.puts "[Checking REST API is contactable...]" unless @quiet
40
+ uri = URI("http://saucelabs.com/rest/v1/#{@config[:username]}/tunnels")
41
+
42
+ response = Net::HTTP.start(uri.host, uri.port) do |http|
43
+ request = Net::HTTP::Get.new uri.request_uri
44
+ request.basic_auth(@config[:username], @config[:access_key])
45
+ response = http.request request
46
+ end
47
+
48
+ unless response.kind_of? Net::HTTPOK
49
+ $stderr.puts Sauce::Connect.cant_access_rest_api_message
50
+ raise TunnelNotPossibleException, "Couldn't access REST API"
51
+ end
52
+
53
+ begin
54
+ $stderr.puts "[Checking port 443 is open...]" unless @quiet
55
+ socket = TCPSocket.new 'saucelabs.com', 443
56
+ rescue SystemCallError => e
57
+ raise e unless e.class.name.start_with? 'Errno::'
58
+ $stderr.puts Sauce::Connect.port_not_open_message
59
+ raise TunnelNotPossibleException, "Couldn't use port 443"
60
+ end
61
+ end
62
+
63
+ def connect
64
+ unless @skip_connection_test
65
+ ensure_connection_is_possible
66
+ end
67
+
68
+ puts "[Sauce Connect is connecting to Sauce Labs...]"
69
+
70
+ formatted_cli_options = array_of_formatted_cli_options_from_hash(cli_options)
71
+
72
+ command_args = ['-u', @config.username, '-k', @config.access_key]
73
+ command_args << formatted_cli_options
74
+
75
+ command = "exec #{find_sauce_connect} #{command_args.join(' ')} 2>&1"
76
+
77
+ unless @quiet
78
+ string_arguments = formatted_cli_options.join(' ')
79
+ puts "[Sauce Connect arguments: '#{string_arguments}' ]"
80
+ end
81
+
82
+ @pipe = IO.popen(command)
83
+
84
+ @process_status = $?
85
+ at_exit do
86
+ Process.kill("INT", @pipe.pid)
87
+ while @ready
88
+ sleep 1
89
+ end
90
+ end
91
+
92
+ Thread.new {
93
+ while( (line = @pipe.gets) )
94
+ if line =~ /Tunnel remote VM is (.*) (\.\.|at)/
95
+ @status = $1
96
+ end
97
+ if line =~/You may start your tests\./i
98
+ @ready = true
99
+ end
100
+ if line =~ /- (Problem.*)$/
101
+ @error = $1
102
+ @quiet = false
103
+ end
104
+ if line =~ /== Missing requirements ==/
105
+ @error = "Missing requirements"
106
+ @quiet = false
107
+ end
108
+ if line =~/Invalid API_KEY provided/
109
+ @error = "Invalid API_KEY provided"
110
+ @quiet = false
111
+ end
112
+ $stderr.puts line unless @quiet
113
+ end
114
+ @ready = false
115
+ }
116
+ end
117
+
118
+ def cli_options
119
+ cli_options = { readyfile: "sauce_connect.ready" }
120
+ cli_options.merge!(@cli_options) if @cli_options
121
+ cli_options
122
+ end
123
+
124
+ def wait_until_ready
125
+ start = Time.now
126
+ while !@ready and (Time.now-start) < @timeout and @error != "Missing requirements"
127
+ sleep 0.5
128
+ end
129
+
130
+ if @error == "Missing requirements"
131
+ raise "Missing requirements"
132
+ end
133
+
134
+ if !@ready
135
+ error_message = "Sauce Connect failed to connect after #{@timeout} seconds"
136
+ error_message << "\n(Using Sauce Connect at #{@sc4_executable}"
137
+ raise error_message
138
+ end
139
+ end
140
+
141
+ def disconnect
142
+ if @ready
143
+ Process.kill("INT", @pipe.pid)
144
+ while @ready
145
+ sleep 1
146
+ end
147
+ end
148
+ end
149
+
150
+ # Check whether the path, or it's bin/sc descendant, exists and is executable
151
+ def find_sauce_connect
152
+ paths = [@sc4_executable, File.join("#{@sc4_executable}", "bin", "sc")]
153
+
154
+ sc_path = paths.find do |path|
155
+ path_is_connect_executable? path
156
+ end
157
+
158
+ if sc_path.nil?
159
+ raise TunnelNotPossibleException, "No executable found at #{sc_path}, or it can't be executed by #{Process.euid}"
160
+ end
161
+
162
+ return File.absolute_path sc_path
163
+ end
164
+
165
+ def path_is_connect_executable? path
166
+ absolute_path = File.absolute_path path
167
+ return (File.exist? absolute_path) && (File.executable? absolute_path) && !(Dir.exist? absolute_path)
168
+ end
169
+
170
+ # Global Sauce Connect-ness
171
+ @connection = nil
172
+
173
+ def self.connect!(*args)
174
+ @connection = self.new(*args)
175
+ @connection.connect
176
+ @connection.wait_until_ready
177
+ at_exit do
178
+ @connection.disconnect
179
+ end
180
+ end
181
+
182
+ def self.ensure_connected(*args)
183
+ if @connection
184
+ @connection.wait_until_ready
185
+ else
186
+ connect!(*args)
187
+ end
188
+ end
189
+
190
+ private
191
+
192
+ def array_of_formatted_cli_options_from_hash(hash)
193
+ hash.collect do |key, value|
194
+ opt_name = key.to_s.gsub("_", "-")
195
+ "--#{opt_name} #{value}"
196
+ end
197
+ end
198
+
199
+ def self.port_not_open_message
200
+ <<-ENDLINE
201
+ Unable to connect to port 443 on saucelabs.com, which may interfere with
202
+ Sauce Connect.
203
+
204
+ This might be caused by a HTTP mocking framework like WebMock or
205
+ FakeWeb. Check out
206
+ (https://github.com/saucelabs/sauce_ruby#network-mocking)
207
+ if you're using one. Sauce Connect needs access to *.saucelabs.com,
208
+ port 80 and port 443.
209
+
210
+ You can disable network tests by setting :skip_connection_test to true in
211
+ your Sauce.config block.
212
+ ENDLINE
213
+ end
214
+
215
+ def self.cant_access_rest_api_message
216
+ <<-ENDLINE
217
+ Unable to connect to the Sauce REST API, which may interfere with
218
+ Sauce Connect.
219
+
220
+ This might be caused by a HTTP mocking framework like WebMock or
221
+ FakeWeb. Check out
222
+ (https://github.com/saucelabs/sauce_ruby#network-mocking)
223
+ if you're using one. Sauce Connect needs access to *.saucelabs.com,
224
+ port 80 and port 443.
225
+
226
+ You can disable network tests by setting :skip_connection_test to true in
227
+ your Sauce.config block.
228
+ ENDLINE
229
+ end
230
+
231
+ def self.plzGetSC4
232
+ <<-ENDLINE
233
+ Using Sauce Connect 3 has been deprecated. Please set the :sauce_connect_4_executable
234
+ option in your Sauce.config block to the path of an installation of
235
+ Sauce Connect 4.
236
+
237
+ You can download Sauce Connect 4 for free at
238
+ http://docs.saucelabs.com/sauce_connect
239
+ ENDLINE
240
+ end
241
+ end
242
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sauce_connect
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.6.1
5
+ platform: ruby
6
+ authors:
7
+ - R. Tyler Croy
8
+ - Steve Hazel
9
+ - Dylan Lacey
10
+ - Rick Martínez
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2015-06-26 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: sauce_ruby
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: '3.5'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '3.5'
30
+ description: A wrapper to start and stop a Sauce Connect tunnel programatically.
31
+ email:
32
+ - tyler@monkeypox.org
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/sauce/connect.rb
38
+ homepage: https://docs.saucelabs.com/reference/sauce-connect
39
+ licenses:
40
+ - Apache 2.0
41
+ metadata: {}
42
+ post_install_message: |2
43
+ To use the Sauce Connect gem, you'll need to download the appropriate
44
+ Sauce Connect binary from https://docs.saucelabs.com/reference/sauce-connect
45
+
46
+ Then, set the 'sauce_connect_4_executable' key in your Sauce.config block, to
47
+ the path of the unzipped file's /bin/sc.
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements:
62
+ - An account at http://www.saucelabs.com
63
+ - A working copy of Sauce Connect from https://docs.saucelabs.com/reference/sauce-connect
64
+ rubyforge_project:
65
+ rubygems_version: 2.4.6
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Manage Sauce Connect from within your tests
69
+ test_files: []
70
+ has_rdoc: