dblink 0.2
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.
- checksums.yaml +7 -0
- data/bin/dblink +189 -0
- data/config.ini.erb +13 -0
- data/dblink.gemspec +16 -0
- data/lib/dblink/color.rb +25 -0
- data/lib/dblink/network.rb +10 -0
- data/lib/dblink/pg_bouncer_runner.rb +124 -0
- data/lib/dblink/psql_runner.rb +48 -0
- data/lib/dblink/system.rb +24 -0
- data/lib/dblink/tonnel_runner.rb +90 -0
- data/lib/dblink/web_service.rb +34 -0
- data/pgbouncer +0 -0
- data/share.pem +27 -0
- metadata +57 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3b0e7af330863d700229caf232c3c2ae55931b42
|
4
|
+
data.tar.gz: bb6ab4186a21d60076e51bd484ffc3f2be908b9a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4316a3ab2414ee53970ef3a21c49996448f93744e2d9d720f332bfaa9202d872568b32b447ae2044a064b15453371cdc96226e453f83c8e618741b7eb79b7058
|
7
|
+
data.tar.gz: ecbb1c23a0b4aac78ebe3358cd4f5af84420e767392a7c0be7db375c5fc4dc60c38cff05e8cf1a8aab26880cc932d04fe36920d02d96b04d3dbe56430b4d120c
|
data/bin/dblink
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'open3'
|
5
|
+
require 'tempfile'
|
6
|
+
require 'erb'
|
7
|
+
require 'securerandom'
|
8
|
+
require 'json'
|
9
|
+
require 'optparse'
|
10
|
+
require 'fileutils'
|
11
|
+
|
12
|
+
BASE_DIR = File.expand_path(File.dirname(__FILE__) + '/..')
|
13
|
+
|
14
|
+
require BASE_DIR + "/lib/dblink/network"
|
15
|
+
require BASE_DIR + "/lib/dblink/system"
|
16
|
+
require BASE_DIR + "/lib/dblink/pg_bouncer_runner"
|
17
|
+
require BASE_DIR + "/lib/dblink/tonnel_runner"
|
18
|
+
require BASE_DIR + "/lib/dblink/psql_runner"
|
19
|
+
require BASE_DIR + "/lib/dblink/web_service"
|
20
|
+
require BASE_DIR + "/lib/dblink/color"
|
21
|
+
|
22
|
+
PUBLIC_HOST = 'dblink.tk'
|
23
|
+
|
24
|
+
CLI_OPTS = {
|
25
|
+
verbose: false,
|
26
|
+
host: 'localhost',
|
27
|
+
port: 5432,
|
28
|
+
user: ENV['USER'],
|
29
|
+
database: ENV['USER'],
|
30
|
+
check_local: true,
|
31
|
+
check_tonnel: true
|
32
|
+
}
|
33
|
+
|
34
|
+
OptionParser.new do |opts|
|
35
|
+
opts.banner = "Usage: dblink [options]"
|
36
|
+
|
37
|
+
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
38
|
+
CLI_OPTS[:verbose] = v
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("-H", "--host [HOST]", "PostgreSQL host", "Default: localhost") do |value|
|
42
|
+
CLI_OPTS[:host] = value
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on("-p", "--port [PORT]", "PostgreSQL port", "Default: 5432") do |value|
|
46
|
+
CLI_OPTS[:port] = value
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on("-d", "--database [DATABASE]", "PostgreSQL database", "Default: #{ENV['USER']}") do |value|
|
50
|
+
CLI_OPTS[:database] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("-u", "--user [USER]", "User to connect to local PostgreSQL server", "Default: #{ENV['USER']}") do |value|
|
54
|
+
CLI_OPTS[:user] = value
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("-P", "--password [PASSWORD]", "PostgreSQL password") do |value|
|
58
|
+
CLI_OPTS[:password] = value
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("-cl", "--[no-]check-local", "Default true. Check connection to local PostgreSQL server") do |value|
|
62
|
+
CLI_OPTS[:check_local] = value
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on("-cr", "--[no-]check-remote", "Default true. Check connection via ssh tonnel") do |value|
|
66
|
+
CLI_OPTS[:check_tonnel] = value
|
67
|
+
end
|
68
|
+
|
69
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
70
|
+
puts opts
|
71
|
+
exit
|
72
|
+
end
|
73
|
+
|
74
|
+
end.parse!
|
75
|
+
|
76
|
+
#p BASE_DIR
|
77
|
+
|
78
|
+
if CLI_OPTS[:check_local]
|
79
|
+
print "Checking connection to "
|
80
|
+
print "#{CLI_OPTS[:host]}:#{CLI_OPTS[:port]}".underline
|
81
|
+
|
82
|
+
result = PsqlRunner.check_connection(CLI_OPTS)
|
83
|
+
|
84
|
+
print " - "
|
85
|
+
puts result === true ? "OK".green : "FAILED".red
|
86
|
+
|
87
|
+
if result != true
|
88
|
+
puts
|
89
|
+
puts result
|
90
|
+
puts
|
91
|
+
|
92
|
+
exit(1)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
pgb_runner = PgBouncerRunner.new(
|
97
|
+
pg_host: CLI_OPTS[:host],
|
98
|
+
pg_port: CLI_OPTS[:port],
|
99
|
+
pg_user: CLI_OPTS[:user],
|
100
|
+
pg_password: CLI_OPTS[:password],
|
101
|
+
pg_db: CLI_OPTS[:database],
|
102
|
+
verbose: CLI_OPTS[:verbose]
|
103
|
+
)
|
104
|
+
|
105
|
+
print "Creating configs in /tmp folder"
|
106
|
+
pgb_runner.create_config
|
107
|
+
puts " - " + "OK".green
|
108
|
+
|
109
|
+
if CLI_OPTS[:verbose]
|
110
|
+
puts "Config folder: #{pgb_runner.tmp_folder}"
|
111
|
+
end
|
112
|
+
|
113
|
+
print "Starting pgbouncer "
|
114
|
+
pgb_runner.run_async
|
115
|
+
pgb_pid = pgb_runner.wait_for_pid
|
116
|
+
puts "- " + "OK".green + " (pid: #{pgb_pid})"
|
117
|
+
|
118
|
+
print "Starting SSH tonnel "
|
119
|
+
tonnel = TonnelRunner.new(pgb_runner.pgb_port, tmp_dir: pgb_runner.tmp_folder, verbose: CLI_OPTS[:verbose])
|
120
|
+
tonnel.run_async
|
121
|
+
tonnel.wait_remote_port_allocated
|
122
|
+
|
123
|
+
puts "- " + "OK".green + " (pid: #{tonnel.runner_pid})"
|
124
|
+
puts "Remote port: #{tonnel.remote_port}"
|
125
|
+
|
126
|
+
psql_opt = {
|
127
|
+
user: pgb_runner.pgb_user,
|
128
|
+
password: pgb_runner.pgb_password,
|
129
|
+
database: pgb_runner.pg_db,
|
130
|
+
port: tonnel.remote_port,
|
131
|
+
host: PUBLIC_HOST,
|
132
|
+
verbose: CLI_OPTS[:verbose]
|
133
|
+
}
|
134
|
+
|
135
|
+
if CLI_OPTS[:check_tonnel]
|
136
|
+
print "Checking connection: "
|
137
|
+
result = PsqlRunner.check_connection(psql_opt)
|
138
|
+
puts result === true ? "OK".green : "FAILED".red
|
139
|
+
|
140
|
+
if result != true
|
141
|
+
puts
|
142
|
+
puts result
|
143
|
+
puts
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
db_url = pgb_runner.make_db_url(tonnel.remote_port)
|
148
|
+
|
149
|
+
puts
|
150
|
+
puts "DATABSE CONNECTION:"
|
151
|
+
puts " #{db_url}"
|
152
|
+
puts
|
153
|
+
|
154
|
+
psql_path = PsqlRunner.find_psql
|
155
|
+
if psql_path
|
156
|
+
puts "CONNECT WITH PSQL:"
|
157
|
+
passwd = pgb_runner.pgb_password == '' ? '' : "PGPASSWORD=#{pgb_runner.pgb_password}"
|
158
|
+
puts " #{passwd} #{psql_path} -h dblink.tk -p #{tonnel.remote_port} -U #{pgb_runner.pgb_user} #{pgb_runner.pg_db}"
|
159
|
+
puts
|
160
|
+
end
|
161
|
+
|
162
|
+
web_response = WebService.register_link(db_url, verbose: CLI_OPTS[:verbose])
|
163
|
+
|
164
|
+
if web_response['status'] == 'success'
|
165
|
+
puts "WEB URL:"
|
166
|
+
puts " #{web_response['web_url']}"
|
167
|
+
puts
|
168
|
+
end
|
169
|
+
|
170
|
+
at_exit do
|
171
|
+
print "Stoping pgbouncer ..."
|
172
|
+
pgb_runner.stop_async
|
173
|
+
puts
|
174
|
+
print "Stoping ssh ..."
|
175
|
+
tonnel.stop_async
|
176
|
+
puts
|
177
|
+
|
178
|
+
tonnel.runner_thread.kill
|
179
|
+
|
180
|
+
puts "Removing tmp dir ..."
|
181
|
+
FileUtils.rm_rf(pgb_runner.tmp_folder)
|
182
|
+
|
183
|
+
exit!
|
184
|
+
end
|
185
|
+
|
186
|
+
tonnel.runner_thread.value
|
187
|
+
|
188
|
+
#ssh_command = SSH_TONNEL_COMMAND.gsub('{{local_port}}', '6543')
|
189
|
+
#system ssh_command
|
data/config.ini.erb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
[databases]
|
2
|
+
<%= pg_db %> = host=<%= pg_host %> port=<%= pg_port %> dbname=<%= pg_db %> user=<%= pg_user %> <%= pg_password && pg_password != '' ? "password=#{pg_password}" : '' %>
|
3
|
+
|
4
|
+
[pgbouncer]
|
5
|
+
pool_mode = session
|
6
|
+
listen_port = <%= pgb_port %>
|
7
|
+
listen_addr = 127.0.0.1
|
8
|
+
auth_type = plain
|
9
|
+
auth_file = ./users.txt
|
10
|
+
logfile = pgbouncer.log
|
11
|
+
pidfile = pgbouncer.pid
|
12
|
+
admin_users = someuser
|
13
|
+
stats_users = stat_collector
|
data/dblink.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "dblink"
|
3
|
+
s.version = "0.2"
|
4
|
+
s.author = ["Pavel Evstigneev"]
|
5
|
+
s.description= "CLI utility to share access to your local PostgreSQL server"
|
6
|
+
s.email = ["pavel.evst@gmail.com"]
|
7
|
+
s.homepage = "http://dblink.r15.railsrumble.com"
|
8
|
+
s.summary = %q{Veritrans ruby library}
|
9
|
+
s.license = 'MIT'
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.test_files = []
|
13
|
+
|
14
|
+
s.require_paths = ["lib"]
|
15
|
+
s.executables = ["dblink"]
|
16
|
+
end
|
data/lib/dblink/color.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
class String
|
2
|
+
def black; "\e[30m#{self}\e[0m" end
|
3
|
+
def red; "\e[31m#{self}\e[0m" end
|
4
|
+
def green; "\e[32m#{self}\e[0m" end
|
5
|
+
def brown; "\e[33m#{self}\e[0m" end
|
6
|
+
def blue; "\e[34m#{self}\e[0m" end
|
7
|
+
def magenta; "\e[35m#{self}\e[0m" end
|
8
|
+
def cyan; "\e[36m#{self}\e[0m" end
|
9
|
+
def gray; "\e[37m#{self}\e[0m" end
|
10
|
+
|
11
|
+
def bg_black; "\e[40m#{self}\e[0m" end
|
12
|
+
def bg_red; "\e[41m#{self}\e[0m" end
|
13
|
+
def bg_green; "\e[42m#{self}\e[0m" end
|
14
|
+
def bg_brown; "\e[43m#{self}\e[0m" end
|
15
|
+
def bg_blue; "\e[44m#{self}\e[0m" end
|
16
|
+
def bg_magenta; "\e[45m#{self}\e[0m" end
|
17
|
+
def bg_cyan; "\e[46m#{self}\e[0m" end
|
18
|
+
def bg_gray; "\e[47m#{self}\e[0m" end
|
19
|
+
|
20
|
+
def bold; "\e[1m#{self}\e[22m" end
|
21
|
+
def italic; "\e[3m#{self}\e[23m" end
|
22
|
+
def underline; "\e[4m#{self}\e[24m" end
|
23
|
+
def blink; "\e[5m#{self}\e[25m" end
|
24
|
+
def reverse_color; "\e[7m#{self}\e[27m" end
|
25
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
class PgBouncerRunner
|
2
|
+
attr_reader :pg_port, :pg_user, :pg_password, :pg_host, :pg_db
|
3
|
+
attr_reader :pgb_port, :pgb_user, :pgb_password
|
4
|
+
attr_reader :tmp_folder, :runner_thread, :runner_pid
|
5
|
+
|
6
|
+
def initialize(pg_port: 5432, pg_user: ENV['USER'], pg_password: '', pg_host: '127.0.0.1', pg_db: ENV['USER'], verbose: true)
|
7
|
+
@pg_port = pg_port
|
8
|
+
@pg_user = pg_user
|
9
|
+
@pg_password = pg_password
|
10
|
+
@pg_host = pg_host
|
11
|
+
@pg_db = pg_db
|
12
|
+
@verbose = verbose
|
13
|
+
|
14
|
+
@pgb_port = Network.find_available_port
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
create_config unless @tmp_folder
|
19
|
+
|
20
|
+
verbose_arg = @verbose ? '-v' : ''
|
21
|
+
pgb_command = "#{pgbouncer_path} #{verbose_arg} ./config.ini"
|
22
|
+
|
23
|
+
if @verbose
|
24
|
+
puts "Running pgbouncer with config at: #{@tmp_folder}"
|
25
|
+
puts "EXEC #{pgb_command}"
|
26
|
+
end
|
27
|
+
|
28
|
+
Dir.chdir(@tmp_folder) do
|
29
|
+
Open3.popen3(pgb_command) {|stdin, stdout, stderr, wait_thr|
|
30
|
+
@runner_pid = wait_thr.pid # pid of the started process.
|
31
|
+
#p "process_started: #{pid}"
|
32
|
+
Thread.new do
|
33
|
+
begin
|
34
|
+
while line = stdout.gets
|
35
|
+
puts "STDOUT: #{line}" if @verbose
|
36
|
+
end
|
37
|
+
rescue Exception => error
|
38
|
+
puts error.message
|
39
|
+
puts error.backtrace
|
40
|
+
end
|
41
|
+
end
|
42
|
+
Thread.new do
|
43
|
+
begin
|
44
|
+
while line = stderr.gets
|
45
|
+
puts "STDERR: #{line}" if @verbose
|
46
|
+
end
|
47
|
+
rescue Exception => error
|
48
|
+
puts error.message
|
49
|
+
puts error.backtrace
|
50
|
+
end
|
51
|
+
end
|
52
|
+
exit_status = wait_thr.value # Process::Status object returned.
|
53
|
+
puts "pgbouncer stoped: #{exit_status}" if @verbose
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def pgbouncer_path
|
59
|
+
if RUBY_PLATFORM =~ /darwin/
|
60
|
+
BASE_DIR + '/pgbouncer'
|
61
|
+
else
|
62
|
+
|
63
|
+
path = `which pgbouncer 2>/dev/null`
|
64
|
+
if path == ''
|
65
|
+
puts "Can not find pgbouncer in your system"
|
66
|
+
puts "Please install it"
|
67
|
+
exit(1)
|
68
|
+
else
|
69
|
+
return path
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def stop_async
|
76
|
+
if @runner_pid && System.process_alive?(@runner_pid, verbose: @verbose)
|
77
|
+
System.stop_process(@runner_pid)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def run_async
|
82
|
+
@runner_thread = Thread.new do
|
83
|
+
begin
|
84
|
+
run
|
85
|
+
rescue Exception => error
|
86
|
+
puts error.message
|
87
|
+
puts error.backtrace
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
def wait_for_pid
|
95
|
+
while !@runner_pid
|
96
|
+
sleep 0.04
|
97
|
+
end
|
98
|
+
|
99
|
+
@runner_pid
|
100
|
+
end
|
101
|
+
|
102
|
+
def create_config
|
103
|
+
@tmp_folder = Dir.mktmpdir
|
104
|
+
pgb_config = render_template('config.ini.erb', {})
|
105
|
+
File.open(@tmp_folder + '/config.ini', 'w') {|f| f.write(pgb_config) }
|
106
|
+
|
107
|
+
@pgb_user = "share"
|
108
|
+
@pgb_password = SecureRandom.hex(10)
|
109
|
+
File.open(@tmp_folder + '/users.txt', 'w') {|f|
|
110
|
+
f.write(%{"#{@pgb_user}" "#{@pgb_password}"})
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def make_db_url(remote_port)
|
115
|
+
"postgres://#{pgb_user}:#{pgb_password}@dblink.tk:#{remote_port}/#{pg_db}"
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
def render_template(file, params)
|
120
|
+
erb = ERB.new(File.read(file))
|
121
|
+
erb.filename = file
|
122
|
+
erb.result(binding)
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module PsqlRunner
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def check_connection(opt = {})
|
5
|
+
psql_path = find_psql
|
6
|
+
|
7
|
+
unless psql_path
|
8
|
+
result = "Can not find #{'psql'.bold} in your system\n"
|
9
|
+
if RUBY_PLATFORM =~ /darwin/
|
10
|
+
result += "Please run 'brew install postgres' or download it from http://postgresapp.com"
|
11
|
+
else
|
12
|
+
result += "Please install postgresql package in your system"
|
13
|
+
end
|
14
|
+
result += "\nOr disable connection checking (--no-check-local and --no-check-remote)"
|
15
|
+
|
16
|
+
return result
|
17
|
+
end
|
18
|
+
|
19
|
+
passwd = opt[:password] && opt[:password] == '' ? '' : "PGPASSWORD=#{opt[:password]}"
|
20
|
+
|
21
|
+
psql_command = "#{passwd} #{psql_path} -h #{opt[:host]} -p #{opt[:port]} -U #{opt[:user]} #{opt[:database]} -c 'select now()'"
|
22
|
+
|
23
|
+
if opt[:verbose]
|
24
|
+
puts "EXEC #{psql_command}"
|
25
|
+
end
|
26
|
+
|
27
|
+
result = %x(#{psql_command} 2>&1)
|
28
|
+
|
29
|
+
#unless result.include?('(1 row)')
|
30
|
+
# puts result
|
31
|
+
#end
|
32
|
+
|
33
|
+
result.include?('(1 row)') ? true : result
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.find_psql
|
37
|
+
path = `which psql`
|
38
|
+
|
39
|
+
if path != ''
|
40
|
+
path = 'psql'
|
41
|
+
elsif RUBY_PLATFORM =~ /darwin/
|
42
|
+
paths = Dir.glob('/Applications/Postgres.app/Contents/Versions/**/psql')
|
43
|
+
path = paths.last if paths.size > 0
|
44
|
+
end
|
45
|
+
|
46
|
+
path == '' ? nil : path
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module System
|
2
|
+
|
3
|
+
def self.process_alive?(pid, verbose: true)
|
4
|
+
begin
|
5
|
+
Process.kill(0, pid)
|
6
|
+
return true
|
7
|
+
rescue Errno::EPERM # changed uid
|
8
|
+
puts "No permission to query #{pid}!" if verbose
|
9
|
+
rescue Errno::ESRCH
|
10
|
+
puts "#{pid} is NOT running." if verbose # or zombied
|
11
|
+
rescue
|
12
|
+
puts "Unable to determine status for #{pid} : #{$!}" if verbose
|
13
|
+
end
|
14
|
+
return false
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.stop_process(pid)
|
18
|
+
Process.kill('INT', pid)
|
19
|
+
while process_alive?(pid, verbose: CLI_OPTS[:verbose])
|
20
|
+
print "."
|
21
|
+
sleep 0.3
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class TonnelRunner
|
2
|
+
SSH_TONNEL_COMMAND = "ssh -N -R 0.0.0.0:0:localhost:{{local_port}} -i {{pem}} -o StrictHostKeyChecking=no share@dblink.tk"
|
3
|
+
PEM_FILE = "#{BASE_DIR}/share.pem"
|
4
|
+
|
5
|
+
attr_reader :local_port, :remote_port, :runner_thread, :runner_pid
|
6
|
+
|
7
|
+
def initialize(local_port, tmp_dir:, verbose: true)
|
8
|
+
@local_port = local_port.to_i
|
9
|
+
@verbose = verbose
|
10
|
+
@tmp_dir = tmp_dir
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
ssh_command = SSH_TONNEL_COMMAND.gsub('{{local_port}}', @local_port.to_s)
|
15
|
+
|
16
|
+
puts "Copying pem file to tmp folder" if @verbose
|
17
|
+
|
18
|
+
tmp_pem = @tmp_dir + '/share.pem'
|
19
|
+
File.open(tmp_pem, 'w') do |file|
|
20
|
+
file.write(File.read(PEM_FILE))
|
21
|
+
end
|
22
|
+
File.chmod(0600, tmp_pem)
|
23
|
+
|
24
|
+
ssh_command.gsub!('{{pem}}', tmp_pem)
|
25
|
+
|
26
|
+
if @verbose
|
27
|
+
puts "EXEC #{ssh_command}"
|
28
|
+
end
|
29
|
+
Open3.popen3(ssh_command) {|stdin, stdout, stderr, wait_thr|
|
30
|
+
@runner_pid = wait_thr.pid # pid of the started process.
|
31
|
+
#p "process_started: #{pid}"
|
32
|
+
Thread.new do
|
33
|
+
begin
|
34
|
+
while line = stdout.gets
|
35
|
+
puts "STDOUT: #{line}" if @verbose
|
36
|
+
end
|
37
|
+
rescue Exception => error
|
38
|
+
puts error.message
|
39
|
+
puts error.backtrace
|
40
|
+
end
|
41
|
+
end
|
42
|
+
Thread.new do
|
43
|
+
begin
|
44
|
+
while line = stderr.gets
|
45
|
+
#puts "STDERR: #{line}"
|
46
|
+
if line =~ /Allocated port/
|
47
|
+
@remote_port = line.match(/Allocated port (\d+)\s/)[1].to_i
|
48
|
+
#puts "Remote port: #{@remote_port}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
rescue Exception => error
|
52
|
+
puts error.message
|
53
|
+
puts error.backtrace
|
54
|
+
end
|
55
|
+
end
|
56
|
+
exit_status = wait_thr.value # Process::Status object returned.
|
57
|
+
puts "SSH tonnel: process stoped: #{exit_status}" if @verbose
|
58
|
+
}
|
59
|
+
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def run_async
|
64
|
+
@runner_thread = Thread.new do
|
65
|
+
begin
|
66
|
+
run
|
67
|
+
rescue Exception => error
|
68
|
+
puts error.message
|
69
|
+
puts error.backtrace
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def stop_async
|
77
|
+
if @runner_pid && System.process_alive?(@runner_pid, verbose: @verbose)
|
78
|
+
System.stop_process(@runner_pid)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def wait_remote_port_allocated
|
83
|
+
while @remote_port.nil?
|
84
|
+
#print '.'
|
85
|
+
sleep 0.1
|
86
|
+
end
|
87
|
+
|
88
|
+
@remote_port
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module WebService
|
2
|
+
extend self
|
3
|
+
|
4
|
+
#SERVICE_URL = "http://localhost:3000/dblinks"
|
5
|
+
SERVICE_URL = "http://dblink.r15.railsrumble.com/dblinks"
|
6
|
+
|
7
|
+
def register_link(database_url, verbose: false)
|
8
|
+
require 'uri'
|
9
|
+
require 'net/http'
|
10
|
+
|
11
|
+
uri = URI(SERVICE_URL)
|
12
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
13
|
+
request = Net::HTTP::Post.new(uri.path)
|
14
|
+
|
15
|
+
params = {
|
16
|
+
database_url: database_url
|
17
|
+
}
|
18
|
+
|
19
|
+
if verbose
|
20
|
+
puts "HTTP POST: #{SERVICE_URL} #{JSON.generate(params)}"
|
21
|
+
end
|
22
|
+
|
23
|
+
response = http.post(uri.path, URI.encode_www_form(params))
|
24
|
+
|
25
|
+
if verbose
|
26
|
+
puts response.class
|
27
|
+
puts response.body
|
28
|
+
end
|
29
|
+
|
30
|
+
response_json = JSON.parse(response.body)
|
31
|
+
response_json
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/pgbouncer
ADDED
Binary file
|
data/share.pem
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEpQIBAAKCAQEA0Obx6lLUY/tgzQ2RJex95iwzm19iyfWHvQ6MA9syRyaDjiyS
|
3
|
+
+0We5xtJeX+jdi+oI18CFgNMklWj3/PXD3/MDGKNTIlGXAfBLK3VjqyQlHch+jAL
|
4
|
+
PI6kZvMMcWVIlg2eCMxeIeoT1cmYbemoEWNltmzd4i+w7C9qUKPktORhlWuGp6Aj
|
5
|
+
kBmr8K7zFqvO5dQCeOowvmwvNvU106xc+YolIYvQc3cPqgWgqP3Q1ejg3JfzglZr
|
6
|
+
Z4kl/znCtdOoyGMNyq5V5XtJ6wq597BnXFO48mM4i8nwODoQM8itGEmIJJbgfzdw
|
7
|
+
5TwKLVODqNRfw1nECUe0rHiK/75rdKvrJiiB6QIDAQABAoIBAGUsNGWEEBEMIemt
|
8
|
+
dGqliqP7LFNHcxTUsRTDAtSrSEJlSLmGpPjE0xyifoc1ZflLQ2c+EEiQ+Hi2uVMx
|
9
|
+
SSrcffpRkJVMtqLDrq7w0cjzkmIVAIOm2QpPGxgxBgm1P95MYglo0P4j289if0xc
|
10
|
+
KWWiehRLzXpKFqmMnTTI628gXJJ8TQ575UsxOeJ0xTw255XhSYcxmkNOArfHstiI
|
11
|
+
Y6kRS2h0wE+KWwLeBn2UTsmpUpqxYRiVlq4yydhd5yWZpgV1RD7AtpOuJ8g8fDRI
|
12
|
+
9PCnXZaLFGawy2bmpsYHxod4pkSlKStJVwV0Z3cY8TPQ+iUy0rv9LtBantcsQBlc
|
13
|
+
sJjK8AECgYEA5/G+/Gw/HPgUH+BlKKJoYAtYXmjx9i18b97KuBfqm8JrHOrl3H2U
|
14
|
+
AT0VTOrF04/eVER5l822gL3bPhGOYh/8qmPytzxyx8xLWh4rJYAQxN8U5G2A8O+Y
|
15
|
+
Gr5NykIK9cRwr2cVOXVmuN5oRIxWdd+KpF2iUgKI/IUM9uLN+MF3q2kCgYEA5pFq
|
16
|
+
ZdUtqDPhlBEsY8PIz/Isif6wqjr5gp+mqtaMsRJ4vQxmqGeB53dOde7g4VOIU8PC
|
17
|
+
1Ee0MJHuBiVYUE+3lA1scBISpJbK8/V7gRTIRlQVO658upFeAFNkAfnWD4rN6G+0
|
18
|
+
gbbIRrr2JhHcKvHunLIhovMow4k37gYRnW9n0oECgYEA0w2EjcgpvcB2/3J+WfU+
|
19
|
+
KHbqWC4CTdGh82G08MLCdiz4rn2H35RdQqtEYJMYo6O72M3eHqXPNWKMxvPOrbcR
|
20
|
+
gDImP1m/z9IzlrLw69SWba3YIusJczNxIpSb0Dk8I9o190VDaN9NM1cdZZKVcfiQ
|
21
|
+
/XM+KoWoBK9E1roWVg4lSFECgYEAmz3Bo7r7OMtl0OoXNzz1kn1+W5Nuo899e+O+
|
22
|
+
tjwrEnZbcP5OiIjYwF5mJ6WlKFGz8qJWJdP5kNJQrzap1bk246p+Hp8sikE1eTkJ
|
23
|
+
5Ql3J1Mp0LtVI9EIt7NuHJ548JIGvb+oi8Xj5yuu3EceNJQcksilOS/7fFyBmfPg
|
24
|
+
HF6MSwECgYEAx/K84j3mE7sO+CRpjjSZyXw+ETKOhA2f1QogpHIn9/vhztAynj4g
|
25
|
+
dn6j3SntqoMK3DNK8LbPzBoKlPr+Y3IOSTgSpN1RGQTp61ikrIG3K+N7vav382PZ
|
26
|
+
tyv7MiEyLATbci1VExU/pn3Qvcv6s89fzR3ThWX3UlyC6c+uO0qIaFs=
|
27
|
+
-----END RSA PRIVATE KEY-----
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dblink
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.2'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pavel Evstigneev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-08 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: CLI utility to share access to your local PostgreSQL server
|
14
|
+
email:
|
15
|
+
- pavel.evst@gmail.com
|
16
|
+
executables:
|
17
|
+
- dblink
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- bin/dblink
|
22
|
+
- config.ini.erb
|
23
|
+
- dblink.gemspec
|
24
|
+
- lib/dblink/color.rb
|
25
|
+
- lib/dblink/network.rb
|
26
|
+
- lib/dblink/pg_bouncer_runner.rb
|
27
|
+
- lib/dblink/psql_runner.rb
|
28
|
+
- lib/dblink/system.rb
|
29
|
+
- lib/dblink/tonnel_runner.rb
|
30
|
+
- lib/dblink/web_service.rb
|
31
|
+
- pgbouncer
|
32
|
+
- share.pem
|
33
|
+
homepage: http://dblink.r15.railsrumble.com
|
34
|
+
licenses:
|
35
|
+
- MIT
|
36
|
+
metadata: {}
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
requirements: []
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 2.4.5.1
|
54
|
+
signing_key:
|
55
|
+
specification_version: 4
|
56
|
+
summary: Veritrans ruby library
|
57
|
+
test_files: []
|