turbot 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +36 -0
- data/bin/turbot +17 -0
- data/data/cacert.pem +3988 -0
- data/lib/turbot/auth.rb +315 -0
- data/lib/turbot/cli.rb +38 -0
- data/lib/turbot/client/cisaurus.rb +25 -0
- data/lib/turbot/client/pgbackups.rb +113 -0
- data/lib/turbot/client/rendezvous.rb +111 -0
- data/lib/turbot/client/ssl_endpoint.rb +25 -0
- data/lib/turbot/client/turbot_postgresql.rb +148 -0
- data/lib/turbot/client.rb +757 -0
- data/lib/turbot/command/auth.rb +85 -0
- data/lib/turbot/command/base.rb +192 -0
- data/lib/turbot/command/bots.rb +326 -0
- data/lib/turbot/command/config.rb +123 -0
- data/lib/turbot/command/help.rb +179 -0
- data/lib/turbot/command/keys.rb +115 -0
- data/lib/turbot/command/logs.rb +34 -0
- data/lib/turbot/command/ssl.rb +43 -0
- data/lib/turbot/command/status.rb +51 -0
- data/lib/turbot/command/update.rb +47 -0
- data/lib/turbot/command/version.rb +23 -0
- data/lib/turbot/command.rb +304 -0
- data/lib/turbot/deprecated/help.rb +38 -0
- data/lib/turbot/deprecated.rb +5 -0
- data/lib/turbot/distribution.rb +9 -0
- data/lib/turbot/errors.rb +28 -0
- data/lib/turbot/excon.rb +11 -0
- data/lib/turbot/helpers/log_displayer.rb +70 -0
- data/lib/turbot/helpers/pg_dump_restore.rb +115 -0
- data/lib/turbot/helpers/turbot_postgresql.rb +213 -0
- data/lib/turbot/helpers.rb +521 -0
- data/lib/turbot/plugin.rb +165 -0
- data/lib/turbot/updater.rb +171 -0
- data/lib/turbot/version.rb +3 -0
- data/lib/turbot.rb +19 -0
- data/lib/vendor/turbot/okjson.rb +598 -0
- data/spec/helper/legacy_help.rb +16 -0
- data/spec/helper/pg_dump_restore_spec.rb +67 -0
- data/spec/schemas/dummy_schema.json +12 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +220 -0
- data/spec/support/display_message_matcher.rb +49 -0
- data/spec/support/dummy_api.rb +120 -0
- data/spec/support/openssl_mock_helper.rb +8 -0
- data/spec/support/organizations_mock_helper.rb +11 -0
- data/spec/turbot/auth_spec.rb +214 -0
- data/spec/turbot/client/pgbackups_spec.rb +43 -0
- data/spec/turbot/client/rendezvous_spec.rb +62 -0
- data/spec/turbot/client/ssl_endpoint_spec.rb +48 -0
- data/spec/turbot/client/turbot_postgresql_spec.rb +71 -0
- data/spec/turbot/client_spec.rb +548 -0
- data/spec/turbot/command/auth_spec.rb +38 -0
- data/spec/turbot/command/base_spec.rb +66 -0
- data/spec/turbot/command/bots_spec.rb +54 -0
- data/spec/turbot/command/config_spec.rb +143 -0
- data/spec/turbot/command/help_spec.rb +90 -0
- data/spec/turbot/command/keys_spec.rb +117 -0
- data/spec/turbot/command/logs_spec.rb +60 -0
- data/spec/turbot/command/status_spec.rb +48 -0
- data/spec/turbot/command/version_spec.rb +16 -0
- data/spec/turbot/command_spec.rb +131 -0
- data/spec/turbot/helpers/turbot_postgresql_spec.rb +181 -0
- data/spec/turbot/helpers_spec.rb +48 -0
- data/spec/turbot/plugin_spec.rb +172 -0
- data/spec/turbot/updater_spec.rb +44 -0
- data/templates/manifest.json +7 -0
- data/templates/scraper.py +5 -0
- data/templates/scraper.rb +6 -0
- metadata +199 -0
data/lib/turbot/auth.rb
ADDED
@@ -0,0 +1,315 @@
|
|
1
|
+
require "cgi"
|
2
|
+
require "turbot"
|
3
|
+
require "turbot/client"
|
4
|
+
require "turbot/helpers"
|
5
|
+
|
6
|
+
require "netrc"
|
7
|
+
|
8
|
+
class Turbot::Auth
|
9
|
+
class << self
|
10
|
+
include Turbot::Helpers
|
11
|
+
|
12
|
+
attr_accessor :credentials
|
13
|
+
|
14
|
+
def api
|
15
|
+
@api ||= begin
|
16
|
+
Turbot::API.new(default_params.merge(:api_key => password))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def client
|
21
|
+
@client ||= begin
|
22
|
+
client = Turbot::Client.new(user, password, host)
|
23
|
+
client.on_warning { |msg| self.display("\n#{msg}\n\n") }
|
24
|
+
client
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def login
|
29
|
+
delete_credentials
|
30
|
+
get_credentials
|
31
|
+
end
|
32
|
+
|
33
|
+
def logout
|
34
|
+
delete_credentials
|
35
|
+
end
|
36
|
+
|
37
|
+
# will raise if not authenticated
|
38
|
+
def check
|
39
|
+
api.get_user
|
40
|
+
end
|
41
|
+
|
42
|
+
def default_host
|
43
|
+
"http://turbot"
|
44
|
+
end
|
45
|
+
|
46
|
+
def git_host
|
47
|
+
ENV['TURBOT_GIT_HOST'] || host
|
48
|
+
end
|
49
|
+
|
50
|
+
def host
|
51
|
+
ENV['TURBOT_HOST'] || default_host
|
52
|
+
end
|
53
|
+
|
54
|
+
def reauthorize
|
55
|
+
@credentials = ask_for_and_save_credentials
|
56
|
+
end
|
57
|
+
|
58
|
+
def user # :nodoc:
|
59
|
+
get_credentials[0]
|
60
|
+
end
|
61
|
+
|
62
|
+
def password # :nodoc:
|
63
|
+
get_credentials[1]
|
64
|
+
end
|
65
|
+
|
66
|
+
def api_key
|
67
|
+
api.get_api_key
|
68
|
+
end
|
69
|
+
|
70
|
+
def api_key_for_credentials(user = get_credentials[0], password = get_credentials[1])
|
71
|
+
api = Turbot::API.new(default_params)
|
72
|
+
api.get_api_key_for_credentials(user, password)["api_key"]
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_credentials # :nodoc:
|
76
|
+
@credentials ||= (read_credentials || ask_for_and_save_credentials)
|
77
|
+
end
|
78
|
+
|
79
|
+
def delete_credentials
|
80
|
+
if netrc
|
81
|
+
netrc.delete("api.#{host}")
|
82
|
+
netrc.delete("code.#{host}")
|
83
|
+
netrc.save
|
84
|
+
end
|
85
|
+
@api, @client, @credentials = nil, nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def netrc_path
|
89
|
+
default = Netrc.default_path
|
90
|
+
encrypted = default + ".gpg"
|
91
|
+
if File.exists?(encrypted)
|
92
|
+
encrypted
|
93
|
+
else
|
94
|
+
default
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def netrc # :nodoc:
|
99
|
+
@netrc ||= begin
|
100
|
+
File.exists?(netrc_path) && Netrc.read(netrc_path)
|
101
|
+
rescue => error
|
102
|
+
if error.message =~ /^Permission bits for/
|
103
|
+
perm = File.stat(netrc_path).mode & 0777
|
104
|
+
abort("Permissions #{perm} for '#{netrc_path}' are too open. You should run `chmod 0600 #{netrc_path}` so that your credentials are NOT accessible by others.")
|
105
|
+
else
|
106
|
+
raise error
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def read_credentials
|
112
|
+
if ENV['TURBOT_API_KEY']
|
113
|
+
['', ENV['TURBOT_API_KEY']]
|
114
|
+
else
|
115
|
+
|
116
|
+
# read netrc credentials if they exist
|
117
|
+
if netrc
|
118
|
+
# force migration of long api tokens (80 chars) to short ones (40)
|
119
|
+
# #write_credentials rewrites both api.* and code.*
|
120
|
+
credentials = netrc["api.#{host}"]
|
121
|
+
if credentials && credentials[1].length > 40
|
122
|
+
@credentials = [ credentials[0], credentials[1][0,40] ]
|
123
|
+
write_credentials
|
124
|
+
end
|
125
|
+
|
126
|
+
netrc["api.#{host}"]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def write_credentials
|
132
|
+
FileUtils.mkdir_p(File.dirname(netrc_path))
|
133
|
+
FileUtils.touch(netrc_path)
|
134
|
+
unless running_on_windows?
|
135
|
+
FileUtils.chmod(0600, netrc_path)
|
136
|
+
end
|
137
|
+
netrc["api.#{host}"] = self.credentials
|
138
|
+
netrc["code.#{host}"] = self.credentials
|
139
|
+
netrc.save
|
140
|
+
end
|
141
|
+
|
142
|
+
def echo_off
|
143
|
+
with_tty do
|
144
|
+
system "stty -echo"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def echo_on
|
149
|
+
with_tty do
|
150
|
+
system "stty echo"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def ask_for_credentials
|
155
|
+
puts "Enter your Turbot credentials."
|
156
|
+
|
157
|
+
print "Email: "
|
158
|
+
user = ask
|
159
|
+
|
160
|
+
print "Password (typing will be hidden): "
|
161
|
+
password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
|
162
|
+
|
163
|
+
[user, api_key_for_credentials(user, password)]
|
164
|
+
end
|
165
|
+
|
166
|
+
def ask_for_password_on_windows
|
167
|
+
require "Win32API"
|
168
|
+
char = nil
|
169
|
+
password = ''
|
170
|
+
|
171
|
+
while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
|
172
|
+
break if char == 10 || char == 13 # received carriage return or newline
|
173
|
+
if char == 127 || char == 8 # backspace and delete
|
174
|
+
password.slice!(-1, 1)
|
175
|
+
else
|
176
|
+
# windows might throw a -1 at us so make sure to handle RangeError
|
177
|
+
(password << char.chr) rescue RangeError
|
178
|
+
end
|
179
|
+
end
|
180
|
+
puts
|
181
|
+
return password
|
182
|
+
end
|
183
|
+
|
184
|
+
def ask_for_password
|
185
|
+
echo_off
|
186
|
+
password = ask
|
187
|
+
puts
|
188
|
+
echo_on
|
189
|
+
return password
|
190
|
+
end
|
191
|
+
|
192
|
+
def ask_for_and_save_credentials
|
193
|
+
begin
|
194
|
+
# ask for username and password, look up API key against API given these
|
195
|
+
# In looking up the API key it also attempts to log the user in
|
196
|
+
@credentials = ask_for_credentials
|
197
|
+
# write these to a hidden file
|
198
|
+
write_credentials
|
199
|
+
check
|
200
|
+
rescue Turbot::API::Errors::NotFound, Turbot::API::Errors::Unauthorized => e
|
201
|
+
delete_credentials
|
202
|
+
display "Authentication failed."
|
203
|
+
retry if retry_login?
|
204
|
+
exit 1
|
205
|
+
rescue Exception => e
|
206
|
+
delete_credentials
|
207
|
+
raise e
|
208
|
+
end
|
209
|
+
check_for_associated_ssh_key unless Turbot::Command.current_command == "keys:add"
|
210
|
+
@credentials
|
211
|
+
end
|
212
|
+
|
213
|
+
def check_for_associated_ssh_key
|
214
|
+
if api.get_ssh_keys.empty?
|
215
|
+
associate_or_generate_ssh_key
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def associate_or_generate_ssh_key
|
220
|
+
public_keys = Dir.glob("#{home_directory}/.ssh/*.pub").sort
|
221
|
+
|
222
|
+
case public_keys.length
|
223
|
+
when 0 then
|
224
|
+
display "Could not find an existing public key."
|
225
|
+
display "Would you like to generate one? [Yn] ", false
|
226
|
+
unless ask.strip.downcase == "n"
|
227
|
+
display "Generating new SSH public key."
|
228
|
+
generate_ssh_key("id_rsa")
|
229
|
+
associate_key("#{home_directory}/.ssh/id_rsa.pub")
|
230
|
+
end
|
231
|
+
when 1 then
|
232
|
+
display "Found existing public key: #{public_keys.first}"
|
233
|
+
associate_key(public_keys.first)
|
234
|
+
else
|
235
|
+
display "Found the following SSH public keys:"
|
236
|
+
public_keys.each_with_index do |key, index|
|
237
|
+
display "#{index+1}) #{File.basename(key)}"
|
238
|
+
end
|
239
|
+
display "Which would you like to use with your Turbot account? ", false
|
240
|
+
choice = ask.to_i - 1
|
241
|
+
chosen = public_keys[choice]
|
242
|
+
if choice == -1 || chosen.nil?
|
243
|
+
error("Invalid choice")
|
244
|
+
end
|
245
|
+
associate_key(chosen)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def generate_ssh_key(keyfile)
|
250
|
+
ssh_dir = File.join(home_directory, ".ssh")
|
251
|
+
unless File.exists?(ssh_dir)
|
252
|
+
FileUtils.mkdir_p ssh_dir
|
253
|
+
unless running_on_windows?
|
254
|
+
File.chmod(0700, ssh_dir)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
output = `ssh-keygen -t rsa -N "" -f \"#{home_directory}/.ssh/#{keyfile}\" 2>&1`
|
258
|
+
if ! $?.success?
|
259
|
+
error("Could not generate key: #{output}")
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def associate_key(key)
|
264
|
+
action("Uploading SSH public key #{key}") do
|
265
|
+
if File.exists?(key)
|
266
|
+
api.post_key(File.read(key))
|
267
|
+
else
|
268
|
+
error("Could not upload SSH public key: key file '" + key + "' does not exist")
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def retry_login?
|
274
|
+
@login_attempts ||= 0
|
275
|
+
@login_attempts += 1
|
276
|
+
@login_attempts < 3
|
277
|
+
end
|
278
|
+
|
279
|
+
def verified_hosts
|
280
|
+
%w( turbot.com turbot-shadow.com )
|
281
|
+
end
|
282
|
+
|
283
|
+
def base_host(host)
|
284
|
+
parts = URI.parse(full_host(host)).host.split(".")
|
285
|
+
return parts.first if parts.size == 1
|
286
|
+
parts[-2..-1].join(".")
|
287
|
+
end
|
288
|
+
|
289
|
+
def full_host(host)
|
290
|
+
(host =~ /^http/) ? host : "https://api.#{host}"
|
291
|
+
end
|
292
|
+
|
293
|
+
def verify_host?(host)
|
294
|
+
hostname = base_host(host)
|
295
|
+
verified = verified_hosts.include?(hostname)
|
296
|
+
verified = false if ENV["TURBOT_SSL_VERIFY"] == "disable"
|
297
|
+
verified
|
298
|
+
end
|
299
|
+
|
300
|
+
protected
|
301
|
+
|
302
|
+
def default_params
|
303
|
+
uri = URI.parse(full_host(host))
|
304
|
+
{
|
305
|
+
:headers => {
|
306
|
+
'User-Agent' => Turbot.user_agent
|
307
|
+
},
|
308
|
+
:host => uri.host,
|
309
|
+
:port => uri.port,
|
310
|
+
:scheme => uri.scheme,
|
311
|
+
:ssl_verify_peer => verify_host?(host)
|
312
|
+
}
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
data/lib/turbot/cli.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
load('turbot/helpers.rb') # reload helpers after possible inject_loadpath
|
2
|
+
load('turbot/updater.rb') # reload updater after possible inject_loadpath
|
3
|
+
|
4
|
+
require "turbot"
|
5
|
+
require "turbot/command"
|
6
|
+
require "turbot/helpers"
|
7
|
+
|
8
|
+
# workaround for rescue/reraise to define errors in command.rb failing in 1.8.6
|
9
|
+
if RUBY_VERSION =~ /^1.8.6/
|
10
|
+
require('turbot-api')
|
11
|
+
require('rest_client')
|
12
|
+
end
|
13
|
+
|
14
|
+
class Turbot::CLI
|
15
|
+
|
16
|
+
extend Turbot::Helpers
|
17
|
+
|
18
|
+
def self.start(*args)
|
19
|
+
begin
|
20
|
+
if $stdin.isatty
|
21
|
+
$stdin.sync = true
|
22
|
+
end
|
23
|
+
if $stdout.isatty
|
24
|
+
$stdout.sync = true
|
25
|
+
end
|
26
|
+
command = args.shift.strip rescue "help"
|
27
|
+
Turbot::Command.load
|
28
|
+
Turbot::Command.run(command, args)
|
29
|
+
rescue Interrupt
|
30
|
+
`stty icanon echo`
|
31
|
+
error("Command cancelled.")
|
32
|
+
rescue => error
|
33
|
+
styled_error(error)
|
34
|
+
exit(1)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "turbot/client"
|
2
|
+
|
3
|
+
class Turbot::Client::Cisaurus
|
4
|
+
|
5
|
+
include Turbot::Helpers
|
6
|
+
|
7
|
+
def initialize(uri)
|
8
|
+
require 'rest_client'
|
9
|
+
@uri = URI.parse(uri)
|
10
|
+
end
|
11
|
+
|
12
|
+
def authenticated_resource(path)
|
13
|
+
host = "#{@uri.scheme}://#{@uri.host}"
|
14
|
+
host += ":#{@uri.port}" if @uri.port
|
15
|
+
RestClient::Resource.new("#{host}#{path}", "", Turbot::Auth.api_key)
|
16
|
+
end
|
17
|
+
|
18
|
+
def copy_slug(from, to)
|
19
|
+
authenticated_resource("/v1/bots/#{from}/copy/#{to}").post(json_encode("description" => "Forked from #{from}"), :content_type => :json).headers[:location]
|
20
|
+
end
|
21
|
+
|
22
|
+
def job_done?(job_location)
|
23
|
+
202 != authenticated_resource(job_location).get.code
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require "turbot/client"
|
2
|
+
|
3
|
+
class Turbot::Client::Pgbackups
|
4
|
+
|
5
|
+
include Turbot::Helpers
|
6
|
+
|
7
|
+
def initialize(uri)
|
8
|
+
require 'rest_client'
|
9
|
+
@uri = URI.parse(uri)
|
10
|
+
end
|
11
|
+
|
12
|
+
def authenticated_resource(path)
|
13
|
+
host = "#{@uri.scheme}://#{@uri.host}"
|
14
|
+
host += ":#{@uri.port}" if @uri.port
|
15
|
+
RestClient::Resource.new("#{host}#{path}",
|
16
|
+
:user => @uri.user,
|
17
|
+
:password => @uri.password,
|
18
|
+
:headers => {:x_turbot_gem_version => Turbot::Client.version}
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_transfer(from_url, from_name, to_url, to_name, opts={})
|
23
|
+
# opts[:expire] => true will delete the oldest backup if at the plan limit
|
24
|
+
resource = authenticated_resource("/client/transfers")
|
25
|
+
params = {:from_url => from_url, :from_name => from_name, :to_url => to_url, :to_name => to_name}.merge opts
|
26
|
+
json_decode post(resource, params).body
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_transfers
|
30
|
+
resource = authenticated_resource("/client/transfers")
|
31
|
+
json_decode get(resource).body
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_transfer(id)
|
35
|
+
resource = authenticated_resource("/client/transfers/#{id}")
|
36
|
+
json_decode get(resource).body
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_backups(opts={})
|
40
|
+
resource = authenticated_resource("/client/backups")
|
41
|
+
json_decode get(resource).body
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_backup(name, opts={})
|
45
|
+
name = URI.escape(name)
|
46
|
+
resource = authenticated_resource("/client/backups/#{name}")
|
47
|
+
json_decode get(resource).body
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_latest_backup
|
51
|
+
resource = authenticated_resource("/client/latest_backup")
|
52
|
+
json_decode get(resource).body
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete_backup(name)
|
56
|
+
name = URI.escape(name)
|
57
|
+
begin
|
58
|
+
resource = authenticated_resource("/client/backups/#{name}")
|
59
|
+
delete(resource).body
|
60
|
+
true
|
61
|
+
rescue RestClient::ResourceNotFound => e
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def get(resource)
|
69
|
+
check_errors do
|
70
|
+
response = resource.get
|
71
|
+
display_turbot_warning response
|
72
|
+
response
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def post(resource, params)
|
77
|
+
check_errors do
|
78
|
+
response = resource.post(params)
|
79
|
+
display_turbot_warning response
|
80
|
+
response
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def delete(resource)
|
85
|
+
check_errors do
|
86
|
+
response = resource.delete
|
87
|
+
display_turbot_warning response
|
88
|
+
response
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def check_errors
|
93
|
+
yield
|
94
|
+
rescue RestClient::Unauthorized
|
95
|
+
error "Invalid PGBACKUPS_URL"
|
96
|
+
end
|
97
|
+
|
98
|
+
def display_turbot_warning(response)
|
99
|
+
warning = response.headers[:x_turbot_warning]
|
100
|
+
display warning if warning
|
101
|
+
response
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
module Pgbackups
|
107
|
+
class Client < Turbot::Client::Pgbackups
|
108
|
+
def initialize(*args)
|
109
|
+
Turbot::Helpers.deprecate "Pgbackups::Client has been deprecated. Please use Turbot::Client::Pgbackups instead."
|
110
|
+
super
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require "timeout"
|
2
|
+
require "socket"
|
3
|
+
require "uri"
|
4
|
+
require "turbot/auth"
|
5
|
+
require "turbot/client"
|
6
|
+
require "turbot/helpers"
|
7
|
+
|
8
|
+
class Turbot::Client::Rendezvous
|
9
|
+
|
10
|
+
include Turbot::Helpers
|
11
|
+
|
12
|
+
attr_reader :rendezvous_url, :connect_timeout, :activity_timeout, :input, :output, :on_connect
|
13
|
+
|
14
|
+
def initialize(opts)
|
15
|
+
@rendezvous_url = opts[:rendezvous_url]
|
16
|
+
@connect_timeout = opts[:connect_timeout]
|
17
|
+
@activity_timeout = opts[:activity_timeout]
|
18
|
+
@input = opts[:input]
|
19
|
+
@output = opts[:output]
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_connect(&blk)
|
23
|
+
@on_connect = blk if block_given?
|
24
|
+
@on_connect
|
25
|
+
end
|
26
|
+
|
27
|
+
def start
|
28
|
+
uri = URI.parse(rendezvous_url)
|
29
|
+
host, port, secret = uri.host, uri.port, uri.path[1..-1]
|
30
|
+
|
31
|
+
ssl_socket = Timeout.timeout(connect_timeout) do
|
32
|
+
ssl_context = OpenSSL::SSL::SSLContext.new
|
33
|
+
ssl_context.ssl_version = :TLSv1
|
34
|
+
|
35
|
+
if Turbot::Auth.verify_host?(host)
|
36
|
+
ssl_context.ca_file = File.expand_path("../../../../data/cacert.pem", __FILE__)
|
37
|
+
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
38
|
+
end
|
39
|
+
|
40
|
+
tcp_socket = TCPSocket.open(host, port)
|
41
|
+
ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_context)
|
42
|
+
ssl_socket.connect
|
43
|
+
ssl_socket.puts(secret)
|
44
|
+
ssl_socket.readline
|
45
|
+
ssl_socket
|
46
|
+
end
|
47
|
+
|
48
|
+
on_connect.call if on_connect
|
49
|
+
|
50
|
+
readables = [input, ssl_socket].compact
|
51
|
+
|
52
|
+
begin
|
53
|
+
loop do
|
54
|
+
if o = IO.select(readables, nil, nil, activity_timeout)
|
55
|
+
if (input && (o.first.first == input))
|
56
|
+
begin
|
57
|
+
data = input.readpartial(10000)
|
58
|
+
rescue EOFError
|
59
|
+
ssl_socket.write(4.chr)
|
60
|
+
ssl_socket.flush
|
61
|
+
readables.delete(input)
|
62
|
+
next
|
63
|
+
end
|
64
|
+
if running_on_windows?
|
65
|
+
data.gsub!("\r\n", "\n") # prevent double CRs
|
66
|
+
end
|
67
|
+
ssl_socket.write(data)
|
68
|
+
ssl_socket.flush
|
69
|
+
elsif (o.first.first == ssl_socket)
|
70
|
+
begin
|
71
|
+
data = ssl_socket.readpartial(10000)
|
72
|
+
rescue EOFError
|
73
|
+
break
|
74
|
+
end
|
75
|
+
output.write(fixup(data))
|
76
|
+
end
|
77
|
+
else
|
78
|
+
raise(Timeout::Error.new)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
rescue Interrupt
|
82
|
+
ssl_socket.write(3.chr)
|
83
|
+
ssl_socket.flush
|
84
|
+
retry
|
85
|
+
rescue SignalException => e
|
86
|
+
if Signal.list["QUIT"] == e.signo
|
87
|
+
ssl_socket.write(28.chr)
|
88
|
+
ssl_socket.flush
|
89
|
+
retry
|
90
|
+
end
|
91
|
+
raise
|
92
|
+
rescue Errno::EIO
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def fixup(data)
|
99
|
+
return nil if ! data
|
100
|
+
if data.respond_to?(:force_encoding)
|
101
|
+
data.force_encoding('utf-8') if data.respond_to?(:force_encoding)
|
102
|
+
end
|
103
|
+
if running_on_windows?
|
104
|
+
begin
|
105
|
+
data.gsub!(/\e\[[\d;]+m/, '')
|
106
|
+
rescue # ignore failed gsub, for instance when non-utf8
|
107
|
+
end
|
108
|
+
end
|
109
|
+
output.isatty ? data : data.gsub(/\cM/,"")
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Turbot::Client
|
2
|
+
def ssl_endpoint_add(bot, pem, key)
|
3
|
+
json_decode(post("bots/#{bot}/ssl-endpoints", :accept => :json, :pem => pem, :key => key).to_s)
|
4
|
+
end
|
5
|
+
|
6
|
+
def ssl_endpoint_info(bot, cname)
|
7
|
+
json_decode(get("bots/#{bot}/ssl-endpoints/#{escape(cname)}", :accept => :json).to_s)
|
8
|
+
end
|
9
|
+
|
10
|
+
def ssl_endpoint_list(bot)
|
11
|
+
json_decode(get("bots/#{bot}/ssl-endpoints", :accept => :json).to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
def ssl_endpoint_remove(bot, cname)
|
15
|
+
json_decode(delete("bots/#{bot}/ssl-endpoints/#{escape(cname)}", :accept => :json).to_s)
|
16
|
+
end
|
17
|
+
|
18
|
+
def ssl_endpoint_rollback(bot, cname)
|
19
|
+
json_decode(post("bots/#{bot}/ssl-endpoints/#{escape(cname)}/rollback", :accept => :json).to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
def ssl_endpoint_update(bot, cname, pem, key)
|
23
|
+
json_decode(put("bots/#{bot}/ssl-endpoints/#{escape(cname)}", :accept => :json, :pem => pem, :key => key).to_s)
|
24
|
+
end
|
25
|
+
end
|