rhc 1.2.7 → 1.3.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/bin/rhc +6 -8
  2. data/bin/rhc-chk +23 -10
  3. data/features/domain.feature +1 -1
  4. data/features/lib/rhc_helper.rb +3 -2
  5. data/features/lib/rhc_helper/api.rb +7 -0
  6. data/features/lib/rhc_helper/app.rb +8 -10
  7. data/features/lib/rhc_helper/domain.rb +2 -1
  8. data/features/lib/rhc_helper/runnable.rb +2 -24
  9. data/features/sshkey.feature +3 -3
  10. data/features/step_definitions/cartridge_steps.rb +6 -6
  11. data/features/step_definitions/client_steps.rb +0 -1
  12. data/features/step_definitions/sshkey_steps.rb +2 -2
  13. data/features/support/before_hooks.rb +0 -1
  14. data/features/support/env.rb +5 -3
  15. data/lib/rhc-common.rb +1 -1
  16. data/lib/rhc.rb +9 -8
  17. data/lib/rhc/auth.rb +3 -0
  18. data/lib/rhc/auth/basic.rb +54 -0
  19. data/lib/rhc/cartridge_helpers.rb +11 -5
  20. data/lib/rhc/cli.rb +4 -2
  21. data/lib/rhc/command_runner.rb +35 -30
  22. data/lib/rhc/commands.rb +127 -18
  23. data/lib/rhc/commands/account.rb +24 -0
  24. data/lib/rhc/commands/alias.rb +1 -1
  25. data/lib/rhc/commands/app.rb +210 -209
  26. data/lib/rhc/commands/apps.rb +22 -0
  27. data/lib/rhc/commands/base.rb +10 -77
  28. data/lib/rhc/commands/cartridge.rb +35 -35
  29. data/lib/rhc/commands/domain.rb +20 -13
  30. data/lib/rhc/commands/git_clone.rb +30 -0
  31. data/lib/rhc/commands/{port-forward.rb → port_forward.rb} +3 -3
  32. data/lib/rhc/commands/server.rb +28 -16
  33. data/lib/rhc/commands/setup.rb +18 -1
  34. data/lib/rhc/commands/snapshot.rb +4 -4
  35. data/lib/rhc/commands/sshkey.rb +4 -18
  36. data/lib/rhc/commands/tail.rb +32 -9
  37. data/lib/rhc/config.rb +168 -99
  38. data/lib/rhc/context_helper.rb +22 -9
  39. data/lib/rhc/core_ext.rb +41 -1
  40. data/lib/rhc/exceptions.rb +21 -5
  41. data/lib/rhc/git_helpers.rb +81 -0
  42. data/lib/rhc/help_formatter.rb +21 -1
  43. data/lib/rhc/helpers.rb +222 -87
  44. data/lib/rhc/output_helpers.rb +94 -110
  45. data/lib/rhc/rest.rb +15 -198
  46. data/lib/rhc/rest/api.rb +88 -0
  47. data/lib/rhc/rest/application.rb +29 -30
  48. data/lib/rhc/rest/attributes.rb +27 -0
  49. data/lib/rhc/rest/base.rb +29 -33
  50. data/lib/rhc/rest/cartridge.rb +42 -20
  51. data/lib/rhc/rest/client.rb +351 -89
  52. data/lib/rhc/rest/domain.rb +7 -13
  53. data/lib/rhc/rest/gear_group.rb +1 -1
  54. data/lib/rhc/rest/key.rb +7 -2
  55. data/lib/rhc/rest/mock.rb +609 -0
  56. data/lib/rhc/rest/user.rb +6 -2
  57. data/lib/rhc/{ssh_key_helpers.rb → ssh_helpers.rb} +58 -28
  58. data/lib/rhc/{targz.rb → tar_gz.rb} +0 -0
  59. data/lib/rhc/usage_templates/command_help.erb +4 -1
  60. data/lib/rhc/usage_templates/help.erb +24 -11
  61. data/lib/rhc/usage_templates/options_help.erb +14 -0
  62. data/lib/rhc/wizard.rb +283 -213
  63. data/spec/keys/example.pem +23 -0
  64. data/spec/keys/example_private.pem +27 -0
  65. data/spec/keys/server.pem +19 -0
  66. data/spec/rest_spec_helper.rb +3 -371
  67. data/spec/rhc/auth_spec.rb +226 -0
  68. data/spec/rhc/cli_spec.rb +41 -14
  69. data/spec/rhc/command_spec.rb +44 -15
  70. data/spec/rhc/commands/account_spec.rb +41 -0
  71. data/spec/rhc/commands/alias_spec.rb +16 -15
  72. data/spec/rhc/commands/app_spec.rb +115 -92
  73. data/spec/rhc/commands/apps_spec.rb +39 -0
  74. data/spec/rhc/commands/cartridge_spec.rb +134 -112
  75. data/spec/rhc/commands/domain_spec.rb +31 -86
  76. data/spec/rhc/commands/git_clone_spec.rb +56 -0
  77. data/spec/rhc/commands/{port-forward_spec.rb → port_forward_spec.rb} +27 -32
  78. data/spec/rhc/commands/server_spec.rb +28 -3
  79. data/spec/rhc/commands/setup_spec.rb +29 -11
  80. data/spec/rhc/commands/snapshot_spec.rb +4 -3
  81. data/spec/rhc/commands/sshkey_spec.rb +24 -56
  82. data/spec/rhc/commands/tail_spec.rb +26 -9
  83. data/spec/rhc/commands/threaddump_spec.rb +12 -11
  84. data/spec/rhc/config_spec.rb +211 -164
  85. data/spec/rhc/context_spec.rb +2 -0
  86. data/spec/rhc/helpers_spec.rb +242 -46
  87. data/spec/rhc/rest_application_spec.rb +42 -28
  88. data/spec/rhc/rest_client_spec.rb +110 -93
  89. data/spec/rhc/rest_spec.rb +220 -131
  90. data/spec/rhc/targz_spec.rb +1 -1
  91. data/spec/rhc/wizard_spec.rb +435 -624
  92. data/spec/spec.opts +1 -1
  93. data/spec/spec_helper.rb +140 -6
  94. data/spec/wizard_spec_helper.rb +326 -0
  95. metadata +163 -143
  96. data/lib/rhc/client.rb +0 -17
  97. data/lib/rhc/git_helper.rb +0 -59
@@ -6,12 +6,18 @@ module RHC
6
6
 
7
7
  if carts.length == 0
8
8
  valid_carts = rest_obj.cartridges.collect { |c| c.name if c.type == type }.compact
9
- if valid_carts.length > 0
10
- msg = "Valid cartridges are (#{valid_carts.join(', ')})."
11
- else
12
- msg = "No cartridges have been added to this app."
9
+
10
+ msg = if RHC::Rest::Application === rest_obj
11
+ "Cartridge '#{cartridge_name}' cannot be found in application '#{rest_obj.name}'."
12
+ else
13
+ "Cartridge '#{cartridge_name}' is not a valid cartridge name."
14
+ end
15
+
16
+ unless valid_carts.empty?
17
+ msg += " Valid cartridges are (#{valid_carts.join(', ')})."
13
18
  end
14
- raise RHC::CartridgeNotFoundException, "Invalid cartridge specified: '#{cartridge_name}'. #{msg}"
19
+
20
+ raise RHC::CartridgeNotFoundException, msg
15
21
  elsif carts.length > 1
16
22
  msg = "Multiple cartridge versions match your criteria. Please specify one."
17
23
  carts.each { |cart| msg += "\n #{cart.name}" }
@@ -21,10 +21,11 @@ module RHC
21
21
 
22
22
  def self.set_terminal
23
23
  $terminal.wrap_at = HighLine::SystemExtensions.terminal_size.first rescue 80 if $stdin.tty?
24
+ $terminal.page_at = :auto if $stdin.tty? and $stdout.tty?
24
25
  # FIXME: ANSI terminals are not default on windows but we may just be
25
26
  # hitting a bug in highline if windows does support another method.
26
27
  # This is a safe fix for now but needs more research.
27
- HighLine::use_color = false if RHC::Helpers.windows?
28
+ HighLine::use_color = false if RHC::Helpers.windows? or not $stdout.tty?
28
29
  end
29
30
 
30
31
  def self.start(args)
@@ -35,9 +36,10 @@ module RHC
35
36
  program :description, 'Command line interface for OpenShift.'
36
37
  program :version, RHC::VERSION::STRING
37
38
  program :help_formatter, RHC::HelpFormatter
39
+ program :int_message, " Interrupted\n"
38
40
 
39
41
  RHC::Commands.load.to_commander
40
- exit(run! || 0)
42
+ run! || 0
41
43
  end
42
44
  end
43
45
  end
@@ -6,8 +6,16 @@ module RHC
6
6
  commands.keys.find_all { |name| name if /^#{name}\b/.match arg_string }
7
7
  end
8
8
 
9
+ if Commander::VERSION == '4.0.3'
10
+ #:nocov:
11
+ def program(*args)
12
+ Array(super).first
13
+ end
14
+ #:nocov:
15
+ end
16
+
9
17
  def options_parse_trace
10
- if @args.include? "--trace"
18
+ if @args.include?("--trace")
11
19
  @args.delete "--trace"
12
20
  return true
13
21
  end
@@ -15,7 +23,7 @@ module RHC
15
23
  end
16
24
 
17
25
  def options_parse_version
18
- if @args.include? "--version" or @args.include? "-v"
26
+ if @args.include? "--version"
19
27
  say version
20
28
  exit 0
21
29
  end
@@ -25,16 +33,15 @@ module RHC
25
33
  def run!
26
34
  trace = false
27
35
  require_program :version, :description
36
+ #trap('INT') { abort program(:int_message) } if program(:int_message)
37
+ #trap('INT') { program(:int_block).call } if program(:int_block)
28
38
 
29
- trap('INT') { abort program(:int_message) } if program(:int_message)
30
- trap('INT') { program(:int_block).call } if program(:int_block)
31
-
32
- global_option('-h', '--help', 'Display help documentation') do
39
+ global_option('-h', '--help', 'Help on any command', :hide => true) do
33
40
  args = @args - %w[-h --help]
34
41
  command(:help).run(*args)
35
42
  return
36
43
  end
37
- global_option('-v', '--version', 'Display version information') { say version; return }
44
+ global_option('--version', 'Display version information', :hide => true) { say version; return }
38
45
 
39
46
  # remove these because we monkey patch Commands to process all options
40
47
  # at once, avoiding conflicts between the global and command options
@@ -60,15 +67,24 @@ module RHC
60
67
  say "See '#{program :name} help' for a list of valid commands."
61
68
  end
62
69
  1
70
+ rescue \
71
+ OptionParser::InvalidOption => e
72
+ RHC::Helpers.error e.message
73
+ 1
63
74
  rescue \
64
75
  ArgumentError,
65
- OptionParser::InvalidOption,
66
- OptionParser::InvalidArgument,
67
- OptionParser::MissingArgument => e
76
+ OptionParser::ParseError => e
68
77
 
69
78
  help_bindings = CommandHelpBindings.new(active_command, commands, self)
70
79
  usage = RHC::HelpFormatter.new(self).render_command_syntax(help_bindings)
71
- RHC::Helpers.error e.message
80
+ message = case e
81
+ when OptionParser::AmbiguousOption
82
+ "The option #{e.args.join(' ')} is ambiguous. You will need to specify the entire option."
83
+ else
84
+ e.message
85
+ end
86
+
87
+ RHC::Helpers.error message
72
88
  say "#{usage}"
73
89
  1
74
90
  rescue RHC::Exception, RHC::Rest::Exception => e
@@ -92,8 +108,15 @@ module RHC
92
108
  end
93
109
 
94
110
  def create_default_commands
111
+ command 'help options' do |c|
112
+ # c.syntax = ''
113
+ c.description = "Display all global options and information about configuration"
114
+ c.when_called do |args, options|
115
+ say help_formatter.render_options self
116
+ end
117
+ end
95
118
  command :help do |c|
96
- c.syntax = 'rhc help <command>'
119
+ c.syntax = '<command>'
97
120
  c.description = 'Display global or <command> help documentation.'
98
121
  c.when_called do |args, options|
99
122
  cmd = (1..args.length).reverse_each.map{ |n| args[0,n].join(' ') }.find{ |cmd| command_exists?(cmd) }
@@ -113,22 +136,4 @@ module RHC
113
136
  end
114
137
  end
115
138
  end
116
-
117
- class CommandHelpBindings
118
- def initialize(command, instance_commands, runner)
119
- @command = command
120
- @actions = instance_commands.collect do |command_name, command_class|
121
- next if command_class.summary.nil?
122
- m = /^#{command.name} ([^ ]+)/.match(command_name)
123
- # if we have a match and it is not an alias then we can use it
124
- m and command_name == command_class.name ? {:name => m[1], :summary => command_class.summary || ""} : nil
125
- end
126
- @actions.compact!
127
- @global_options = runner.options
128
- @runner = runner
129
- end
130
- def program(*args)
131
- @runner.program *args
132
- end
133
- end
134
139
  end
@@ -1,5 +1,5 @@
1
1
  require 'commander'
2
- require 'rhc/helpers'
2
+ require 'commander/command'
3
3
 
4
4
  ## monkey patch option parsing to also parse global options all at once
5
5
  # to avoid conflicts and side effects of similar short switches
@@ -11,14 +11,12 @@ module Commander
11
11
  end
12
12
 
13
13
  def parse_options_and_call_procs *args
14
- return args if args.empty?
15
- opts = OptionParser.new
16
14
  runner = Commander::Runner.instance
15
+ opts = OptionParser.new
16
+
17
17
  # add global options
18
18
  runner.options.each do |option|
19
- opts.on *option[:args],
20
- &runner.global_option_proc(option[:switches], &option[:proc])
21
-
19
+ opts.on(*option[:args], &runner.global_option_proc(option[:switches], &option[:proc]))
22
20
  end
23
21
 
24
22
  # add command options
@@ -27,13 +25,76 @@ module Commander
27
25
  opts
28
26
  end
29
27
 
30
- opts.parse! args
28
+ remaining = opts.parse! args
29
+
30
+ _, config_path = proxy_options.find{ |arg| arg[0] == :config }
31
+ clean, _ = proxy_options.find{ |arg| arg[0] == :clean }
32
+
33
+ begin
34
+ @config = RHC::Config.new
35
+ @config.use_config(config_path) if config_path
36
+
37
+ unless clean
38
+ @config.to_options.each_pair do |key, value|
39
+ next if proxy_options.detect{ |arr| arr[0] == key }
40
+ if sw = opts.send(:search, :long, key.to_s.gsub(/_/, '-'))
41
+ _, cb, val = sw.send(:conv_arg, nil, value) {|*exc| raise(*exc) }
42
+ cb.call(val) if cb
43
+ end
44
+ end
45
+ end
46
+ rescue ArgumentError => e
47
+ n = OptionParser::InvalidOption.new(e.message)
48
+ n.reason = "The configuration file #{@config.path} contains an invalid setting"
49
+ n.set_backtrace(e.backtrace)
50
+ raise n
51
+ rescue OptionParser::ParseError => e
52
+ e.reason = "The configuration file #{@config.path} contains an invalid setting"
53
+ raise
54
+ end
55
+ remaining
56
+ end
57
+ end
58
+ end
59
+
60
+ #
61
+ # Allow Command::Options to lazily evaluate procs and lambdas
62
+ #
63
+ module Commander
64
+ class Command
65
+ class Options
66
+ def initialize(init=nil)
67
+ @table = {}
68
+ default(init) if init
69
+ end
70
+ def method_missing meth, *args, &block
71
+ meth.to_s =~ /=$/ ? self[meth.to_s.chop] = args.first : self[meth]
72
+ end
73
+ def []=(meth, value)
74
+ @table[meth.to_sym] = value
75
+ end
76
+ def [](meth)
77
+ value = @table[meth.to_sym]
78
+ value = value.call if value.is_a? Proc
79
+ value
80
+ end
81
+ def default defaults = {}
82
+ @table = @table.reverse_merge!(__to_hash__(defaults))
83
+ end
84
+ def __replace__(options)
85
+ @table = __to_hash__(options).dup
86
+ end
87
+ def __to_hash__(obj)
88
+ Options === obj ? obj.__hash__ : obj
89
+ end
31
90
  end
32
91
  end
33
92
  end
34
93
 
35
94
  module RHC
36
95
  module Commands
96
+ autoload :Base, 'rhc/commands/base'
97
+
37
98
  def self.load
38
99
  Dir[File.join(File.dirname(__FILE__), "commands", "*.rb")].each do |file|
39
100
  require file
@@ -47,13 +108,6 @@ module RHC
47
108
  global_options << [args, block]
48
109
  end
49
110
 
50
- def self.global_config_setup(options)
51
- RHC::Config.set_opts_config(options.config) if options.config
52
- RHC::Config.password = options.password if options.password
53
- RHC::Config.opts_login = options.rhlogin if options.rhlogin
54
- RHC::Config
55
- end
56
-
57
111
  def self.deprecated?
58
112
  command_name = Commander::Runner.instance.command_name_from_args
59
113
  command = Commander::Runner.instance.active_command
@@ -125,17 +179,17 @@ module RHC
125
179
  end
126
180
 
127
181
  c.when_called do |args, options|
128
- config = global_config_setup(options)
129
182
  deprecated?
130
183
 
184
+ config = c.instance_variable_get(:@config)
185
+
131
186
  cmd = opts[:class].new
132
187
  cmd.options = options
133
188
  cmd.config = config
134
189
 
135
- filled_args = cmd.validate_args_and_options(args_metadata, options_metadata, args)
136
-
190
+ args = fill_arguments(cmd, options, args_metadata, options_metadata, args)
137
191
  needs_configuration!(cmd, options, config)
138
- cmd.send(opts[:method], *filled_args)
192
+ execute(cmd, opts[:method], args)
139
193
  end
140
194
  end
141
195
  end
@@ -143,6 +197,61 @@ module RHC
143
197
  end
144
198
 
145
199
  protected
200
+ def self.execute(cmd, method, args)
201
+ cmd.send(method, *args)
202
+ end
203
+
204
+ def self.fill_arguments(cmd, options, args_metadata, options_metadata, args)
205
+ # process options
206
+ options_metadata.each do |option_meta|
207
+ arg = option_meta[:arg]
208
+
209
+ # Check to see if we've provided a value for an option tagged as deprecated
210
+ if (!(val = options.__hash__[arg]).nil? && dep_info = option_meta[:deprecated])
211
+ # Get the arg for the correct option and what the value should be
212
+ (correct_arg, default) = dep_info.values_at(:key, :value)
213
+ # Set the default value for the correct option to the passed value
214
+ ## Note: If this isn't triggered, then the original default will be honored
215
+ ## If the user specifies any value for the correct option, it will be used
216
+ options.default correct_arg => default
217
+ # Alert the users if they're using a deprecated option
218
+ (correct, incorrect) = [options_metadata.find{|x| x[:arg] == correct_arg },option_meta].flatten.map{|x| x[:switches].join(", ") }
219
+ RHC::Helpers.deprecated_option(incorrect, correct)
220
+ end
221
+
222
+ if context_helper = option_meta[:context_helper]
223
+ options[arg] = lambda{ cmd.send(context_helper) } if options.__hash__[arg].nil?
224
+ end
225
+ raise ArgumentError.new("Missing required option '#{arg}'.") if option_meta[:required] && options[arg].nil?
226
+ end
227
+ # process args
228
+ arg_slots = [].fill(nil, 0, args_metadata.length)
229
+ fill_args = args.reverse
230
+ args_metadata.each_with_index do |arg_meta, i|
231
+ # check options
232
+ option = arg_meta[:option_symbol]
233
+ context_helper = arg_meta[:context_helper]
234
+
235
+ value = options.__hash__[option] if option
236
+ value = fill_args.pop if value.nil?
237
+ value = cmd.send(context_helper) if value.nil? and context_helper
238
+
239
+ if arg_meta[:arg_type] == :list
240
+ fill_args.push(value) unless value.nil?
241
+ value = fill_args.reverse
242
+ fill_args = []
243
+ elsif value.nil?
244
+ raise ArgumentError.new("Missing required argument '#{arg_meta[:name]}'.") if fill_args.empty?
245
+ end
246
+ arg_slots[i] = value
247
+ options.__hash__[option] = value if option
248
+ end
249
+
250
+ raise ArgumentError.new("Too many arguments passed in: #{fill_args.reverse.join(" ")}") unless fill_args.empty?
251
+
252
+ arg_slots
253
+ end
254
+
146
255
  def self.commands
147
256
  @commands ||= {}
148
257
  end
@@ -0,0 +1,24 @@
1
+ module RHC::Commands
2
+ class Account < Base
3
+ suppress_wizard
4
+
5
+ summary "Display details about your OpenShift account"
6
+ description <<-DESC
7
+ Shows who you are logged in to the server as and the capabilities
8
+ available to you on this server.
9
+
10
+ To access more details about your account please visit the website.
11
+ DESC
12
+ def run
13
+ user = rest_client.user
14
+
15
+ say_table nil, get_properties(user, :login, :plan_id, :consumed_gears, :max_gears) + get_properties(user.capabilities, :gear_sizes), :delete => true
16
+
17
+ if openshift_online_server?
18
+ else
19
+ end
20
+
21
+ 0
22
+ end
23
+ end
24
+ end
@@ -1,6 +1,6 @@
1
1
  require 'rhc/commands/base'
2
2
  require 'rhc/config'
3
- require 'rhc-common'
3
+
4
4
  module RHC::Commands
5
5
  class Alias < Base
6
6
  summary "Add or remove a custom domain name for the application"
@@ -1,6 +1,6 @@
1
1
  require 'rhc/commands/base'
2
2
  require 'resolv'
3
- require 'rhc/git_helper'
3
+ require 'rhc/git_helpers'
4
4
  require 'rhc/cartridge_helpers'
5
5
 
6
6
  module RHC::Commands
@@ -10,13 +10,39 @@ module RHC::Commands
10
10
  syntax "<action>"
11
11
  default_action :help
12
12
 
13
- summary "Create an application and adds it to a domain"
14
- description "Create an application in your domain. You can see a list of all valid cartridge types by running 'rhc cartridge list'."
13
+ summary "Create an application"
14
+ description <<-DESC
15
+ Create an application. Every OpenShift application must have one
16
+ web cartridge which serves web requests, and can have a number of
17
+ other cartridges which provide capabilities like databases,
18
+ scheduled jobs, or continuous integration.
19
+
20
+ You can see a list of all valid cartridge types by running
21
+ 'rhc cartridge list'.
22
+
23
+ When your application is created, a domain name that is a combination
24
+ of the name of your app and the namespace of your domain will be
25
+ registered in DNS. A copy of the code for your application
26
+ will be checked out locally into a folder with the same name as
27
+ your application. Note that different types of applications may
28
+ require different structures - check the README provided with the
29
+ cartridge if you have questions.
30
+
31
+ OpenShift runs the components of your application on small virtual
32
+ servers called "gears". Each account or plan is limited to a number
33
+ of gears which you can use across multiple applications. Some
34
+ accounts or plans provide access to gears with more memory or more
35
+ CPU. Run 'rhc account' to see the number and sizes of gears available
36
+ to you. When creating an application the --gear-size parameter
37
+ may be specified to change the gears used.
38
+
39
+ DESC
15
40
  syntax "<name> <cartridge> [-n namespace]"
16
- option ["-n", "--namespace namespace"], "Namespace for the application", :context => :namespace_context, :required => true
41
+ option ["-n", "--namespace namespace"], "Namespace for the application", :context => :namespace_context
17
42
  option ["-g", "--gear-size size"], "Gear size controls how much memory and CPU your cartridges can use."
18
43
  option ["-s", "--scaling"], "Enable scaling for the web cartridge."
19
44
  option ["-r", "--repo dir"], "Path to the Git repository (defaults to ./$app_name)"
45
+ option ["--from-code URL"], "URL to a Git repository that will become the initial contents of the application"
20
46
  option ["--[no-]git"], "Skip creating the local Git repository."
21
47
  option ["--nogit"], "DEPRECATED: Skip creating the local Git repository.", :deprecated => {:key => :git, :value => false}
22
48
  option ["--[no-]dns"], "Skip waiting for the application DNS name to resolve. Must be used in combination with --no-git"
@@ -25,151 +51,142 @@ module RHC::Commands
25
51
  argument :cartridges, "The web framework this application should use", ["-t", "--type cartridge"], :arg_type => :list
26
52
  #argument :additional_cartridges, "A list of other cartridges such as databases you wish to add. Cartridges can also be added later using 'rhc cartridge add'", [], :arg_type => :list
27
53
  def create(name, cartridges)
28
- cartridge = check_cartridges(cartridges).first
54
+ cartridges = check_cartridges(cartridges)
29
55
 
30
56
  options.default \
31
57
  :dns => true,
32
58
  :git => true
33
59
 
34
- header "Creating application '#{name}'"
60
+ 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?
61
+
62
+ raise RHC::DomainNotFoundException.new("No domains found. Please create a domain with 'rhc domain create <namespace>' before creating applications.") if rest_client.domains.empty?
63
+ rest_domain = rest_client.find_domain(options.namespace)
64
+ rest_app = nil
65
+
35
66
  paragraph do
36
- table({"Namespace:" => options.namespace,
37
- "Cartridge:" => cartridge,
38
- "Gear Size:" => options.gear_size || "default",
39
- "Scaling:" => options.scaling ? "yes" : "no",
40
- }
67
+ header "Application Options"
68
+ table([["Namespace:", options.namespace],
69
+ ["Cartridges:", cartridges.map(&:name).join(', ')],
70
+ (["Source Code:", options.from_code] if options.from_code),
71
+ ["Gear Size:", options.gear_size || "default"],
72
+ ["Scaling:", options.scaling ? "yes" : "no"],
73
+ ].compact
41
74
  ).each { |s| say " #{s}" }
42
75
  end
43
76
 
44
- 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?
77
+ messages = []
45
78
 
46
- rest_app, rest_domain = nil
47
- raise RHC::DomainNotFoundException.new("No domains found. Please create a domain with 'rhc domain create <namespace>' before creating applications.") if rest_client.domains.empty?
79
+ paragraph do
80
+ say "Creating application '#{name}' ... "
48
81
 
49
- rest_domain = rest_client.find_domain(options.namespace)
50
82
 
51
- # check to make sure the right options are set for enabling jenkins
52
- jenkins_rest_app = check_jenkins(name, rest_domain) if enable_jenkins?
53
-
54
- # create the main app
55
- rest_app = create_app(name, cartridge, rest_domain,
56
- options.gear_size, options.scaling)
57
-
58
- # create a jenkins app if not available
59
- # don't error out if there are issues, setup warnings instead
60
- begin
61
- jenkins_rest_app = setup_jenkins_app(rest_domain) if enable_jenkins? and jenkins_rest_app.nil?
62
- rescue Exception => e
63
- add_issue("Jenkins failed to install - #{e}",
64
- "Installing jenkins and jenkins-client",
65
- "rhc app create jenkins",
66
- "rhc cartridge add jenkins-client -a #{rest_app.name}")
83
+ # create the main app
84
+ rest_app = create_app(name, cartridges.map(&:name), rest_domain,
85
+ options.gear_size, options.scaling, options.from_code)
86
+
87
+ messages.concat(rest_app.messages)
88
+
89
+ success "done"
67
90
  end
68
91
 
69
- if jenkins_rest_app
70
- success, attempts, exit_code, exit_message = false, 1, 157, nil
71
- while (!success && exit_code == 157 && attempts < MAX_RETRIES)
72
- begin
73
- setup_jenkins_client(rest_app)
74
- success = true
75
- rescue RHC::Rest::ServerErrorException => e
76
- if (e.code == 157)
77
- # error downloading Jenkins /jnlpJars/jenkins-cli.jar
78
- attempts += 1
79
- debug "Jenkins server could not be contacted, sleep and then retry: attempt #{attempts}\n #{e.message}"
80
- Kernel.sleep(10)
92
+ build_app_exists = rest_app.building_app
93
+
94
+ if enable_jenkins?
95
+ unless build_app_exists
96
+ paragraph do
97
+ say "Setting up a Jenkins application ... "
98
+
99
+ begin
100
+ build_app_exists = add_jenkins_app(rest_domain)
101
+
102
+ success "done"
103
+ messages.concat(build_app_exists.messages)
104
+
105
+ rescue Exception => e
106
+ warn "not complete"
107
+ add_issue("Jenkins failed to install - #{e}",
108
+ "Installing jenkins and jenkins-client",
109
+ "rhc app create jenkins",
110
+ "rhc cartridge add jenkins-client -a #{rest_app.name}")
81
111
  end
82
- exit_code = e.code
83
- exit_message = e.message
84
- rescue Exception => e
85
- # timeout and other exceptions
86
- exit_code = 1
87
- exit_message = e.message
88
112
  end
89
113
  end
90
- add_issue("Jenkins client failed to install - #{exit_message}",
91
- "Install the jenkins client",
92
- "rhc cartridge add jenkins-client -a #{rest_app.name}") if !success
114
+
115
+ paragraph do
116
+ add_jenkins_client_to(rest_app, messages)
117
+ end if build_app_exists
93
118
  end
94
119
 
95
120
  if options.dns
96
- say "Your application's domain name is being propagated worldwide (this might take a minute)..."
97
- unless dns_propagated? rest_app.host
98
- add_issue("We were unable to lookup your hostname (#{rest_app.host}) in a reasonable amount of time and can not clone your application.",
99
- "Clone your git repo",
100
- "rhc app git-clone #{rest_app.name}")
101
-
102
- output_issues(rest_app)
103
- return 0
121
+ paragraph do
122
+ say "Waiting for your DNS name to be available ... "
123
+ if dns_propagated? rest_app.host
124
+ success "done"
125
+ else
126
+ warn "failure"
127
+ add_issue("We were unable to lookup your hostname (#{rest_app.host}) in a reasonable amount of time and can not clone your application.",
128
+ "Clone your git repo",
129
+ "rhc git-clone #{rest_app.name}")
130
+
131
+ output_issues(rest_app)
132
+ return 0
133
+ end
104
134
  end
105
135
 
106
136
  if options.git
107
- begin
108
- run_git_clone(rest_app)
109
- rescue RHC::GitException => e
110
- warn "#{e}"
111
- unless RHC::Helpers.windows? and windows_nslookup_bug?(rest_app)
112
- add_issue("We were unable to clone your application's git repo - #{e}",
113
- "Clone your git repo",
114
- "rhc app git-clone #{rest_app.name}")
137
+ paragraph do
138
+ debug "Checking SSH keys through the wizard"
139
+ check_sshkeys! unless options.noprompt
140
+
141
+ say "Downloading the application Git repository ..."
142
+ paragraph do
143
+ begin
144
+ git_clone_application(rest_app)
145
+ rescue RHC::GitException => e
146
+ warn "#{e}"
147
+ unless RHC::Helpers.windows? and windows_nslookup_bug?(rest_app)
148
+ add_issue("We were unable to clone your application's git repo - #{e}",
149
+ "Clone your git repo",
150
+ "rhc git-clone #{rest_app.name}")
151
+ end
152
+ end
115
153
  end
116
154
  end
117
155
  end
118
156
  end
119
157
 
120
- display_app(rest_app, rest_app.cartridges, rest_app.scalable_carts.first)
158
+ display_app(rest_app, rest_app.cartridges)
121
159
 
122
160
  if issues?
123
161
  output_issues(rest_app)
124
162
  else
125
- results {
126
- rest_app.messages.each { |msg| say msg }
127
- jenkins_rest_app.messages.each { |msg| say msg } if enable_jenkins? and jenkins_rest_app
128
- }
163
+ results{ messages.each { |msg| success msg } }.blank? and "Application created"
129
164
  end
130
165
 
131
166
  0
132
167
  end
133
168
 
134
- summary "Clone and configure an application's repository locally"
135
- description "This is a convenience wrapper for 'git clone' with the added",
136
- "benefit of adding configuration data such as the application's",
137
- "UUID to the local repository. It also automatically",
138
- "figures out the git url from the application name so you don't",
139
- "have to look it up."
140
- syntax "<app> [--namespace namespace]"
141
- option ["-n", "--namespace namespace"], "Namespace to add your application to", :context => :namespace_context, :required => true
142
- argument :app, "The application you wish to clone", ["-a", "--app name"]
143
- # TODO: Implement default values for arguments once ffranz has added context arguments
144
- # argument :directory, "The name of a new directory to clone into", [], :default => nil
145
- def git_clone(app)
146
- rest_domain = rest_client.find_domain(options.namespace)
147
- rest_app = rest_domain.find_application(app)
148
- run_git_clone(rest_app)
149
- 0
150
- end
151
169
 
152
170
  summary "Delete an application from the server"
153
171
  description "Deletes your application and all of its data from the server.",
154
172
  "Use with caution as this operation is permanent."
155
173
  syntax "<app> [--namespace namespace]"
156
- option ["-n", "--namespace namespace"], "Namespace to add your application to", :context => :namespace_context, :required => true
174
+ option ["-n", "--namespace namespace"], "Namespace your application belongs to", :context => :namespace_context, :required => true
157
175
  option ["-b", "--bypass"], "DEPRECATED Please use '--confirm'", :deprecated => {:key => :confirm, :value => true}
158
- option ["--confirm"], "Deletes the application without prompting the user"
159
- argument :app, "The application you wish to delete", ["-a", "--app name"]
176
+ option ["--confirm"], "Pass to confirm deleting the application"
177
+ argument :app, "The application you wish to delete", ["-a", "--app name"], :context => :app_context
160
178
  alias_action :destroy, :deprecated => true
161
179
  def delete(app)
180
+
162
181
  rest_domain = rest_client.find_domain(options.namespace)
163
182
  rest_app = rest_domain.find_application(app)
164
- do_delete = true
165
183
 
166
- do_delete = agree "Are you sure you wish to delete the '#{rest_app.name}' application? (yes/no)" unless options.confirm
184
+ 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}'?"
185
+
186
+ say "Deleting application '#{rest_app.name}' ... "
187
+ rest_app.destroy
188
+ success "deleted"
167
189
 
168
- if do_delete
169
- paragraph { say "Deleting application '#{rest_app.name}'" }
170
- rest_app.destroy
171
- results { say "Application '#{rest_app.name}' successfully deleted" }
172
- end
173
190
  0
174
191
  end
175
192
 
@@ -231,7 +248,7 @@ module RHC::Commands
231
248
  summary "Clean out the application's logs and tmp directories and tidy up the git repo on the server"
232
249
  syntax "<app> [--namespace namespace] [--app app]"
233
250
  argument :app, "The name of the application you are tidying", ["-a", "--app app"], :context => :app_context
234
- option ["-n", "--namespace namespace"], "Namespace of the application the cartridge belongs to", :context => :namespace_context, :required => true
251
+ option ["-n", "--namespace namespace"], "Namespace of the application belongs to", :context => :namespace_context, :required => true
235
252
  def tidy(app)
236
253
  app_action app, :tidy
237
254
 
@@ -240,29 +257,31 @@ module RHC::Commands
240
257
  end
241
258
 
242
259
  summary "Show information about an application"
243
- syntax "<app> [--namespace namespace] [--app app]"
260
+ syntax "<app> [--namespace namespace]"
244
261
  argument :app, "The name of the application you are getting information on", ["-a", "--app app"], :context => :app_context
245
262
  option ["-n", "--namespace namespace"], "Namespace of the application the cartridge belongs to", :context => :namespace_context, :required => true
246
263
  option ["--state"], "Get the current state of the application's gears"
247
- def show(app)
248
- rest_domain = rest_client.find_domain(options.namespace)
249
- rest_app = rest_domain.find_application(app)
250
- unless options.state
251
- display_app(rest_app,rest_app.cartridges,rest_app.scalable_carts.first)
252
- else
264
+ def show(app_name)
265
+ domain = rest_client.find_domain(options.namespace)
266
+ app = domain.find_application(app_name)
267
+
268
+ if options.state
253
269
  results do
254
- rest_app.gear_groups.each do |gg|
255
- say "Geargroup #{gg.cartridges.collect { |c| c['name'] }.join('+')} is #{gg.gears.first['state']}"
270
+ app.gear_groups.each do |gg|
271
+ say "Gear group #{gg.cartridges.collect { |c| c['name'] }.join('+')} is #{gg.gears.first['state']}"
256
272
  end
257
273
  end
274
+ else
275
+ display_app(app, app.cartridges)
258
276
  end
277
+
259
278
  0
260
279
  end
261
280
 
262
- summary "Show status of an application's gears"
281
+ summary "DEPRECATED use 'show <app> --state' instead"
263
282
  syntax "<app> [--namespace namespace] [--app app]"
264
283
  argument :app, "The name of the application you are getting information on", ["-a", "--app app"], :context => :app_context
265
- option ["-n", "--namespace namespace"], "Namespace of the application the cartridge belongs to", :context => :namespace_context, :required => true
284
+ option ["-n", "--namespace namespace"], "Namespace of the application belongs to", :context => :namespace_context, :required => true
266
285
  deprecated "rhc app show --state"
267
286
  def status(app)
268
287
  # TODO: add a way to deprecate this and alias to show --apache
@@ -274,33 +293,46 @@ module RHC::Commands
274
293
  include RHC::GitHelpers
275
294
  include RHC::CartridgeHelpers
276
295
 
296
+ def check_sshkeys!
297
+ RHC::SSHWizard.new(rest_client, config, options).run
298
+ end
299
+
277
300
  def standalone_cartridges
278
- @standalone_cartridges ||= rest_client.cartridges.select{ |c| c.type == 'standalone' }
301
+ @standalone_cartridges ||= all_cartridges.select{ |c| c.type == 'standalone' }
302
+ end
303
+ def all_cartridges
304
+ @all_cartridges = rest_client.cartridges
279
305
  end
280
306
 
281
307
  def check_cartridges(cartridge_names)
282
308
  cartridge_names = Array(cartridge_names).map{ |s| s.strip if s && s.length > 0 }.compact
283
309
 
284
- unless cartridge_name = cartridge_names.first
285
- section(:bottom => 1){ list_cartridges }
286
- raise ArgumentError.new "Every application needs a web cartridge to handle incoming web requests. Please provide the short name of one of the carts listed above."
287
- end
288
-
289
- unless standalone_cartridges.find{ |c| c.name == cartridge_name }
290
- matching_cartridges = standalone_cartridges.select{ |c| c.name.include?(cartridge_name) }
291
- if matching_cartridges.length == 1
292
- cartridge_names[0] = use_cart(matching_cartridges.first, cartridge_name)
293
- elsif matching_cartridges.present?
294
- paragraph { list_cartridges(matching_cartridges) }
295
- raise RHC::MultipleCartridgesException.new("There are multiple web cartridges named '#{cartridge_name}'. Please provide the short name of your desired cart.")
310
+ cartridge_names.map do |name|
311
+ all_cartridges.find{ |c| c.name == name } ||
312
+ begin
313
+ matching_cartridges = all_cartridges.select{ |c| c.name.include?(name) }
314
+ unless matching_cartridges.length == 1
315
+ if matching_cartridges.present?
316
+ paragraph { list_cartridges(matching_cartridges) }
317
+ raise RHC::MultipleCartridgesException, "There are multiple web cartridges named '#{name}'. Please provide the short name of your desired cart." if matching_cartridges.present?
318
+ else
319
+ paragraph { list_cartridges(all_cartridges) }
320
+ raise RHC::CartridgeNotFoundException, "There are no cartridges that match '#{name}'."
321
+ end
322
+ end
323
+ use_cart(matching_cartridges.first, name)
324
+ end
325
+ end.tap do |carts|
326
+ if carts.none?(&:only_in_new?)
327
+ section(:bottom => 1){ list_cartridges }
328
+ 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."
296
329
  end
297
330
  end
298
- cartridge_names.first(1)
299
331
  end
300
332
 
301
333
  def use_cart(cart, for_cartridge_name)
302
- info "Using #{cart.name}#{cart.display_name ? " (#{cart.display_name})" : ''} instead of '#{for_cartridge_name}'"
303
- cart.name
334
+ info "Using #{cart.name}#{cart.display_name ? " (#{cart.display_name})" : ''} for '#{for_cartridge_name}'"
335
+ cart
304
336
  end
305
337
 
306
338
  def list_cartridges(cartridges=standalone_cartridges)
@@ -317,14 +349,14 @@ module RHC::Commands
317
349
  result
318
350
  end
319
351
 
320
- def create_app(name, cartridge, rest_domain, gear_size=nil, scale=nil)
321
- app_options = {:cartridge => cartridge}
352
+ def create_app(name, cartridges, rest_domain, gear_size=nil, scale=nil, from_code=nil)
353
+ app_options = {:cartridges => Array(cartridges)}
322
354
  app_options[:gear_profile] = gear_size if gear_size
323
355
  app_options[:scale] = scale if scale
356
+ app_options[:initial_git_url] = from_code if from_code
324
357
  app_options[:debug] = true if @debug
325
-
326
358
  debug "Creating application '#{name}' with these options - #{app_options.inspect}"
327
- rest_app = rest_domain.add_application name, app_options
359
+ rest_app = rest_domain.add_application(name, app_options)
328
360
  debug "'#{rest_app.name}' created"
329
361
 
330
362
  rest_app
@@ -336,6 +368,48 @@ module RHC::Commands
336
368
  raise
337
369
  end
338
370
 
371
+ def add_jenkins_app(rest_domain)
372
+ create_app(jenkins_app_name, "jenkins-1.4", rest_domain)
373
+ end
374
+
375
+ def add_jenkins_cartridge(rest_app)
376
+ rest_app.add_cartridge("jenkins-client-1.4")
377
+ end
378
+
379
+ def add_jenkins_client_to(rest_app, messages)
380
+ say "Setting up Jenkins build ... "
381
+ successful, attempts, exit_code, exit_message = false, 1, 157, nil
382
+ while (!successful && exit_code == 157 && attempts < MAX_RETRIES)
383
+ begin
384
+ cartridge = add_jenkins_cartridge(rest_app)
385
+ successful = true
386
+
387
+ success "done"
388
+ messages.concat(cartridge.messages)
389
+
390
+ rescue RHC::Rest::ServerErrorException => e
391
+ if (e.code == 157)
392
+ # error downloading Jenkins /jnlpJars/jenkins-cli.jar
393
+ attempts += 1
394
+ debug "Jenkins server could not be contacted, sleep and then retry: attempt #{attempts}\n #{e.message}"
395
+ Kernel.sleep(10)
396
+ end
397
+ exit_code = e.code
398
+ exit_message = e.message
399
+ rescue Exception => e
400
+ # timeout and other exceptions
401
+ exit_code = 1
402
+ exit_message = e.message
403
+ end
404
+ end
405
+ unless successful
406
+ warn "not complete"
407
+ add_issue("Jenkins client failed to install - #{exit_message}",
408
+ "Install the jenkins client",
409
+ "rhc cartridge add jenkins-client -a #{rest_app.name}")
410
+ end
411
+ end
412
+
339
413
  def dns_propagated?(host, sleep_time=2)
340
414
  #
341
415
  # Confirm that the host exists in DNS
@@ -362,88 +436,15 @@ module RHC::Commands
362
436
  found
363
437
  end
364
438
 
365
- def check_sshkeys!
366
- wizard = RHC::SSHWizard.new(rest_client)
367
- wizard.run
368
- end
369
-
370
- def run_git_clone(rest_app)
371
- debug "Pulling new repo down"
372
-
373
- check_sshkeys! unless options.noprompt
374
-
375
- repo_dir = options.repo || rest_app.name
376
- git_clone_repo rest_app.git_url, repo_dir
377
-
378
- configure_git rest_app
379
-
380
- true
381
- end
382
-
383
- def configure_git(rest_app)
384
- debug "Configuring git repo"
385
-
386
- repo_dir = options.repo || rest_app.name
387
- Dir.chdir(repo_dir) do |dir|
388
- git_config_set "rhc.app-uuid", rest_app.uuid
389
- end
390
- end
391
-
392
439
  def enable_jenkins?
393
440
  # legacy issue, commander 4.0.x will place the option in the hash with nil value (BZ878407)
394
441
  options.__hash__.has_key?(:enable_jenkins)
395
442
  end
396
443
 
397
444
  def jenkins_app_name
398
- return "jenkins" if options.enable_jenkins == true || options.enable_jenkins == "true" || (enable_jenkins? && options.enable_jenkins.nil?)
399
- return options.enable_jenkins if options.enable_jenkins.is_a?(String)
400
- nil
401
- end
402
-
403
- def check_jenkins(app_name, rest_domain)
404
- debug "Checking if jenkins arguments are valid"
405
- raise ArgumentError, "The --no-dns option can't be used in conjunction with --enable-jenkins when creating an application. Either remove the --no-dns option or first install your application with --no-dns and then use 'rhc cartridge add' to embed the Jenkins client." unless options.dns
406
-
407
-
408
- begin
409
- jenkins_rest_app = rest_domain.find_application(:framework => "jenkins-1.4")
410
- rescue RHC::ApplicationNotFoundException
411
- debug "No Jenkins apps found during check"
412
-
413
- # app name and jenkins app name are the same
414
- raise ArgumentError, "You have named both your main application and your Jenkins application '#{app_name}'. In order to continue you'll need to specify a different name with --enable-jenkins or choose
415
- a different application name." if jenkins_app_name == app_name
416
-
417
- # jenkins app name and existing app are the same
418
- begin
419
- rest_app = rest_domain.find_application(:name => jenkins_app_name)
420
- raise ArgumentError, "You have named your Jenkins application the same as an existing application '#{app_name}'. In order to continue you'll need to specify a different name with --enable-jenkins or delete the current application using 'rhc app delete #{app_name}'"
421
- rescue RHC::ApplicationNotFoundException
422
- end
423
-
424
- debug "Jenkins arguments valid"
425
- return nil
426
- end
427
-
428
- say "Found existing Jenkins application: #{jenkins_rest_app.name}"
429
- say "Ignoring user specified Jenkins app name : #{options.enable_jenkins}" if jenkins_rest_app.name != options.enable_jenkins and options.enable_jenkins.is_a?(String)
430
-
431
- debug "Jenkins arguments valid"
432
- jenkins_rest_app
433
- end
434
-
435
- def setup_jenkins_app(rest_domain)
436
- debug "Creating a new jenkins application"
437
- rest_app = create_app(jenkins_app_name, "jenkins-1.4", rest_domain)
438
-
439
- say "Jenkins domain name is being propagated worldwide (this might take a minute)..."
440
- # If we can't get the dns we can't install the client so return nil
441
- dns_propagated?(rest_app.host) ? rest_app : nil
442
-
443
- end
444
-
445
- def setup_jenkins_client(rest_app)
446
- rest_app.add_cartridge("jenkins-client-1.4", 300)
445
+ if options.enable_jenkins.is_a? String
446
+ options.enable_jenkins
447
+ end || "jenkins"
447
448
  end
448
449
 
449
450
  def run_nslookup(host)
@@ -477,7 +478,7 @@ We recommend you wait a few minutes then clone your git repository manually.
477
478
  WINSOCKISSUE
478
479
  add_issue(issue,
479
480
  "Clone your git repo",
480
- "rhc app git-clone #{rest_app.name}")
481
+ "rhc git-clone #{rest_app.name}")
481
482
 
482
483
  return true
483
484
  end