pagoda 0.3.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.DS_Store +0 -0
  2. data/Gemfile +3 -3
  3. data/Gemfile.lock +33 -24
  4. data/README.md +68 -0
  5. data/Rakefile +4 -7
  6. data/bin/pagoda +100 -8
  7. data/lib/pagoda/cli/commands/clone.rb +13 -0
  8. data/lib/pagoda/cli/commands/create.rb +13 -0
  9. data/lib/pagoda/cli/commands/deploy.rb +12 -0
  10. data/lib/pagoda/cli/commands/destroy.rb +16 -0
  11. data/lib/pagoda/cli/commands/info.rb +13 -0
  12. data/lib/pagoda/cli/commands/init.rb +13 -0
  13. data/lib/pagoda/cli/commands/list.rb +20 -0
  14. data/lib/pagoda/cli/commands/rename.rb +17 -0
  15. data/lib/pagoda/cli/commands/rollback.rb +12 -0
  16. data/lib/pagoda/cli/commands/ssh_key.rb +27 -0
  17. data/lib/pagoda/cli/commands/tunnel.rb +18 -0
  18. data/lib/pagoda/cli/commands.rb +11 -0
  19. data/lib/pagoda/{helpers.rb → cli/core_ext.rb} +10 -118
  20. data/lib/pagoda/cli/helpers/app.rb +143 -0
  21. data/lib/pagoda/cli/helpers/base.rb +199 -0
  22. data/lib/pagoda/cli/helpers/key.rb +69 -0
  23. data/lib/pagoda/cli/helpers/tunnel.rb +36 -0
  24. data/lib/pagoda/cli/helpers.rb +127 -0
  25. data/lib/pagoda/cli/override.rb +23 -0
  26. data/lib/pagoda/cli/version.rb +5 -0
  27. data/lib/pagoda/cli.rb +11 -0
  28. data/lib/pagoda-cli.rb +1 -0
  29. data/pagoda.gemspec +20 -24
  30. data/pagoda.rdoc +139 -0
  31. data/pkg/newpagoda-0.1.10.gem +0 -0
  32. data/pkg/newpagoda-0.1.11.gem +0 -0
  33. data/pkg/newpagoda-0.1.12.gem +0 -0
  34. data/pkg/newpagoda-0.1.13.gem +0 -0
  35. data/pkg/newpagoda-0.1.15.gem +0 -0
  36. data/pkg/newpagoda-0.1.4.gem +0 -0
  37. data/pkg/newpagoda-0.1.6.gem +0 -0
  38. data/pkg/newpagoda-0.1.7.gem +0 -0
  39. data/pkg/newpagoda-0.1.8.gem +0 -0
  40. data/pkg/newpagoda-0.1.9.gem +0 -0
  41. data/pkg/newpagoda-0.5.0.gem +0 -0
  42. data/spec/lib/helper_spec.rb +32 -0
  43. data/spec/lib/helpers/app_spec.rb +104 -0
  44. data/spec/lib/helpers/base_spec.rb +27 -0
  45. data/spec/lib/helpers/key_spec.rb +42 -0
  46. data/spec/lib/helpers/tunnel_spec.rb +30 -0
  47. data/spec/spec_helper.rb +17 -0
  48. metadata +74 -50
  49. data/.bundle/config +0 -1
  50. data/README +0 -3
  51. data/lib/pagoda/client.rb +0 -225
  52. data/lib/pagoda/command.rb +0 -96
  53. data/lib/pagoda/commands/app.rb +0 -247
  54. data/lib/pagoda/commands/auth.rb +0 -149
  55. data/lib/pagoda/commands/base.rb +0 -184
  56. data/lib/pagoda/commands/db.rb +0 -18
  57. data/lib/pagoda/commands/help.rb +0 -100
  58. data/lib/pagoda/commands/tunnel.rb +0 -49
  59. data/lib/pagoda/tunnel_proxy.rb +0 -130
  60. data/lib/pagoda/version.rb +0 -3
  61. data/lib/pagoda.rb +0 -3
  62. data/spec/base.rb +0 -21
  63. data/spec/client_spec.rb +0 -255
  64. data/spec/command_spec.rb +0 -26
  65. data/spec/commands/auth_spec.rb +0 -57
data/lib/pagoda/client.rb DELETED
@@ -1,225 +0,0 @@
1
- require 'pagoda/version'
2
- require 'rexml/document'
3
- require 'rest_client'
4
- require 'uri'
5
- require 'json/pure' unless {}.respond_to?(:to_json)
6
-
7
- class Pagoda::Client
8
-
9
- attr_reader :user, :password
10
-
11
- class << self
12
- def version
13
- Pagoda::VERSION
14
- end
15
-
16
- def gem_version_string
17
- "pagoda-gem/#{version}"
18
- end
19
- end
20
-
21
- def initialize(user, password)
22
- @user = user
23
- @password = password
24
- end
25
-
26
- def app_list
27
- doc = xml(get("/apps.xml").to_s)
28
- doc.elements['apps'].elements.to_a('//app/').inject([]) do |list, app|
29
- list << {
30
- :name => app.elements['name'].text,
31
- :instances => app.elements['instances'].text,
32
- :git_url => app.elements['git-url'].text
33
- }
34
- end
35
- end
36
-
37
- def database_exists?(app, mysql_instance)
38
- begin
39
- response = get("/apps/#{app}/databases/#{mysql_instance}.xml")
40
- true
41
- rescue RestClient::ResourceNotFound => e
42
- false
43
- end
44
-
45
- end
46
-
47
- def app_database_create(app)
48
- response = post("/apps/#{app}/databases.xml", {:mysql_instance => {:ram => 10}})
49
- end
50
-
51
- def app_create(name, git_url)
52
- doc = xml(post("/apps.xml", {:app => {:name => name, :git_url => git_url}}).to_s)
53
- doc.elements.to_a('//app/*').inject({}) do |hash, element|
54
- case element.name
55
- when "owner"
56
- hash[:owner] = {:username => element.elements['username'].text, :email => element.elements['email'].text}
57
- when "collaborators"
58
- hash[:collaborators] = element.elements.to_a('//collaborator/').inject([]) do |list, collaborator|
59
- list << {:username => collaborator.elements['username'].text, :email => collaborator.elements['email'].text}
60
- end
61
- when "transactions"
62
- hash[:transactions] = element.elements.to_a('//transaction/').inject([]) do |list, transaction|
63
- list << {
64
- :id => transaction.elements["id"].text,
65
- :name => transaction.elements["name"].text,
66
- :description => transaction.elements["description"].text,
67
- :state => transaction.elements["state"].text,
68
- :status => transaction.elements["status"].text
69
- }
70
- end
71
- else
72
- hash[element.name.gsub(/-/, '_').to_sym] = element.text
73
- end
74
- hash
75
- end
76
- end
77
-
78
- def app_info(app)
79
- doc = xml(get("/apps/#{app}.xml").to_s)
80
- doc.elements.to_a('//app/*').inject({}) do |hash, element|
81
- case element.name
82
- when "owner"
83
- hash[:owner] = {:username => element.elements['username'].text, :email => element.elements['email'].text}
84
- when "collaborators"
85
- hash[:collaborators] = element.elements.to_a('//collaborator/').inject([]) do |list, collaborator|
86
- list << {:username => collaborator.elements['username'].text, :email => collaborator.elements['email'].text}
87
- end
88
- when "transactions"
89
- hash[:transactions] = element.elements.to_a('//transaction/').inject([]) do |list, transaction|
90
- list << {
91
- :id => transaction.elements["id"].text,
92
- :name => transaction.elements["name"].text,
93
- :description => transaction.elements["description"].text,
94
- :state => transaction.elements["state"].text,
95
- :status => transaction.elements["status"].text
96
- }
97
- end
98
- else
99
- hash[element.name.gsub(/-/, '_').to_sym] = element.text
100
- end
101
- hash
102
- end
103
- end
104
-
105
- def app_update(app, updates)
106
- put("/apps/#{app}.xml", {:update => updates}).to_s
107
- end
108
-
109
- def app_destroy(app)
110
- delete("/apps/#{app}.xml").to_s
111
- end
112
-
113
- def transaction_list(app)
114
- doc = xml(get("/apps/#{app}/transactions.xml").to_s)
115
- doc.elements['transactions'].elements.to_a('//transaction/').inject([]) do |list, transaction|
116
- list << {
117
- :id => transaction.elements['id'].text,
118
- :name => transaction.elements['name'].text,
119
- :description => transaction.elements['description'].text,
120
- :state => transaction.elements['state'].text,
121
- :status => transaction.elements['status'].text
122
- }
123
- end
124
- end
125
-
126
- def transaction_status(app, transaction)
127
- doc = xml(get("/apps/#{app}/transactions/#{transaction}.xml").to_s)
128
- doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
129
- end
130
-
131
- def rewind(app, places=1)
132
- doc = xml(put("/apps/#{app}/rewind.xml", {:places => places}).to_s)
133
- doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
134
- end
135
-
136
- def fast_forward(app, places=1)
137
- doc = xml(put("/apps/#{app}/fast-forward.xml", {:places => places}).to_s)
138
- doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
139
- end
140
-
141
- def rollback(app)
142
- get("/apps/#{app}/rollback.xml").to_s
143
- end
144
-
145
- def deploy_latest(app)
146
- doc = xml(post("/apps/#{app}/deploy.xml").to_s)
147
- doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
148
- end
149
-
150
- def deploy(app, branch, commit)
151
- doc = xml(post("/apps/#{app}/deploy.xml", {:deploy => {:branch => branch, :commit => commit}}).to_s)
152
- doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
153
- end
154
-
155
- # def deploy(app)
156
- # doc = xml(put("/apps/#{app}/deploy.xml").to_s)
157
- # doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
158
- # end
159
-
160
- def scale_up(app, qty=1)
161
- doc = xml(put("/apps/#{app}/scale-up.xml").to_s)
162
- doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
163
- end
164
-
165
- def scale_down(app, qty=1)
166
- doc = xml(put("/apps/#{app}/scale-down.xml").to_s)
167
- doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
168
- end
169
-
170
- def app_databases(app)
171
- doc = xml(get("/apps/#{app}/databases.xml").to_s)
172
- doc.elements['databases'].elements.to_a('//database/').inject([]) {|list, instance| list << {:name => instance.elements['name'].text} }
173
- end
174
-
175
- def on_warning(&blk)
176
- @warning_callback = blk
177
- end
178
-
179
- protected
180
-
181
- def resource(uri)
182
- RestClient.proxy = ENV['HTTP_PROXY'] || ENV['http_proxy']
183
- if uri =~ /^https?/
184
- RestClient::Resource.new(uri, @user, @password)
185
- else
186
- # RestClient::Resource.new("http://127.0.0.1:3000#{uri}", @user, @password)
187
- RestClient::Resource.new("https://dashboard.pagodabox.com#{uri}", @user, @password)
188
- end
189
- end
190
-
191
- def get(uri, extra_headers={})
192
- process(:get, uri, extra_headers)
193
- end
194
-
195
- def post(uri, payload="", extra_headers={})
196
- process(:post, uri, extra_headers, payload)
197
- end
198
-
199
- def put(uri, payload="", extra_headers={})
200
- process(:put, uri, extra_headers, payload)
201
- end
202
-
203
- def delete(uri, extra_headers={})
204
- process(:delete, uri, extra_headers)
205
- end
206
-
207
- def process(method, uri, extra_headers={}, payload=nil)
208
- headers = pagoda_headers.merge(extra_headers)
209
- args = [method, payload, headers].compact
210
- response = resource(uri).send(*args)
211
- end
212
-
213
- def pagoda_headers
214
- {
215
- 'User-Agent' => self.class.gem_version_string,
216
- 'X-Ruby-Version' => RUBY_VERSION,
217
- 'X-Ruby-Platform' => RUBY_PLATFORM,
218
- }
219
- end
220
-
221
- def xml(raw) # :nodoc:
222
- REXML::Document.new(raw)
223
- end
224
-
225
- end
@@ -1,96 +0,0 @@
1
- require 'pagoda/helpers'
2
- require 'pagoda/tunnel_proxy'
3
- require 'pagoda/commands/base'
4
- require 'pagoda/commands/auth'
5
- require 'pagoda/commands/app'
6
- require 'pagoda/commands/db'
7
- require 'pagoda/commands/help'
8
- require 'pagoda/commands/tunnel'
9
-
10
- module Pagoda
11
- module Command
12
- class InvalidCommand < RuntimeError; end
13
- class CommandFailed < RuntimeError; end
14
-
15
- extend Pagoda::Helpers
16
-
17
- class << self
18
-
19
- def run(command, args, retries=0)
20
- begin
21
- run_internal 'auth:reauthorize', args.dup if retries > 0
22
- run_internal(command, args.dup)
23
- rescue InvalidCommand
24
- error "Unknown command: #{command}. Run 'pagoda help' for usage information."
25
- rescue RestClient::Unauthorized
26
- if retries < 3
27
- STDERR.puts "Authentication failure"
28
- run(command, args, retries+1)
29
- else
30
- error "Authentication failure"
31
- end
32
- rescue RestClient::ResourceNotFound => e
33
- error extract_not_found(e.http_body)
34
- rescue RestClient::RequestFailed => e
35
- error extract_error(e.http_body) unless e.http_code == 402 || e.http_code == 102
36
- rescue RestClient::RequestTimeout
37
- error "API request timed out. Please try again, or contact support@pagodagrid.com if this issue persists."
38
- rescue CommandFailed => e
39
- error e.message
40
- rescue Interrupt => e
41
- error "\n[canceled]"
42
- end
43
- end
44
-
45
- def run_internal(command, args)
46
- klass, method = parse(command)
47
- runner = klass.new(args)
48
- raise InvalidCommand unless runner.respond_to?(method)
49
- runner.send(method)
50
- end
51
-
52
- def parse(command)
53
- parts = command.split(':')
54
- case parts.size
55
- when 1
56
- begin
57
- return eval("Pagoda::Command::#{command.capitalize}"), :index
58
- rescue NameError, NoMethodError
59
- return Pagoda::Command::App, command.to_sym
60
- end
61
- else
62
- begin
63
- const = Pagoda::Command
64
- command = parts.pop
65
- parts.each { |part| const = const.const_get(part.capitalize) }
66
- return const, command.to_sym
67
- rescue NameError
68
- raise InvalidCommand
69
- end
70
- end
71
- end
72
-
73
- def extract_not_found(body)
74
- body =~ /^[\w\s]+ not found$/ ? body : "Resource not found"
75
- end
76
-
77
- def extract_error(body)
78
- msg = parse_error_xml(body) || parse_error_json(body) || 'Internal server error'
79
- end
80
-
81
- def parse_error_xml(body)
82
- xml_errors = REXML::Document.new(body).elements.to_a("//errors/error")
83
- msg = xml_errors.map { |a| a.text }.join(" / ")
84
- return msg unless msg.empty?
85
- rescue Exception
86
- end
87
-
88
- def parse_error_json(body)
89
- json = JSON.parse(body.to_s)
90
- json['error']
91
- rescue JSON::ParserError
92
- end
93
- end
94
-
95
- end
96
- end
@@ -1,247 +0,0 @@
1
- module Pagoda::Command
2
- class App < Base
3
-
4
- def list
5
- apps = client.app_list
6
- if !apps.empty?
7
- display
8
- display "APPS"
9
- display "//////////////////////////////////"
10
- display
11
- apps.each do |app|
12
- display "- #{app[:name]}"
13
- end
14
- else
15
- error ["looks like you haven't launched any apps", "type 'pagoda launch' to launch this project"]
16
- end
17
- display
18
- end
19
-
20
- def create
21
- if app_name = app(true)
22
- error ["This project is already launched and paired to #{app_name}.", "To unpair run 'pagoda unpair'"]
23
- end
24
-
25
- unless locate_app_root
26
- error ["Unable to find git config in this directory or in any parent directory"]
27
- end
28
-
29
- unless clone_url = extract_git_clone_url
30
- errors = []
31
- errors << "It appears you are using git (fantastic)."
32
- errors << "However we only support git repos hosted with github."
33
- errors << "Please ensure your repo is hosted with github."
34
- error errors
35
- end
36
-
37
- unless name = args.dup.shift
38
- error "Please Specify an app name ie. 'pagoda launch awesomeapp'"
39
- end
40
-
41
- display
42
- display "+> Registering #{name}"
43
- app = client.app_create(name, clone_url)
44
- display "+> Launching...", false
45
- loop_transaction(name)
46
- add_app(name, clone_url)
47
- display "+> #{name} launched"
48
-
49
- unless option_value(nil, "--latest")
50
- Pagoda::Command.run_internal("app:deploy", args)
51
- end
52
-
53
- if option_value(nil, "--with-mysql")
54
- Pagoda::Command.run_internal("db:create", args)
55
- end
56
-
57
- display "-----------------------------------------------"
58
- display
59
- display "LIVE URL : http://#{name}.pagodabox.com"
60
- display "ADMIN PANEL : http://dashboard.pagodabox.com"
61
- display
62
- display "-----------------------------------------------"
63
- display
64
-
65
- end
66
- alias :launch :create
67
- alias :register :create
68
-
69
- def destroy
70
- display
71
- if confirm ["Are you totally completely sure you want to delete #{app} forever and ever?", "THIS CANNOT BE UNDONE! (y/n)"]
72
- display "+> Destroying #{app}"
73
- client.app_destroy(app)
74
- display "+> #{app} has been successfully destroyed. RIP #{app}."
75
- remove_app(app)
76
- end
77
- display
78
- end
79
- alias :delete :destroy
80
-
81
- def info
82
- display
83
- info = client.app_info(app)
84
- display "INFO - #{info[:name]}"
85
- display "//////////////////////////////////"
86
- display "name : #{info[:name]}"
87
- display "clone_url : #{info[:git_url]}"
88
- display
89
- display "owner"
90
- display "username : #{info[:owner][:username]}", true, 2
91
- display "email : #{info[:owner][:email]}", true, 2
92
- display
93
- display "collaborators"
94
- if info[:collaborators].any?
95
- info[:collaborators].each_with_index do |collaborator, index|
96
- display "username : #{collaborator[:username]}", true, 2
97
- display "email : #{collaborator[:email]}", true, 2
98
- end
99
- else
100
- display "(none)", true, 2
101
- end
102
- display
103
- end
104
-
105
- def pair
106
-
107
- if app_name = app(true)
108
- error ["This project is paired to #{app_name}.", "To unpair run 'pagoda unpair'"]
109
- end
110
-
111
- unless locate_app_root
112
- error ["Unable to find git config in this directory or in any parent directory"]
113
- end
114
-
115
- unless my_repo = extract_git_clone_url
116
- errors = []
117
- errors << "It appears you are using git (fantastic)."
118
- errors << "However we only support git repos hosted with github."
119
- errors << "Please ensure your repo is hosted with github."
120
- error errors
121
- end
122
-
123
- display
124
- display "+> Locating deployed app with matching git repo"
125
-
126
- apps = client.app_list
127
-
128
- matching_apps = []
129
- apps.each do |a|
130
- if a[:git_url] == my_repo
131
- matching_apps.push a
132
- end
133
- end
134
-
135
- if matching_apps.count > 1
136
- if name = app(true) || args.dup.shift
137
- assign_app = nil
138
- matching_apps.each do |a|
139
- assign_app = a if a[:name] == name
140
- end
141
- if assign_app
142
- display "+> Pairing this repo to deployed app - #{assign_app[:name]}"
143
- pair_with_remote(assign_app)
144
- display "+> Repo is now paired to '#{assign_app[:name]}'"
145
- display
146
- else
147
- error "#{name} is not found among your launched app list"
148
- end
149
- else
150
- errors = []
151
- errors << "Multiple matches found"
152
- errors << ""
153
- matching_apps.each do |match|
154
- errors << "-> #{match[:name]}"
155
- end
156
- errors << ""
157
- errors << "You have more then one app that uses this repo."
158
- errors << "Please specify which app you would like to pair to."
159
- errors << ""
160
- errors << "ex: pagoda pair #{matching_apps[0][:name]}"
161
- error errors
162
- end
163
- elsif matching_apps.count == 1
164
- match = matching_apps.first
165
- display "+> Pairing this repo to deployed app - #{match[:name]}"
166
- pair_with_remote match
167
- display "+> Repo is now paired to '#{match[:name]}'"
168
- display
169
- else
170
- error "Current git repo doesn't match any launched app repos"
171
- end
172
- end
173
-
174
- def unpair
175
- app
176
- display
177
- display "+> Unpairing this repo"
178
- remove_app(app)
179
- display "+> Free at last!"
180
- display
181
- end
182
-
183
- def deploy
184
- app
185
- display
186
- branch = parse_branch
187
- commit = parse_commit
188
- if option_value(nil, "--latest")
189
- client.deploy_latest(app)
190
- display "+> deploying to latest commit point on github...", false
191
- loop_transaction
192
- display "+> deployed"
193
- display
194
- else
195
- client.deploy(app, branch, commit)
196
- display "+> deploying current branch and commit...", false
197
- loop_transaction
198
- display "+> deployed"
199
- display
200
- end
201
- end
202
-
203
- def rewind
204
- app
205
- display
206
- transaction = client.rewind(app)
207
- display "+> undo...", false
208
- loop_transaction
209
- display "+> done"
210
- display
211
- end
212
- alias :rollback :rewind
213
- alias :undo :rewind
214
-
215
- def fast_forward
216
- app
217
- display
218
- transaction = client.fast_forward(app)
219
- display "+> redo...", false
220
- loop_transaction
221
- display "+> done"
222
- display
223
- end
224
- alias :fastforward :fast_forward
225
- alias :forward :fast_forward
226
- alias :redo :fast_forward
227
-
228
- protected
229
-
230
- def pair_with_remote(app)
231
- my_app_list = read_apps
232
- current_root = locate_app_root
233
- in_list = false
234
- my_app_list.each do |app_str|
235
- app_arr = app_str.split(" ")
236
- if app[:git_url] == app_arr[1] && app[:name] == app_arr[0] || app_arr[2] == current_root
237
- in_list = true
238
- end
239
- end
240
- unless in_list
241
- add_app app[:name]
242
- end
243
- end
244
-
245
-
246
- end
247
- end