pbox 1.17.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +7 -0
  2. data/COPYRIGHT +1 -0
  3. data/LICENSE +11 -0
  4. data/README.md +40 -0
  5. data/Rakefile +6 -0
  6. data/autocomplete/pbox_bash +1639 -0
  7. data/bin/pbox +37 -0
  8. data/conf/protonbox.conf +8 -0
  9. data/features/assets/deploy.tar.gz +0 -0
  10. data/features/core_feature.rb +178 -0
  11. data/features/deployments_feature.rb +127 -0
  12. data/features/domains_feature.rb +49 -0
  13. data/features/keys_feature.rb +37 -0
  14. data/features/members_feature.rb +166 -0
  15. data/lib/rhc/auth/basic.rb +64 -0
  16. data/lib/rhc/auth/token.rb +102 -0
  17. data/lib/rhc/auth/token_store.rb +53 -0
  18. data/lib/rhc/auth.rb +5 -0
  19. data/lib/rhc/autocomplete.rb +66 -0
  20. data/lib/rhc/autocomplete_templates/bash.erb +39 -0
  21. data/lib/rhc/cartridge_helpers.rb +118 -0
  22. data/lib/rhc/cli.rb +40 -0
  23. data/lib/rhc/command_runner.rb +186 -0
  24. data/lib/rhc/commands/account.rb +25 -0
  25. data/lib/rhc/commands/alias.rb +124 -0
  26. data/lib/rhc/commands/app.rb +701 -0
  27. data/lib/rhc/commands/apps.rb +20 -0
  28. data/lib/rhc/commands/authorization.rb +96 -0
  29. data/lib/rhc/commands/base.rb +174 -0
  30. data/lib/rhc/commands/cartridge.rb +326 -0
  31. data/lib/rhc/commands/deployment.rb +82 -0
  32. data/lib/rhc/commands/domain.rb +167 -0
  33. data/lib/rhc/commands/env.rb +142 -0
  34. data/lib/rhc/commands/git_clone.rb +29 -0
  35. data/lib/rhc/commands/logout.rb +51 -0
  36. data/lib/rhc/commands/member.rb +148 -0
  37. data/lib/rhc/commands/port_forward.rb +197 -0
  38. data/lib/rhc/commands/server.rb +40 -0
  39. data/lib/rhc/commands/setup.rb +60 -0
  40. data/lib/rhc/commands/snapshot.rb +137 -0
  41. data/lib/rhc/commands/ssh.rb +51 -0
  42. data/lib/rhc/commands/sshkey.rb +97 -0
  43. data/lib/rhc/commands/tail.rb +47 -0
  44. data/lib/rhc/commands/threaddump.rb +14 -0
  45. data/lib/rhc/commands.rb +396 -0
  46. data/lib/rhc/config.rb +320 -0
  47. data/lib/rhc/context_helper.rb +121 -0
  48. data/lib/rhc/core_ext.rb +202 -0
  49. data/lib/rhc/coverage_helper.rb +33 -0
  50. data/lib/rhc/deployment_helpers.rb +88 -0
  51. data/lib/rhc/exceptions.rb +232 -0
  52. data/lib/rhc/git_helpers.rb +91 -0
  53. data/lib/rhc/help_formatter.rb +55 -0
  54. data/lib/rhc/helpers.rb +477 -0
  55. data/lib/rhc/highline_extensions.rb +479 -0
  56. data/lib/rhc/json.rb +51 -0
  57. data/lib/rhc/output_helpers.rb +260 -0
  58. data/lib/rhc/rest/activation.rb +11 -0
  59. data/lib/rhc/rest/alias.rb +42 -0
  60. data/lib/rhc/rest/api.rb +87 -0
  61. data/lib/rhc/rest/application.rb +332 -0
  62. data/lib/rhc/rest/attributes.rb +36 -0
  63. data/lib/rhc/rest/authorization.rb +8 -0
  64. data/lib/rhc/rest/base.rb +79 -0
  65. data/lib/rhc/rest/cartridge.rb +154 -0
  66. data/lib/rhc/rest/client.rb +650 -0
  67. data/lib/rhc/rest/deployment.rb +18 -0
  68. data/lib/rhc/rest/domain.rb +98 -0
  69. data/lib/rhc/rest/environment_variable.rb +15 -0
  70. data/lib/rhc/rest/gear_group.rb +16 -0
  71. data/lib/rhc/rest/httpclient.rb +145 -0
  72. data/lib/rhc/rest/key.rb +44 -0
  73. data/lib/rhc/rest/membership.rb +105 -0
  74. data/lib/rhc/rest/mock.rb +1024 -0
  75. data/lib/rhc/rest/user.rb +32 -0
  76. data/lib/rhc/rest.rb +148 -0
  77. data/lib/rhc/ssh_helpers.rb +378 -0
  78. data/lib/rhc/tar_gz.rb +51 -0
  79. data/lib/rhc/usage_templates/command_help.erb +51 -0
  80. data/lib/rhc/usage_templates/command_syntax_help.erb +11 -0
  81. data/lib/rhc/usage_templates/help.erb +35 -0
  82. data/lib/rhc/usage_templates/missing_help.erb +1 -0
  83. data/lib/rhc/usage_templates/options_help.erb +12 -0
  84. data/lib/rhc/vendor/okjson.rb +600 -0
  85. data/lib/rhc/vendor/parseconfig.rb +178 -0
  86. data/lib/rhc/vendor/sshkey.rb +253 -0
  87. data/lib/rhc/vendor/zliby.rb +628 -0
  88. data/lib/rhc/version.rb +5 -0
  89. data/lib/rhc/wizard.rb +633 -0
  90. data/lib/rhc.rb +34 -0
  91. data/spec/coverage_helper.rb +89 -0
  92. data/spec/direct_execution_helper.rb +338 -0
  93. data/spec/keys/example.pem +23 -0
  94. data/spec/keys/example_private.pem +27 -0
  95. data/spec/keys/server.pem +19 -0
  96. data/spec/rest_spec_helper.rb +31 -0
  97. data/spec/rhc/assets/cert.crt +22 -0
  98. data/spec/rhc/assets/cert_key_rsa +27 -0
  99. data/spec/rhc/assets/empty.txt +0 -0
  100. data/spec/rhc/assets/env_vars.txt +7 -0
  101. data/spec/rhc/assets/env_vars_2.txt +1 -0
  102. data/spec/rhc/assets/foo.txt +1 -0
  103. data/spec/rhc/assets/targz_corrupted.tar.gz +1 -0
  104. data/spec/rhc/assets/targz_sample.tar.gz +0 -0
  105. data/spec/rhc/auth_spec.rb +442 -0
  106. data/spec/rhc/cli_spec.rb +188 -0
  107. data/spec/rhc/command_spec.rb +435 -0
  108. data/spec/rhc/commands/account_spec.rb +42 -0
  109. data/spec/rhc/commands/alias_spec.rb +333 -0
  110. data/spec/rhc/commands/app_spec.rb +754 -0
  111. data/spec/rhc/commands/apps_spec.rb +39 -0
  112. data/spec/rhc/commands/authorization_spec.rb +145 -0
  113. data/spec/rhc/commands/cartridge_spec.rb +641 -0
  114. data/spec/rhc/commands/deployment_spec.rb +286 -0
  115. data/spec/rhc/commands/domain_spec.rb +383 -0
  116. data/spec/rhc/commands/env_spec.rb +493 -0
  117. data/spec/rhc/commands/git_clone_spec.rb +80 -0
  118. data/spec/rhc/commands/logout_spec.rb +86 -0
  119. data/spec/rhc/commands/member_spec.rb +228 -0
  120. data/spec/rhc/commands/port_forward_spec.rb +217 -0
  121. data/spec/rhc/commands/server_spec.rb +69 -0
  122. data/spec/rhc/commands/setup_spec.rb +118 -0
  123. data/spec/rhc/commands/snapshot_spec.rb +179 -0
  124. data/spec/rhc/commands/ssh_spec.rb +163 -0
  125. data/spec/rhc/commands/sshkey_spec.rb +188 -0
  126. data/spec/rhc/commands/tail_spec.rb +81 -0
  127. data/spec/rhc/commands/threaddump_spec.rb +84 -0
  128. data/spec/rhc/config_spec.rb +407 -0
  129. data/spec/rhc/helpers_spec.rb +524 -0
  130. data/spec/rhc/highline_extensions_spec.rb +314 -0
  131. data/spec/rhc/json_spec.rb +30 -0
  132. data/spec/rhc/rest_application_spec.rb +248 -0
  133. data/spec/rhc/rest_client_spec.rb +752 -0
  134. data/spec/rhc/rest_spec.rb +740 -0
  135. data/spec/rhc/targz_spec.rb +55 -0
  136. data/spec/rhc/wizard_spec.rb +756 -0
  137. data/spec/spec_helper.rb +575 -0
  138. data/spec/wizard_spec_helper.rb +330 -0
  139. metadata +435 -0
@@ -0,0 +1,701 @@
1
+ require 'rhc/commands/base'
2
+ require 'resolv'
3
+ require 'rhc/git_helpers'
4
+ require 'rhc/cartridge_helpers'
5
+ require 'rhc/deployment_helpers'
6
+
7
+ module RHC::Commands
8
+ class App < Base
9
+ summary "Commands for creating and managing applications"
10
+ description <<-DESC
11
+ Creates and controls an ProtonBox application. To see the list of all
12
+ applications use the rhc domain show command. Note that delete is not
13
+ reversible and will stop your application and then remove the application
14
+ and repo from the remote server. No local changes are made.
15
+ DESC
16
+ syntax "<action>"
17
+ default_action :help
18
+ suppress_wizard
19
+
20
+ summary "Create an application"
21
+ description <<-DESC
22
+ Create an application. Every ProtonBox application must have one
23
+ web cartridge which serves web requests, and can have a number of
24
+ other cartridges which provide capabilities like databases,
25
+ scheduled jobs, or continuous integration.
26
+
27
+ You can see a list of all valid cartridge types by running
28
+ 'pbox cartridge list'. ProtonBox also supports downloading cartridges -
29
+ pass a URL in place of the cartridge name and we'll download
30
+ and install that cartridge into your app. Keep in mind that
31
+ these cartridges receive no security updates. Note that not
32
+ all ProtonBox servers allow downloaded cartridges.
33
+
34
+ When your application is created, a URL combining the name of
35
+ your app and the name of your domain will be registered in DNS.
36
+ A copy of the code for your application will be checked out locally
37
+ into a folder with the same name as your application. Note that
38
+ different types of applications may require different folder
39
+ structures - check the README provided with the cartridge if
40
+ you have questions.
41
+
42
+ ProtonBox runs the components of your application on small virtual
43
+ servers called "gears". Each account or plan is limited to a number
44
+ of gears which you can use across multiple applications. Some
45
+ accounts or plans provide access to gears with more memory or more
46
+ CPU. Run 'pbox account' to see the number and sizes of gears available
47
+ to you. When creating an application the --gear-size parameter
48
+ may be specified to change the gears used.
49
+
50
+ DESC
51
+ syntax "<name> <cartridge> [... <cartridge>] [... VARIABLE=VALUE] [-n namespace]"
52
+ option ["-n", "--namespace NAME"], "Namespace for the application"
53
+ option ["-g", "--gear-size SIZE"], "Gear size controls how much memory and CPU your cartridges can use."
54
+ option ["-s", "--scaling"], "Enable scaling for the web cartridge."
55
+ option ["-r", "--repo DIR"], "Path to the Git repository (defaults to ./$app_name)"
56
+ option ["-e", "--env VARIABLE=VALUE"], "Environment variable(s) to be set on this app, or path to a file containing environment variables", :type => :list
57
+ option ["--from-code URL"], "URL to a Git repository that will become the initial contents of the application"
58
+ option ["--[no-]git"], "Skip creating the local Git repository."
59
+ option ["--[no-]dns"], "Skip waiting for the application DNS name to resolve. Must be used in combination with --no-git"
60
+ option ['--no-keys'], "Skip checking SSH keys during app creation", :hide => true
61
+ option ["--enable-jenkins [NAME]"], "Enable Jenkins builds for this application (will create a Jenkins application if not already available). The default name will be 'jenkins' if not specified."
62
+ argument :name, "Name for your application", ["-a", "--app NAME"], :optional => true
63
+ argument :cartridges, "The web framework this application should use", ["-t", "--type CARTRIDGE"], :optional => true, :type => :list
64
+ def create(name, cartridges)
65
+ check_config!
66
+
67
+ check_name!(name)
68
+
69
+ arg_envs, cartridges = cartridges.partition{|item| item.match(env_var_regex_pattern)}
70
+
71
+ cartridges = check_cartridges(cartridges, &require_one_web_cart)
72
+
73
+ options.default \
74
+ :dns => true,
75
+ :git => true
76
+
77
+ raise ArgumentError, "You have named both your main application and your Jenkins application '#{name}'. In order to continue you'll need to specify a different name with --enable-jenkins or choose a different application name." if jenkins_app_name == name && enable_jenkins?
78
+
79
+ rest_domain = check_domain!
80
+ rest_app = nil
81
+ repo_dir = nil
82
+
83
+ cart_names = cartridges.collect do |c|
84
+ c.usage_rate? ? "#{c.short_name} (addtl. costs may apply)" : c.short_name
85
+ end.join(', ')
86
+
87
+ env = collect_env_vars(arg_envs.concat(Array(options.env)))
88
+ if env.present? && !rest_domain.supports_add_application_with_env_vars?
89
+ env = []
90
+ warn "Server does not support environment variables."
91
+ end
92
+
93
+ paragraph do
94
+ header "Application Options"
95
+ table([["Domain:", options.namespace],
96
+ ["Cartridges:", cart_names],
97
+ (["Source Code:", options.from_code] if options.from_code),
98
+ ["Gear Size:", options.gear_size || "default"],
99
+ ["Scaling:", options.scaling ? "yes" : "no"],
100
+ (["Environment Variables:", env.map{|item| "#{item.name}=#{item.value}"}.join(', ')] if env.present?),
101
+ ].compact
102
+ ).each { |s| say " #{s}" }
103
+ end
104
+
105
+ paragraph do
106
+ say "Creating application '#{name}' ... "
107
+
108
+ # create the main app
109
+ rest_app = create_app(name, cartridges, rest_domain, options.gear_size, options.scaling, options.from_code, env, options.auto_deploy, options.keep_deployments, options.deployment_branch)
110
+ success "done"
111
+
112
+ paragraph{ indent{ success rest_app.messages.map(&:strip) } }
113
+ end
114
+
115
+ build_app_exists = rest_app.building_app
116
+
117
+ if enable_jenkins?
118
+
119
+ unless build_app_exists
120
+ paragraph do
121
+ say "Setting up a Jenkins application ... "
122
+
123
+ begin
124
+ build_app_exists = add_jenkins_app(rest_domain)
125
+
126
+ success "done"
127
+ paragraph{ indent{ success build_app_exists.messages.map(&:strip) } }
128
+
129
+ rescue Exception => e
130
+ warn "not complete"
131
+ add_issue("Jenkins failed to install - #{e}",
132
+ "Installing jenkins and jenkins-client",
133
+ "pbox create-app jenkins",
134
+ "pbox add-cartridge jenkins-client -a #{rest_app.name}")
135
+ end
136
+ end
137
+ end
138
+
139
+ paragraph do
140
+ messages = []
141
+ add_jenkins_client_to(rest_app, messages)
142
+ paragraph{ indent{ success messages.map(&:strip) } }
143
+ end if build_app_exists
144
+ end
145
+
146
+ debug "Checking SSH keys through the wizard"
147
+ check_sshkeys! unless options.no_keys
148
+
149
+ if options.dns
150
+ paragraph do
151
+ say "Waiting for your DNS name to be available ... "
152
+ if dns_propagated? rest_app.host
153
+ success "done"
154
+ else
155
+ warn "failure"
156
+ add_issue("We were unable to lookup your hostname (#{rest_app.host}) in a reasonable amount of time and can not clone your application.",
157
+ "Clone your git repo",
158
+ "pbox git-clone #{rest_app.name}")
159
+
160
+ output_issues(rest_app)
161
+ return 0
162
+ end
163
+ end
164
+
165
+ if options.git
166
+ section(:now => true, :top => 1, :bottom => 1) do
167
+ begin
168
+ repo_dir = git_clone_application(rest_app)
169
+ rescue RHC::GitException => e
170
+ warn "#{e}"
171
+ unless RHC::Helpers.windows? and windows_nslookup_bug?(rest_app)
172
+ add_issue("We were unable to clone your application's git repo - #{e}",
173
+ "Clone your git repo",
174
+ "pbox git-clone #{rest_app.name}")
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ output_issues(rest_app) if issues?
182
+
183
+ paragraph do
184
+ say "Your application '#{rest_app.name}' is now available."
185
+ paragraph do
186
+ indent do
187
+ say table [
188
+ ['URL:', rest_app.app_url],
189
+ ['SSH to:', rest_app.ssh_string],
190
+ ['Git remote:', rest_app.git_url],
191
+ (['Cloned to:', repo_dir] if repo_dir)
192
+ ].compact
193
+ end
194
+ end
195
+ end
196
+ paragraph{ say "Run 'pbox show-app #{name}' for more details about your app." }
197
+
198
+ 0
199
+ end
200
+
201
+
202
+ summary "Delete an application from the server"
203
+ description "Deletes your application and all of its data from the server.",
204
+ "Use with caution as this operation is permanent."
205
+ syntax "<app> [--namespace NAME]"
206
+ takes_application :argument => true
207
+ option ["--confirm"], "Pass to confirm deleting the application"
208
+ alias_action :destroy, :deprecated => true
209
+ def delete(app)
210
+ rest_app = find_app
211
+
212
+ confirm_action "#{color("This is a non-reversible action! Your application code and data will be permanently deleted if you continue!", :yellow)}\n\nAre you sure you want to delete the application '#{app}'?"
213
+
214
+ say "Deleting application '#{rest_app.name}' ... "
215
+ rest_app.destroy
216
+ success "deleted"
217
+
218
+ paragraph{ rest_app.messages.each{ |s| success s } }
219
+
220
+ 0
221
+ end
222
+
223
+ summary "Start the application"
224
+ syntax "<app> [--namespace NAME]"
225
+ takes_application :argument => true
226
+ def start(app)
227
+ app_action :start
228
+
229
+ results { say "#{app} started" }
230
+ 0
231
+ end
232
+
233
+ summary "Stop the application"
234
+ syntax "<app> [--namespace NAME]"
235
+ takes_application :argument => true
236
+ def stop(app)
237
+ app_action :stop
238
+
239
+ results { say "#{app} stopped" }
240
+ 0
241
+ end
242
+
243
+ summary "Stops all application processes"
244
+ syntax "<app> [--namespace NAME]"
245
+ takes_application :argument => true
246
+ def force_stop(app)
247
+ app_action :stop, true
248
+
249
+ results { say "#{app} force stopped" }
250
+ 0
251
+ end
252
+
253
+ summary "Restart the application"
254
+ syntax "<app> [--namespace NAME]"
255
+ takes_application :argument => true
256
+ def restart(app)
257
+ app_action :restart
258
+
259
+ results { say "#{app} restarted" }
260
+ 0
261
+ end
262
+
263
+ summary "Reload the application's configuration"
264
+ syntax "<app> [--namespace NAME]"
265
+ takes_application :argument => true
266
+ def reload(app)
267
+ app_action :reload
268
+
269
+ results { say "#{app} config reloaded" }
270
+ 0
271
+ end
272
+
273
+ summary "Clean out the application's logs and tmp directories and tidy up the git repo on the server"
274
+ syntax "<app> [--namespace NAME]"
275
+ takes_application :argument => true
276
+ def tidy(app)
277
+ app_action :tidy
278
+
279
+ results { say "#{app} cleaned up" }
280
+ 0
281
+ end
282
+
283
+ summary "Show information about an application"
284
+ description <<-DESC
285
+ Display the properties of an application, including its URL, the SSH
286
+ connection string, and the Git remote URL. Will also display any
287
+ cartridges, their scale, and any values they expose.
288
+
289
+ The '--state' option will retrieve information from each cartridge in
290
+ the application, which may include cartridge specific text.
291
+
292
+ The '--configuration' option will display configuration values set in
293
+ the application. Use 'pbox configure-app' to configure.
294
+
295
+ To see information about the individual gears within an application,
296
+ use '--gears', including whether they are started or stopped and their
297
+ SSH host strings. Passing '--gears quota' will show the free and maximum
298
+ storage on each gear.
299
+
300
+ If you want to run commands against individual gears, use:
301
+
302
+ pbox ssh <app> --gears '<command>'
303
+
304
+ to run and display the output from each gear.
305
+
306
+ DESC
307
+ syntax "<app> [--namespace NAME]"
308
+ takes_application :argument => true
309
+ option ["--state"], "Get the current state of the cartridges in this application"
310
+ option ["--configuration"], "Get the current configuration values set in this application"
311
+ option ["--gears [quota|ssh]"], "Show information about the cartridges on each gear in this application. Pass 'quota' to see per gear disk usage and limits. Pass 'ssh' to print only the SSH connection strings of each gear."
312
+ def show(app_name)
313
+
314
+ if options.state
315
+ find_app(:with_gear_groups => true).each do |gg|
316
+ say "Cartridge #{gg.cartridges.collect { |c| c['name'] }.join(', ')} is #{gear_group_state(gg.gears.map{ |g| g['state'] })}"
317
+ end
318
+
319
+ elsif options.gears && options.gears != true
320
+ groups = find_app(:with_gear_groups => true)
321
+
322
+ case options.gears
323
+ when 'quota'
324
+ opts = {:as => :gear, :split_cells_on => /\s*\t/, :header => ['Gear', 'Cartridges', 'Used', 'Limit'], :align => [nil, nil, :right, :right]}
325
+ table_from_gears('echo "$(du --block-size=1 -s 2>/dev/null | cut -f 1)"', groups, opts) do |gear, data, group|
326
+ [gear['id'], group.cartridges.collect{ |c| c['name'] }.join(' '), (human_size(data.chomp) rescue 'error'), human_size(group.quota)]
327
+ end
328
+ when 'ssh'
329
+ groups.each{ |group| group.gears.each{ |g| say (ssh_string(g['ssh_url']) or raise NoPerGearOperations) } }
330
+ else
331
+ run_on_gears(ssh_command_for_op(options.gears), groups)
332
+ end
333
+
334
+ elsif options.gears
335
+ gear_info = find_app(:with_gear_groups => true).map do |group|
336
+ group.gears.map do |gear|
337
+ [
338
+ gear['id'],
339
+ gear['state'] == 'started' ? gear['state'] : color(gear['state'], :yellow),
340
+ group.cartridges.collect{ |c| c['name'] }.join(' '),
341
+ group.gear_profile,
342
+ ssh_string(gear['ssh_url'])
343
+ ]
344
+ end
345
+ end.flatten(1)
346
+
347
+ say table(gear_info, :header => ['ID', 'State', 'Cartridges', 'Size', 'SSH URL'])
348
+
349
+ elsif options.configuration
350
+ display_app_configurations(find_app)
351
+ paragraph { say "Use 'pbox configure-app' to change the configuration values of this application." }
352
+
353
+ else
354
+ app = find_app(:include => :cartridges)
355
+ display_app(app, app.cartridges)
356
+ end
357
+
358
+ 0
359
+ end
360
+
361
+ summary "Deploy a git reference or binary file of an application"
362
+ syntax "<ref> --app NAME [--namespace NAME]"
363
+ description <<-DESC
364
+ By default ProtonBox applications prepare, distribute, and activate deployments
365
+ on every git push. Alternatively, a user may choose to disable automatic
366
+ deployments and use 'pbox deploy' and 'pbox deployment' commands to fully control the
367
+ deployment lifecycle.
368
+
369
+ Use this command to prepare, distribute and deploy manually from a git reference
370
+ (commit id, tag or branch) or from a binary file. Check also 'pbox configure-app'
371
+ to configure your application to deploy manually and set the number of deployments
372
+ to keep in history.
373
+
374
+ DESC
375
+ takes_application
376
+ argument :ref, "Git tag, branch or commit id or path to binary file to be deployed", ["--ref REF"], :optional => false
377
+ option "--[no-]hot-deploy", "Perform hot deployment according to the specified argument rather than checking for the presence of the hot_deploy marker in the application git repo"
378
+ option "--[no-]force-clean-build", "Perform a clean build according to the specified argument rather than checking for the presence of the force_clean_build marker in the application git repo"
379
+ alias_action :"deploy", :root_command => true
380
+ def deploy(ref)
381
+ rest_app = find_app
382
+
383
+ raise RHC::DeploymentsNotSupportedException.new if !rest_app.supports? "DEPLOY"
384
+
385
+ deploy_artifact(rest_app, ref, options.hot_deploy, options.force_clean_build)
386
+
387
+ 0
388
+ end
389
+
390
+ summary "Configure several properties that apply to an application"
391
+ syntax "<app> [--[no-]auto-deploy] [--keep-deployments INTEGER] [--deployment-branch BRANCH] [--deployment-type TYPE] [--namespace NAME]"
392
+ takes_application :argument => true
393
+ option ["--[no-]auto-deploy"], "Build and deploy automatically when pushing to the git repo. Defaults to true."
394
+ option ["--keep-deployments INTEGER", Integer], "Number of deployments to preserve. Defaults to 1."
395
+ option ["--deployment-branch BRANCH"], "Which branch should trigger an automatic deployment, if automatic deployment is enabled with --auto-deploy. Defaults to master."
396
+ option ["--deployment-type git|binary"], "Type of deployment the application accepts ('git' or 'binary'). Defaults to git."
397
+ def configure(app_name)
398
+ rest_app = find_app
399
+
400
+ app_options = {}
401
+ app_options[:auto_deploy] = options.auto_deploy if !options.auto_deploy.nil?
402
+ app_options[:keep_deployments] = options.keep_deployments if options.keep_deployments
403
+ app_options[:deployment_branch] = options.deployment_branch if options.deployment_branch
404
+ app_options[:deployment_type] = options.deployment_type if options.deployment_type
405
+
406
+ if app_options.present?
407
+ paragraph do
408
+ say "Configuring application '#{app_name}' ... "
409
+ rest_app.configure(app_options)
410
+ success "done"
411
+ end
412
+ end
413
+
414
+ paragraph { display_app(find_app, nil, [:auto_deploy, :keep_deployments, :deployment_type, :deployment_branch]) }
415
+
416
+ paragraph { say "Your application '#{rest_app.name}' is #{app_options.empty? ? '' : 'now '}configured as listed above." }
417
+ paragraph { say "Use 'pbox show-app #{rest_app.name} --configuration' to check your configuration values any time." } if app_options.present?
418
+
419
+ 0
420
+ end
421
+
422
+ private
423
+ include RHC::GitHelpers
424
+ include RHC::CartridgeHelpers
425
+ include RHC::SSHHelpers
426
+ include RHC::DeploymentHelpers
427
+
428
+ MAX_RETRIES = 7
429
+ DEFAULT_DELAY_THROTTLE = 2.0
430
+
431
+ def require_one_web_cart
432
+ lambda{ |carts|
433
+ match, ambiguous = carts.partition{ |c| not c.is_a?(Array) }
434
+ selected_web = match.any?{ |c| not c.only_in_existing? }
435
+ possible_web = ambiguous.flatten.any?{ |c| not c.only_in_existing? }
436
+ if not (selected_web or possible_web)
437
+ section(:bottom => 1){ list_cartridges(standalone_cartridges) }
438
+ raise RHC::CartridgeNotFoundException, "Every application needs a web cartridge to handle incoming web requests. Please provide the short name of one of the carts listed above."
439
+ end
440
+ if selected_web
441
+ carts.map! &other_carts_only
442
+ elsif possible_web && ambiguous.length == 1
443
+ carts.map! &web_carts_only
444
+ end
445
+ }
446
+ end
447
+
448
+ def check_sshkeys!
449
+ return unless interactive?
450
+ RHC::SSHWizard.new(rest_client, config, options).run
451
+ end
452
+
453
+ def check_name!(name)
454
+ return unless name.blank?
455
+
456
+ paragraph{ say "When creating an application, you must provide a name and a cartridge from the list below:" }
457
+ paragraph{ list_cartridges(standalone_cartridges) }
458
+
459
+ raise ArgumentError, "Please specify the name of the application and the web cartridge to install"
460
+ end
461
+
462
+ def check_config!
463
+ return if not interactive? or (!options.clean && config.has_local_config?) or (options.server && (options.pblogin || options.token))
464
+ RHC::EmbeddedWizard.new(config, options).run
465
+ end
466
+
467
+ def check_domain!
468
+ if options.namespace
469
+ rest_client.find_domain(options.namespace)
470
+ else
471
+ if rest_client.domains.empty?
472
+ raise RHC::Rest::DomainNotFoundException, "No domains found. Please create a domain with 'pbox create-domain <namespace>' before creating applications." unless interactive?
473
+ RHC::DomainWizard.new(config, options, rest_client).run
474
+ end
475
+ domain = rest_client.domains.first
476
+ raise RHC::Rest::DomainNotFoundException, "No domains found. Please create a domain with 'pbox create-domain <namespace>' before creating applications." unless domain
477
+ options.namespace = domain.name
478
+ domain
479
+ end
480
+ end
481
+
482
+ def gear_group_state(states)
483
+ return states[0] if states.length == 1 || states.uniq.length == 1
484
+ "#{states.select{ |s| s == 'started' }.count}/#{states.length} started"
485
+ end
486
+
487
+ def app_action(action, *args)
488
+ rest_app = find_app
489
+ result = rest_app.send action, *args
490
+ result
491
+ end
492
+
493
+ def create_app(name, cartridges, rest_domain, gear_size=nil, scale=nil, from_code=nil, environment_variables=nil, auto_deploy=nil, keep_deployments=nil, deployment_branch=nil)
494
+ app_options = {:cartridges => Array(cartridges)}
495
+ app_options[:gear_profile] = gear_size if gear_size
496
+ app_options[:scale] = scale if scale
497
+ app_options[:initial_git_url] = from_code if from_code
498
+ app_options[:debug] = true if @debug
499
+ app_options[:environment_variables] = environment_variables.map{ |item| item.to_hash } if environment_variables.present?
500
+ app_options[:auto_deploy] = auto_deploy if !auto_deploy.nil?
501
+ app_options[:keep_deployments] = keep_deployments if keep_deployments
502
+ app_options[:deployment_branch] = deployment_branch if deployment_branch
503
+ debug "Creating application '#{name}' with these options - #{app_options.inspect}"
504
+ rest_app = rest_domain.add_application(name, app_options)
505
+
506
+ rest_app
507
+ rescue RHC::Rest::Exception => e
508
+ if e.code == 109
509
+ paragraph{ say "Valid cartridge types:" }
510
+ paragraph{ list_cartridges(standalone_cartridges) }
511
+ end
512
+ raise
513
+ end
514
+
515
+ def add_jenkins_app(rest_domain)
516
+ create_app(jenkins_app_name, jenkins_cartridge_name, rest_domain)
517
+ end
518
+
519
+ def add_jenkins_cartridge(rest_app)
520
+ rest_app.add_cartridge(jenkins_client_cartridge_name)
521
+ end
522
+
523
+ def add_jenkins_client_to(rest_app, messages)
524
+ say "Setting up Jenkins build ... "
525
+ successful, attempts, exit_code, exit_message = false, 1, 157, nil
526
+ while (!successful && exit_code == 157 && attempts < MAX_RETRIES)
527
+ begin
528
+ cartridge = add_jenkins_cartridge(rest_app)
529
+ successful = true
530
+
531
+ success "done"
532
+ messages.concat(cartridge.messages)
533
+
534
+ rescue RHC::Rest::ServerErrorException => e
535
+ if (e.code == 157)
536
+ # error downloading Jenkins /jnlpJars/jenkins-cli.jar
537
+ attempts += 1
538
+ debug "Jenkins server could not be contacted, sleep and then retry: attempt #{attempts}\n #{e.message}"
539
+ Kernel.sleep(10)
540
+ end
541
+ exit_code = e.code
542
+ exit_message = e.message
543
+ rescue Exception => e
544
+ # timeout and other exceptions
545
+ exit_code = 1
546
+ exit_message = e.message
547
+ end
548
+ end
549
+ unless successful
550
+ warn "not complete"
551
+ add_issue("Jenkins client failed to install - #{exit_message}",
552
+ "Install the jenkins client",
553
+ "pbox add-cartridge jenkins-client -a #{rest_app.name}")
554
+ end
555
+ end
556
+
557
+ def dns_propagated?(host, sleep_time=2)
558
+ #
559
+ # Confirm that the host exists in DNS
560
+ #
561
+ debug "Start checking for application dns @ '#{host}'"
562
+
563
+ found = false
564
+
565
+ # Allow DNS to propagate
566
+ Kernel.sleep 5
567
+
568
+ # Now start checking for DNS
569
+ host_found = hosts_file_contains?(host) or
570
+ 1.upto(MAX_RETRIES) { |i|
571
+ host_found = host_exists?(host)
572
+ break found if host_found
573
+
574
+ say " retry # #{i} - Waiting for DNS: #{host}"
575
+ Kernel.sleep sleep_time.to_i
576
+ sleep_time *= DEFAULT_DELAY_THROTTLE
577
+ }
578
+
579
+ debug "End checking for application dns @ '#{host} - found=#{found}'"
580
+
581
+ host_found
582
+ end
583
+
584
+ def enable_jenkins?
585
+ # legacy issue, commander 4.0.x will place the option in the hash with nil value (BZ878407)
586
+ options.__hash__.has_key?(:enable_jenkins)
587
+ end
588
+
589
+ def jenkins_app_name
590
+ if options.enable_jenkins.is_a? String
591
+ options.enable_jenkins
592
+ end || "jenkins"
593
+ end
594
+
595
+ def jenkins_cartridge_name
596
+ jenkins_cartridges.last.name
597
+ end
598
+
599
+ def jenkins_client_cartridge_name
600
+ jenkins_client_cartridges.last.name
601
+ end
602
+
603
+ def run_nslookup(host)
604
+ # :nocov:
605
+ `nslookup #{host}`
606
+ $?.exitstatus == 0
607
+ # :nocov:
608
+ end
609
+
610
+ def run_ping(host)
611
+ # :nocov:
612
+ `ping #{host} -n 2`
613
+ $?.exitstatus == 0
614
+ # :nocov:
615
+ end
616
+
617
+ def windows_nslookup_bug?(rest_app)
618
+ windows_nslookup = run_nslookup(rest_app.host)
619
+ windows_ping = run_ping(rest_app.host)
620
+
621
+ if windows_nslookup and !windows_ping # this is related to BZ #826769
622
+ issue = <<WINSOCKISSUE
623
+ We were unable to lookup your hostname (#{rest_app.host})
624
+ in a reasonable amount of time. This can happen periodically and may
625
+ take up to 10 extra minutes to propagate depending on where you are in the
626
+ world. This may also be related to an issue with Winsock on Windows [1][2].
627
+ We recommend you wait a few minutes then clone your git repository manually.
628
+
629
+ [1] http://support.microsoft.com/kb/299357
630
+ [2] http://support.microsoft.com/kb/811259
631
+ WINSOCKISSUE
632
+ add_issue(issue,
633
+ "Clone your git repo",
634
+ "pbox git-clone #{rest_app.name}")
635
+
636
+ return true
637
+ end
638
+
639
+ false
640
+ end
641
+
642
+ def output_issues(rest_app)
643
+ reasons, steps = format_issues(4)
644
+ warn <<WARNING_OUTPUT
645
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
646
+ WARNING: Your application was created successfully but had problems during
647
+ configuration. Below is a list of the issues and steps you can
648
+ take to complete the configuration of your application.
649
+
650
+ Application URL: #{rest_app.app_url}
651
+
652
+ Issues:
653
+ #{reasons}
654
+ Steps to complete your configuration:
655
+ #{steps}
656
+ If you continue to experience problems after completing these steps,
657
+ you can try destroying and recreating the application:
658
+
659
+ $ pbox app delete #{rest_app.name} --confirm
660
+
661
+ Please contact us if you are unable to successfully create your
662
+ application:
663
+
664
+ Support - https://my.protonbox.com
665
+
666
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
667
+
668
+ WARNING_OUTPUT
669
+ end
670
+
671
+ # Issues collector collects a set of recoverable issues and steps to fix them
672
+ # for output at the end of a complex command
673
+ def add_issue(reason, commands_header, *commands)
674
+ @issues ||= []
675
+ issue = {:reason => reason,
676
+ :commands_header => commands_header,
677
+ :commands => commands}
678
+ @issues << issue
679
+ end
680
+
681
+ def format_issues(indent)
682
+ return nil unless issues?
683
+
684
+ indentation = " " * indent
685
+ reasons = ""
686
+ steps = ""
687
+
688
+ @issues.each_with_index do |issue, i|
689
+ reasons << "#{indentation}#{i+1}. #{issue[:reason].strip}\n"
690
+ steps << "#{indentation}#{i+1}. #{issue[:commands_header].strip}\n"
691
+ issue[:commands].each { |cmd| steps << "#{indentation} $ #{cmd}\n" }
692
+ end
693
+
694
+ [reasons, steps]
695
+ end
696
+
697
+ def issues?
698
+ not @issues.nil?
699
+ end
700
+ end
701
+ end