pagoda 0.3.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.DS_Store +0 -0
- data/Gemfile +3 -3
- data/Gemfile.lock +33 -24
- data/README.md +68 -0
- data/Rakefile +4 -7
- data/bin/pagoda +100 -8
- data/lib/pagoda/cli/commands/clone.rb +13 -0
- data/lib/pagoda/cli/commands/create.rb +13 -0
- data/lib/pagoda/cli/commands/deploy.rb +12 -0
- data/lib/pagoda/cli/commands/destroy.rb +16 -0
- data/lib/pagoda/cli/commands/info.rb +13 -0
- data/lib/pagoda/cli/commands/init.rb +13 -0
- data/lib/pagoda/cli/commands/list.rb +20 -0
- data/lib/pagoda/cli/commands/rename.rb +17 -0
- data/lib/pagoda/cli/commands/rollback.rb +12 -0
- data/lib/pagoda/cli/commands/ssh_key.rb +27 -0
- data/lib/pagoda/cli/commands/tunnel.rb +18 -0
- data/lib/pagoda/cli/commands.rb +11 -0
- data/lib/pagoda/{helpers.rb → cli/core_ext.rb} +10 -118
- data/lib/pagoda/cli/helpers/app.rb +143 -0
- data/lib/pagoda/cli/helpers/base.rb +199 -0
- data/lib/pagoda/cli/helpers/key.rb +69 -0
- data/lib/pagoda/cli/helpers/tunnel.rb +36 -0
- data/lib/pagoda/cli/helpers.rb +127 -0
- data/lib/pagoda/cli/override.rb +23 -0
- data/lib/pagoda/cli/version.rb +5 -0
- data/lib/pagoda/cli.rb +11 -0
- data/lib/pagoda-cli.rb +1 -0
- data/pagoda.gemspec +20 -24
- data/pagoda.rdoc +139 -0
- data/pkg/newpagoda-0.1.10.gem +0 -0
- data/pkg/newpagoda-0.1.11.gem +0 -0
- data/pkg/newpagoda-0.1.12.gem +0 -0
- data/pkg/newpagoda-0.1.13.gem +0 -0
- data/pkg/newpagoda-0.1.15.gem +0 -0
- data/pkg/newpagoda-0.1.4.gem +0 -0
- data/pkg/newpagoda-0.1.6.gem +0 -0
- data/pkg/newpagoda-0.1.7.gem +0 -0
- data/pkg/newpagoda-0.1.8.gem +0 -0
- data/pkg/newpagoda-0.1.9.gem +0 -0
- data/pkg/newpagoda-0.5.0.gem +0 -0
- data/spec/lib/helper_spec.rb +32 -0
- data/spec/lib/helpers/app_spec.rb +104 -0
- data/spec/lib/helpers/base_spec.rb +27 -0
- data/spec/lib/helpers/key_spec.rb +42 -0
- data/spec/lib/helpers/tunnel_spec.rb +30 -0
- data/spec/spec_helper.rb +17 -0
- metadata +74 -50
- data/.bundle/config +0 -1
- data/README +0 -3
- data/lib/pagoda/client.rb +0 -225
- data/lib/pagoda/command.rb +0 -96
- data/lib/pagoda/commands/app.rb +0 -247
- data/lib/pagoda/commands/auth.rb +0 -149
- data/lib/pagoda/commands/base.rb +0 -184
- data/lib/pagoda/commands/db.rb +0 -18
- data/lib/pagoda/commands/help.rb +0 -100
- data/lib/pagoda/commands/tunnel.rb +0 -49
- data/lib/pagoda/tunnel_proxy.rb +0 -130
- data/lib/pagoda/version.rb +0 -3
- data/lib/pagoda.rb +0 -3
- data/spec/base.rb +0 -21
- data/spec/client_spec.rb +0 -255
- data/spec/command_spec.rb +0 -26
- data/spec/commands/auth_spec.rb +0 -57
data/lib/pagoda/commands/auth.rb
DELETED
@@ -1,149 +0,0 @@
|
|
1
|
-
module Pagoda::Command
|
2
|
-
class Auth < Base
|
3
|
-
attr_accessor :credentials
|
4
|
-
|
5
|
-
def initialize(args)
|
6
|
-
@args = args
|
7
|
-
check_for_credentials
|
8
|
-
end
|
9
|
-
|
10
|
-
|
11
|
-
def client
|
12
|
-
@client ||= init_client
|
13
|
-
end
|
14
|
-
|
15
|
-
def init_client
|
16
|
-
client = Pagoda::Client.new(user, password)
|
17
|
-
client.on_warning { |message| self.display("\n#{message}\n\n") }
|
18
|
-
client
|
19
|
-
end
|
20
|
-
|
21
|
-
# just a stub; will raise if not authenticated
|
22
|
-
def check
|
23
|
-
client.app_list
|
24
|
-
end
|
25
|
-
|
26
|
-
def check_for_credentials
|
27
|
-
if option_value("-u", "--username") && option_value("-p", "--password")
|
28
|
-
reauthorize
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def reauthorize
|
33
|
-
@credentials = ask_for_credentials
|
34
|
-
write_credentials
|
35
|
-
end
|
36
|
-
|
37
|
-
def user # :nodoc:
|
38
|
-
get_credentials
|
39
|
-
@credentials[0]
|
40
|
-
end
|
41
|
-
|
42
|
-
def password # :nodoc:
|
43
|
-
get_credentials
|
44
|
-
@credentials[1]
|
45
|
-
end
|
46
|
-
|
47
|
-
def credentials_file
|
48
|
-
"#{home_directory}/.pagoda/credentials"
|
49
|
-
end
|
50
|
-
|
51
|
-
def get_credentials # :nodoc:
|
52
|
-
return if @credentials
|
53
|
-
unless @credentials = read_credentials
|
54
|
-
@credentials = ask_for_credentials
|
55
|
-
save_credentials
|
56
|
-
end
|
57
|
-
@credentials
|
58
|
-
end
|
59
|
-
|
60
|
-
def read_credentials
|
61
|
-
File.exists?(credentials_file) and File.read(credentials_file).split("\n")
|
62
|
-
end
|
63
|
-
|
64
|
-
def echo_off
|
65
|
-
system "stty -echo"
|
66
|
-
end
|
67
|
-
|
68
|
-
def echo_on
|
69
|
-
system "stty echo"
|
70
|
-
end
|
71
|
-
|
72
|
-
def ask_for_credentials
|
73
|
-
unless username = option_value("-u", "--username")
|
74
|
-
username = ask "Username: "
|
75
|
-
end
|
76
|
-
unless password = option_value("-p", "--password")
|
77
|
-
display "Password: ", false
|
78
|
-
password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
|
79
|
-
end
|
80
|
-
[username, password] # return
|
81
|
-
end
|
82
|
-
|
83
|
-
def ask_for_password
|
84
|
-
echo_off
|
85
|
-
password = ask
|
86
|
-
puts
|
87
|
-
echo_on
|
88
|
-
return password
|
89
|
-
end
|
90
|
-
|
91
|
-
def ask_for_password_on_windows
|
92
|
-
require "Win32API"
|
93
|
-
char = nil
|
94
|
-
password = ''
|
95
|
-
|
96
|
-
while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
|
97
|
-
break if char == 10 || char == 13 # received carriage return or newline
|
98
|
-
if char == 127 || char == 8 # backspace and delete
|
99
|
-
password.slice!(-1, 1)
|
100
|
-
else
|
101
|
-
# windows might throw a -1 at us so make sure to handle RangeError
|
102
|
-
(password << char.chr) rescue RangeError
|
103
|
-
end
|
104
|
-
end
|
105
|
-
return password
|
106
|
-
end
|
107
|
-
|
108
|
-
def save_credentials
|
109
|
-
begin
|
110
|
-
write_credentials
|
111
|
-
Pagoda::Command.run_internal('auth:check', args)
|
112
|
-
rescue RestClient::Unauthorized => e
|
113
|
-
delete_credentials
|
114
|
-
raise e unless retry_login?
|
115
|
-
|
116
|
-
error "Authentication failed"
|
117
|
-
@credentials = ask_for_credentials
|
118
|
-
@client = init_client
|
119
|
-
retry
|
120
|
-
rescue Exception => e
|
121
|
-
delete_credentials
|
122
|
-
raise e
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def retry_login?
|
127
|
-
@login_attempts ||= 0
|
128
|
-
@login_attempts += 1
|
129
|
-
@login_attempts < 3
|
130
|
-
end
|
131
|
-
|
132
|
-
def write_credentials
|
133
|
-
FileUtils.mkdir_p(File.dirname(credentials_file))
|
134
|
-
File.open(credentials_file, 'w') do |file|
|
135
|
-
file.puts self.credentials
|
136
|
-
end
|
137
|
-
set_credentials_permissions
|
138
|
-
end
|
139
|
-
|
140
|
-
def set_credentials_permissions
|
141
|
-
FileUtils.chmod 0700, File.dirname(credentials_file)
|
142
|
-
FileUtils.chmod 0600, credentials_file
|
143
|
-
end
|
144
|
-
|
145
|
-
def delete_credentials
|
146
|
-
FileUtils.rm_f(credentials_file)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
data/lib/pagoda/commands/base.rb
DELETED
@@ -1,184 +0,0 @@
|
|
1
|
-
require 'iniparse'
|
2
|
-
|
3
|
-
module Pagoda
|
4
|
-
module Command
|
5
|
-
class Base
|
6
|
-
include Pagoda::Helpers
|
7
|
-
|
8
|
-
attr_accessor :args
|
9
|
-
|
10
|
-
def initialize(args)
|
11
|
-
@args = args
|
12
|
-
end
|
13
|
-
|
14
|
-
def client
|
15
|
-
@client ||= Pagoda::Command.run_internal('auth:client', args)
|
16
|
-
end
|
17
|
-
|
18
|
-
def shell(cmd)
|
19
|
-
FileUtils.cd(Dir.pwd) {|d| return `#{cmd}`}
|
20
|
-
end
|
21
|
-
|
22
|
-
def app(soft_fail=false)
|
23
|
-
if override = option_value("-a", "--app")
|
24
|
-
return override
|
25
|
-
else
|
26
|
-
if name = find_app
|
27
|
-
return name
|
28
|
-
else
|
29
|
-
if locate_app_root
|
30
|
-
if extract_git_clone_url
|
31
|
-
return false if soft_fail
|
32
|
-
errors = []
|
33
|
-
errors << "This repo is either not launched, or not paired with a launched app"
|
34
|
-
errors << ""
|
35
|
-
errors << "To launch this app run 'pagoda launch <app-name>'"
|
36
|
-
errors << ""
|
37
|
-
errors << "To pair this project with a deployed app, run 'pagoda pair <app-name>'"
|
38
|
-
errors << ""
|
39
|
-
errors << "To see a list of currently deployed apps, run 'pagoda list'"
|
40
|
-
error errors
|
41
|
-
else
|
42
|
-
return false if soft_fail
|
43
|
-
errors = []
|
44
|
-
errors << "It appears you are using git (fantastic)."
|
45
|
-
errors << "However we only support git repos hosted with github."
|
46
|
-
errors << "Please ensure your repo is hosted with github."
|
47
|
-
errors << ""
|
48
|
-
errors << "If you are trying to reference a specific app, try argument: -a <app-name>"
|
49
|
-
error errors
|
50
|
-
end
|
51
|
-
else
|
52
|
-
return false if soft_fail
|
53
|
-
errors = []
|
54
|
-
errors << "Unable to find git config in this directory or in any parent directory"
|
55
|
-
errors << ""
|
56
|
-
errors << "If you are trying to reference a specific app, try argument: -a <app-name>"
|
57
|
-
error errors
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
name
|
62
|
-
end
|
63
|
-
|
64
|
-
def parse_branch
|
65
|
-
option_value("-b", "--branch") || find_branch
|
66
|
-
end
|
67
|
-
|
68
|
-
def parse_commit
|
69
|
-
option_value("-c", "--commit") || find_commit
|
70
|
-
end
|
71
|
-
|
72
|
-
def find_app
|
73
|
-
read_apps.each do |line|
|
74
|
-
app = line.split(" ")
|
75
|
-
return app[0] if app[2] == locate_app_root
|
76
|
-
end
|
77
|
-
false
|
78
|
-
end
|
79
|
-
|
80
|
-
def find_branch
|
81
|
-
begin
|
82
|
-
line = File.new("#{locate_app_root}/.git/HEAD").gets
|
83
|
-
line.strip.split(' ').last.split("/").last
|
84
|
-
rescue
|
85
|
-
nil
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def find_commit
|
90
|
-
begin
|
91
|
-
File.new("#{locate_app_root}/.git/refs/heads/#{parse_branch}").gets.strip
|
92
|
-
rescue
|
93
|
-
nil
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def read_apps
|
98
|
-
return [] if !File.exists?(apps_file)
|
99
|
-
File.read(apps_file).split(/\n/).inject([]) {|apps, line| apps << line if line.include?("git@github.com"); apps}
|
100
|
-
end
|
101
|
-
|
102
|
-
def write_app(name, git_url=nil, app_root=nil)
|
103
|
-
git_url = extract_git_clone_url unless git_url
|
104
|
-
app_root = locate_app_root unless app_root
|
105
|
-
FileUtils.mkdir_p(File.dirname(apps_file)) if !File.exists?(apps_file)
|
106
|
-
current_apps = read_apps
|
107
|
-
File.open(apps_file, 'w') do |file|
|
108
|
-
current_apps.each do |app|
|
109
|
-
file.puts app
|
110
|
-
end
|
111
|
-
file.puts "#{name} #{git_url} #{app_root}"
|
112
|
-
end
|
113
|
-
set_apps_file_permissions
|
114
|
-
end
|
115
|
-
alias :add_app :write_app
|
116
|
-
|
117
|
-
def remove_app(name)
|
118
|
-
current_apps = read_apps
|
119
|
-
current_apps.delete_if do |app|
|
120
|
-
app.split(" ")[0] == name
|
121
|
-
end
|
122
|
-
File.open(apps_file, 'w') do |file|
|
123
|
-
current_apps.each do |app|
|
124
|
-
file.puts app
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def set_apps_file_permissions
|
130
|
-
FileUtils.chmod 0700, File.dirname(apps_file)
|
131
|
-
FileUtils.chmod 0600, apps_file
|
132
|
-
end
|
133
|
-
|
134
|
-
def apps_file
|
135
|
-
"#{home_directory}/.pagoda/apps"
|
136
|
-
end
|
137
|
-
|
138
|
-
def extract_possible_name
|
139
|
-
cleanup_name(extract_git_clone_url.split(":")[1].split("/")[1].split(".")[0])
|
140
|
-
end
|
141
|
-
|
142
|
-
def cleanup_name(name)
|
143
|
-
name.gsub(/-/, '').gsub(/_/, '').gsub(/ /, '').downcase
|
144
|
-
end
|
145
|
-
|
146
|
-
def extract_git_clone_url(soft=false)
|
147
|
-
begin
|
148
|
-
url = IniParse.parse( File.read("#{locate_app_root}/.git/config") )['remote "origin"']["url"]
|
149
|
-
raise unless url.match(/^((git@github.com:)|(.*:)|((http|https):\/\/.*github.com\/)|(git:\/\/github.com\/))(.*)\/(.*).git$/i)
|
150
|
-
url
|
151
|
-
rescue Exception => e
|
152
|
-
return false
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def version
|
157
|
-
display Client.gem_version_string
|
158
|
-
end
|
159
|
-
|
160
|
-
def locate_app_root(dir=Dir.pwd)
|
161
|
-
return dir if File.exists? "#{dir}/.git/config"
|
162
|
-
parent = dir.split('/')[0..-2].join('/')
|
163
|
-
return false if parent.empty?
|
164
|
-
locate_app_root(parent)
|
165
|
-
end
|
166
|
-
|
167
|
-
def extract_option(options, default=true)
|
168
|
-
values = options.is_a?(Array) ? options : [options]
|
169
|
-
return unless opt_index = args.select { |a| values.include? a }.first
|
170
|
-
opt_position = args.index(opt_index) + 1
|
171
|
-
if args.size > opt_position && opt_value = args[opt_position]
|
172
|
-
if opt_value.include?('--')
|
173
|
-
opt_value = nil
|
174
|
-
else
|
175
|
-
args.delete_at(opt_position)
|
176
|
-
end
|
177
|
-
end
|
178
|
-
opt_value ||= default
|
179
|
-
args.delete(opt_index)
|
180
|
-
block_given? ? yield(opt_value) : opt_value
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
data/lib/pagoda/commands/db.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
module Pagoda::Command
|
2
|
-
class Db < Base
|
3
|
-
|
4
|
-
def create
|
5
|
-
app
|
6
|
-
display
|
7
|
-
client.app_database_create(app)
|
8
|
-
display "+> creating a sql database on pagodabox...", false
|
9
|
-
loop_transaction
|
10
|
-
display "+> created"
|
11
|
-
display
|
12
|
-
end
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
data/lib/pagoda/commands/help.rb
DELETED
@@ -1,100 +0,0 @@
|
|
1
|
-
module Pagoda::Command
|
2
|
-
class Help < Base
|
3
|
-
def index
|
4
|
-
display %{
|
5
|
-
Pagoda
|
6
|
-
|
7
|
-
NAME
|
8
|
-
pagoda -- command line utility for pagodabox.com
|
9
|
-
|
10
|
-
SYNOPSIS
|
11
|
-
pagoda [command] [parameters]
|
12
|
-
|
13
|
-
DESCRIPTION
|
14
|
-
|
15
|
-
If no operands are given, we will attempt to pull data from the current
|
16
|
-
directory. If more than one operand is given, non-directory operands are
|
17
|
-
displayed first.
|
18
|
-
|
19
|
-
The following options are available:
|
20
|
-
|
21
|
-
COMMANDS
|
22
|
-
|
23
|
-
list # list your apps
|
24
|
-
deploy # Deploy your current state to pagoda
|
25
|
-
launch <name> # create (register) a new app
|
26
|
-
info # display info about an app
|
27
|
-
destroy # remove app
|
28
|
-
rollback # rollback app
|
29
|
-
tunnel # create a tunnel to your database on pagoda
|
30
|
-
|
31
|
-
|
32
|
-
PARAMETERS
|
33
|
-
|
34
|
-
---------------------------
|
35
|
-
GLOBAL :
|
36
|
-
---------------------------
|
37
|
-
-a <name> | --app=<name>
|
38
|
-
Set the application name (Only necessary when not in repo dir).
|
39
|
-
|
40
|
-
-u <username> | --username=<username>
|
41
|
-
When set, will not attempt to save your username. Also over-rides
|
42
|
-
any saved username.
|
43
|
-
|
44
|
-
-p <password> | --password=<password>
|
45
|
-
When set, will not attempt to save your password. Also over-rides
|
46
|
-
any saved password.
|
47
|
-
|
48
|
-
-f
|
49
|
-
Executes all commands without confirmation request.
|
50
|
-
|
51
|
-
---------------------------
|
52
|
-
DEPLOYING - pagoda deploy :
|
53
|
-
---------------------------
|
54
|
-
-b <branch> | --branch=<branch>
|
55
|
-
Specify the branch name. By default uses the branch
|
56
|
-
your local repo is on.
|
57
|
-
|
58
|
-
-c <commit> | --commit=<commit>
|
59
|
-
Specify the commit id. By default uses the commit HEAD is set to.
|
60
|
-
|
61
|
-
--latest
|
62
|
-
Will attempt to deploy to the latest commit on github rather than
|
63
|
-
your local repo's current commit.
|
64
|
-
|
65
|
-
---------------------------
|
66
|
-
TUNNELING - pagoda tunnel :
|
67
|
-
---------------------------
|
68
|
-
|
69
|
-
-t <type> | --type=<type>
|
70
|
-
Specify the tunnel type. (ex:mysql)
|
71
|
-
|
72
|
-
-n <instance> | --name=<instance>
|
73
|
-
Specify the instance name you want to operate on used for
|
74
|
-
database instance
|
75
|
-
|
76
|
-
|
77
|
-
EXAMPLES
|
78
|
-
launch an application on pagoda from inside the clone folder:
|
79
|
-
(must be done inside your repo folder)
|
80
|
-
pagoda launch <app name>
|
81
|
-
|
82
|
-
list your applications:
|
83
|
-
|
84
|
-
pagoda list
|
85
|
-
|
86
|
-
create tunnel to your database:
|
87
|
-
(must be inside your repo folder or specify app)
|
88
|
-
|
89
|
-
pagoda tunnel -a <app name> -t mysql -n <database name>
|
90
|
-
|
91
|
-
destroy an application:
|
92
|
-
(must be inside your repo folder or specify app)
|
93
|
-
|
94
|
-
pagoda destroy
|
95
|
-
|
96
|
-
|
97
|
-
}
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
module Pagoda::Command
|
2
|
-
class Tunnel < Auth
|
3
|
-
|
4
|
-
def index
|
5
|
-
app
|
6
|
-
type = option_value("-t", "--type") || 'mysql'
|
7
|
-
Pagoda::Command.run_internal("tunnel:#{type}", args)
|
8
|
-
end
|
9
|
-
|
10
|
-
def mysql
|
11
|
-
instance_name = option_value("-n", "--name")
|
12
|
-
unless instance_name
|
13
|
-
# try to find mysql instances here
|
14
|
-
dbs = client.app_databases(app)
|
15
|
-
if dbs.length == 0
|
16
|
-
errors = []
|
17
|
-
errors << "It looks like you don't have any MySQL instances for #{app}"
|
18
|
-
errors << "Feel free to add one in the admin panel (10 MB Free)"
|
19
|
-
error errors
|
20
|
-
elsif dbs.length == 1
|
21
|
-
instance_name = dbs.first[:name]
|
22
|
-
else
|
23
|
-
errors = []
|
24
|
-
errors << "Multiple MySQL instances found"
|
25
|
-
errors << ""
|
26
|
-
dbs.each do |instance|
|
27
|
-
errors << "-> #{instance[:name]}"
|
28
|
-
end
|
29
|
-
errors << ""
|
30
|
-
errors << "Please specify which instance you would like to use."
|
31
|
-
errors << ""
|
32
|
-
errors << "ex: pagoda tunnel -n #{dbs[0][:name]}"
|
33
|
-
error errors
|
34
|
-
end
|
35
|
-
end
|
36
|
-
display
|
37
|
-
display "+> Authenticating Database Ownership"
|
38
|
-
|
39
|
-
if client.database_exists?(app, instance_name)
|
40
|
-
Pagoda::TunnelProxy.new(:mysql, user, password, app, instance_name).start
|
41
|
-
else
|
42
|
-
errors = []
|
43
|
-
errors << "Security exception -"
|
44
|
-
errors << "Either the MySQL instance doesn't exist or you are unauthorized"
|
45
|
-
error errors
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
data/lib/pagoda/tunnel_proxy.rb
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
require "socket"
|
2
|
-
require 'openssl'
|
3
|
-
|
4
|
-
module Pagoda
|
5
|
-
class TunnelProxy
|
6
|
-
include Pagoda::Helpers
|
7
|
-
|
8
|
-
def initialize(type, user, pass, app, instance)
|
9
|
-
@type = type
|
10
|
-
@user = user
|
11
|
-
@pass = pass
|
12
|
-
@app = app
|
13
|
-
@instance = instance
|
14
|
-
end
|
15
|
-
|
16
|
-
def start
|
17
|
-
|
18
|
-
[:INT, :TERM].each do |sig|
|
19
|
-
Signal.trap(sig) do
|
20
|
-
display "Tunnel Closed."
|
21
|
-
display "-----------------------------------------------"
|
22
|
-
display
|
23
|
-
exit
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
local_port = 3307
|
28
|
-
remote_host = "www.pagodabox.com"
|
29
|
-
remote_port = 3306
|
30
|
-
|
31
|
-
max_threads = 20
|
32
|
-
threads = []
|
33
|
-
|
34
|
-
chunk = 4096*4096
|
35
|
-
|
36
|
-
#puts "start TCP server"
|
37
|
-
display "+> Opening Tunnel"
|
38
|
-
bound = false
|
39
|
-
until bound
|
40
|
-
begin
|
41
|
-
proxy_server = TCPServer.new('0.0.0.0', local_port)
|
42
|
-
bound = true
|
43
|
-
rescue Errno::EADDRINUSE
|
44
|
-
local_port += 1
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
display
|
49
|
-
display "Tunnel Established! Accepting connections on :"
|
50
|
-
display "-----------------------------------------------"
|
51
|
-
display
|
52
|
-
display "HOST : 127.0.0.1 (or localhost)", true, 2
|
53
|
-
display "PORT : #{local_port}", true, 2
|
54
|
-
display "USER : (found in pagodabox dashboard)", true, 2
|
55
|
-
display "PASS : (found in pagodabox dashboard)", true, 2
|
56
|
-
display
|
57
|
-
display "-----------------------------------------------"
|
58
|
-
display "(note : ctrl-c To close this tunnel)"
|
59
|
-
|
60
|
-
loop do
|
61
|
-
|
62
|
-
#puts "start a new thread for every client connection"
|
63
|
-
threads << Thread.new(proxy_server.accept) do |client_socket|
|
64
|
-
|
65
|
-
begin
|
66
|
-
# puts "client connection"
|
67
|
-
begin
|
68
|
-
server_socket = TCPSocket.new(remote_host, remote_port)
|
69
|
-
ssl_context = OpenSSL::SSL::SSLContext.new()
|
70
|
-
ssl_socket = OpenSSL::SSL::SSLSocket.new(server_socket, ssl_context)
|
71
|
-
ssl_socket.sync_close = true
|
72
|
-
ssl_socket.connect
|
73
|
-
rescue Errno::ECONNREFUSED
|
74
|
-
# puts "connection refused"
|
75
|
-
client_socket.close
|
76
|
-
raise
|
77
|
-
end
|
78
|
-
|
79
|
-
# puts "authenticate"
|
80
|
-
if ssl_socket.readpartial(chunk) == "auth"
|
81
|
-
# puts "authentication"
|
82
|
-
ssl_socket.write "auth=#{@user}:#{@pass}:#{@app}:#{@instance}"
|
83
|
-
if ssl_socket.readpartial(chunk) == "success"
|
84
|
-
# puts "successful connection"
|
85
|
-
else
|
86
|
-
# puts "failed connection"
|
87
|
-
end
|
88
|
-
else
|
89
|
-
# puts "danger will robbinson! abort!"
|
90
|
-
end
|
91
|
-
|
92
|
-
loop do
|
93
|
-
# puts "wait for data on either socket"
|
94
|
-
(ready_sockets, dummy, dummy) = IO.select([client_socket, ssl_socket])
|
95
|
-
|
96
|
-
# puts "full duplex connection until data stream ends"
|
97
|
-
begin
|
98
|
-
ready_sockets.each do |socket|
|
99
|
-
data = socket.readpartial(chunk)
|
100
|
-
if socket == client_socket
|
101
|
-
#puts "read from client and write to server"
|
102
|
-
ssl_socket.write data
|
103
|
-
ssl_socket.flush
|
104
|
-
else
|
105
|
-
#puts "read from server and write to client."
|
106
|
-
client_socket.write data
|
107
|
-
client_socket.flush
|
108
|
-
end
|
109
|
-
end
|
110
|
-
rescue EOFError
|
111
|
-
break
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
rescue StandardError => error
|
116
|
-
end
|
117
|
-
client_socket.close rescue StandardError
|
118
|
-
ssl_socket.close rescue StandardError
|
119
|
-
end
|
120
|
-
|
121
|
-
#puts "clean up the dead threads, and wait until we have available threads"
|
122
|
-
threads = threads.select { |thread| thread.alive? ? true : (thread.join; false) }
|
123
|
-
while threads.size >= max_threads
|
124
|
-
sleep 1
|
125
|
-
threads = threads.select { |thread| thread.alive? ? true : (thread.join; false) }
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
data/lib/pagoda/version.rb
DELETED
data/lib/pagoda.rb
DELETED
data/spec/base.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'webmock/rspec'
|
2
|
-
|
3
|
-
require 'pagoda/command'
|
4
|
-
require 'pagoda/commands/base'
|
5
|
-
|
6
|
-
Dir["#{File.dirname(__FILE__)}/../lib/pagoda/commands/*"].each { |c| require c }
|
7
|
-
|
8
|
-
include WebMock::API
|
9
|
-
|
10
|
-
def stub_api_request(method, path)
|
11
|
-
stub_request(method, "http://www.pagodabox.com#{path}")
|
12
|
-
end
|
13
|
-
|
14
|
-
def prepare_command(klass)
|
15
|
-
command = klass.new(['--app', 'myapp'])
|
16
|
-
command.stub!(:args).and_return([])
|
17
|
-
command.stub!(:display)
|
18
|
-
command.stub!(:pagoda).and_return(mock('pagoda client', :host => 'pagoda.com'))
|
19
|
-
command.stub!(:extract_app).and_return('myapp')
|
20
|
-
command
|
21
|
-
end
|