morpheus-cli 2.10.2 → 2.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ecd262f3f7122ce8af78b08d5539dc450db6dafa
4
- data.tar.gz: b0c4136d4746c82f05d1602f10797dd632f7c081
3
+ metadata.gz: 63a320542a8d185d0e96a8ca526ff4ff23b21868
4
+ data.tar.gz: aeccbd35966dd37101a032acd9346804767ae627
5
5
  SHA512:
6
- metadata.gz: 40e0e43ff256acc748caa4577e1231b733c2c25e725a52e122b61602b092a5a541464afbcddf7010494f6e1901c973df6fce291a6961c0ed6100f5e076848f29
7
- data.tar.gz: 214f1a02d4d192ec428d13a6bec8d1e33de2aa89f05cf51c8f94e7876bca9169a049fe7c8fbcf649ff357e9ec655dd1be00eec4fb5ea499e897a74e0564a8751
6
+ metadata.gz: c825db622aed4d8d571d97bf13a250333f04543834b7749f57a33555fc17e89a0190797e6c0ec6b06dfdf246f623329c4fd7b3b9aef097ac0160e196f1d80712
7
+ data.tar.gz: fe005b26254bbc9fcf41cde841a5ad40715583d0b637a763e8d0612bdc23341f98df11e9385b98a4d8588fc15c6e53a517d040f9463935be7b2e139141500c84
@@ -81,7 +81,12 @@ end
81
81
 
82
82
  # ok, execute the command (or alias)
83
83
  begin
84
- cmd_result = Morpheus::Cli::CliRegistry.exec(args[0], args[1..-1])
84
+ # shell is a Singleton command class
85
+ if args[0] == "shell"
86
+ cmd_result = Morpheus::Cli::Shell.instance.handle(args[1..-1])
87
+ else
88
+ cmd_result = Morpheus::Cli::CliRegistry.exec(args[0], args[1..-1])
89
+ end
85
90
  if cmd_result == false
86
91
  exit 1
87
92
  else
@@ -27,7 +27,7 @@ class Morpheus::Cli::AliasCommand
27
27
  exit 127
28
28
  elsif self.class.has_subcommand?(args[0])
29
29
  handle_subcommand(args)
30
- elsif args.count == 1
30
+ elsif args.count == 1 || (args.count == 2 && args.include?('-e'))
31
31
  add(args)
32
32
  else
33
33
  handle_subcommand(args)
@@ -80,7 +80,7 @@ class Morpheus::Cli::AliasCommand
80
80
  Morpheus::Cli::CliRegistry.instance.add_alias(alias_name, command_string)
81
81
  #print "registered alias #{alias_name}", "\n"
82
82
  if do_export
83
- puts "exporting alias '#{alias_name}' now..."
83
+ # puts "exporting alias '#{alias_name}' now..."
84
84
  morpheus_profile = Morpheus::Cli::DotFile.new(Morpheus::Cli::DotFile.morpheus_profile_filename)
85
85
  morpheus_profile.export_aliases({(alias_name) => command_string})
86
86
  end
@@ -146,6 +146,9 @@ module Morpheus
146
146
  credential_map[app_name] = token
147
147
  begin
148
148
  fn = credentials_file_path
149
+ if !Dir.exists?(File.dirname(fn))
150
+ FileUtils.mkdir_p(File.dirname(fn))
151
+ end
149
152
  print "#{dark} #=> adding credentials to #{fn}#{reset}\n" if Morpheus::Logging.debug?
150
153
  File.open(fn, 'w') {|f| f.write credential_map.to_yaml } #Store
151
154
  FileUtils.chmod(0600, fn)
@@ -1,3 +1,4 @@
1
+ require 'fileutils'
1
2
  require 'yaml'
2
3
  require 'io/console'
3
4
  require 'rest_client'
@@ -550,8 +551,12 @@ public
550
551
  end
551
552
 
552
553
  def save_groups(groups_map)
553
- File.open(groups_file_path, 'w') {|f| f.write groups_map.to_yaml } #Store
554
- FileUtils.chmod(0600, groups_file_path)
554
+ fn = groups_file_path
555
+ if !Dir.exists?(File.dirname(fn))
556
+ FileUtils.mkdir_p(File.dirname(fn))
557
+ end
558
+ File.open(fn, 'w') {|f| f.write groups_map.to_yaml } #Store
559
+ FileUtils.chmod(0600, fn)
555
560
  @@groups = groups_map
556
561
  end
557
562
 
@@ -48,15 +48,19 @@ class Morpheus::Cli::Login
48
48
  else
49
49
  @appliance_name, @appliance_url = nil, nil
50
50
  end
51
+ if !@appliance_name
52
+ print_red_alert "You have no appliance named '#{options[:remote]}' configured. See the `remote add` command."
53
+ return false
54
+ end
51
55
  else
52
56
  @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
53
- end
54
-
55
- begin
56
57
  if !@appliance_name
57
58
  print yellow,"Please specify a remote appliance with -r or see the command `remote use`#{reset}\n"
58
59
  return false
59
60
  end
61
+ end
62
+
63
+ begin
60
64
  if options[:quiet]
61
65
  if username.empty? || password.empty?
62
66
  print yellow,"You have not specified username and password\n"
@@ -162,7 +162,7 @@ module Morpheus::Cli::PrintHelper
162
162
 
163
163
  def print_results_pagination(json_response)
164
164
  if json_response && json_response["meta"]
165
- print cyan,"\nViewing #{json_response['meta']['offset'].to_i + 1}-#{json_response['meta']['offset'].to_i + json_response['meta']['size'].to_i} of #{json_response['meta']['total']}\n"
165
+ print cyan,"\nViewing #{json_response['meta']['offset'].to_i + 1}-#{json_response['meta']['offset'].to_i + json_response['meta']['size'].to_i} of #{json_response['meta']['total']}\n", reset
166
166
  end
167
167
  end
168
168
 
@@ -217,29 +217,29 @@ module Morpheus::Cli::ProvisioningHelper
217
217
  end
218
218
 
219
219
  payload = {
220
- :zoneId => cloud_id,
221
- :instance => {
222
- :name => instance_name,
223
- :site => {
224
- :id => group_id
220
+ 'zoneId' => cloud_id,
221
+ 'instance' => {
222
+ 'name' => instance_name,
223
+ 'site' => {
224
+ 'id' => group_id
225
225
  },
226
- :instanceType => {
227
- :code => instance_type_code
226
+ 'instanceType' => {
227
+ 'code' => instance_type_code
228
228
  }
229
229
  }
230
230
  }
231
231
 
232
232
  # Description
233
233
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false}], options[:options])
234
- payload[:instance][:description] = v_prompt['description'] if !v_prompt['description'].empty?
234
+ payload['instance']['description'] = v_prompt['description'] if !v_prompt['description'].empty?
235
235
 
236
236
  # Environment
237
237
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'instanceContext', 'fieldLabel' => 'Environment', 'type' => 'select', 'required' => false, 'selectOptions' => instance_context_options()}], options[:options])
238
- payload[:instance][:instanceContext] = v_prompt['instanceContext'] if !v_prompt['instanceContext'].empty?
238
+ payload['instance']['instanceContext'] = v_prompt['instanceContext'] if !v_prompt['instanceContext'].empty?
239
239
 
240
240
  # Tags
241
241
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tags', 'fieldLabel' => 'Tags', 'type' => 'text', 'required' => false}], options[:options])
242
- payload[:instance][:tags] = v_prompt['tags'].split(',').collect {|it| it.to_s.strip }.compact.uniq if !v_prompt['tags'].empty?
242
+ payload['instance']['tags'] = v_prompt['tags'].split(',').collect {|it| it.to_s.strip }.compact.uniq if !v_prompt['tags'].empty?
243
243
 
244
244
  # Version and Layout
245
245
 
@@ -247,7 +247,11 @@ module Morpheus::Cli::ProvisioningHelper
247
247
  layout_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'layout', 'type' => 'select', 'fieldLabel' => 'Layout', 'optionSource' => 'layoutsForCloud', 'required' => true, 'description' => 'Select which configuration of the instance type to be provisioned.'}],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], version: version_prompt['version']})
248
248
  layout_id = layout_prompt['layout']
249
249
  layout = instance_type['instanceTypeLayouts'].find{ |lt| lt['id'] == layout_id.to_i}
250
- payload[:instance][:layout] = {id: layout['id']}
250
+ if !layout
251
+ print_red_alert "Layout not found by id #{layout_id}"
252
+ exit 1
253
+ end
254
+ payload['instance']['layout'] = {'id' => layout['id']}
251
255
 
252
256
  # prompt for service plan
253
257
  service_plans_json = @instances_interface.service_plans({zoneId: cloud_id, layoutId: layout_id})
@@ -255,14 +259,12 @@ module Morpheus::Cli::ProvisioningHelper
255
259
  service_plans_dropdown = service_plans.collect {|sp| {'name' => sp["name"], 'value' => sp["id"]} } # already sorted
256
260
  plan_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'servicePlan', 'type' => 'select', 'fieldLabel' => 'Plan', 'selectOptions' => service_plans_dropdown, 'required' => true, 'description' => 'Choose the appropriately sized plan for this instance'}],options[:options])
257
261
  service_plan = service_plans.find {|sp| sp["id"] == plan_prompt['servicePlan'].to_i }
258
- # todo: pick one of these three, let's go with the last one...
259
- #payload[:servicePlan] = service_plan["id"] # pre-2.10 appliances
260
- payload[:instance][:plan] = {id: service_plan["id"]}
262
+ payload['instance']['plan'] = {'id' => service_plan["id"]}
261
263
 
262
264
  # prompt for volumes
263
265
  volumes = prompt_volumes(service_plan, options, api_client, {})
264
266
  if !volumes.empty?
265
- payload[:volumes] = volumes
267
+ payload['volumes'] = volumes
266
268
  end
267
269
 
268
270
  if layout["provisionType"] && layout["provisionType"]["id"] && layout["provisionType"]["hasNetworks"]
@@ -270,46 +272,42 @@ module Morpheus::Cli::ProvisioningHelper
270
272
  begin
271
273
  network_interfaces = prompt_network_interfaces(cloud_id, layout["provisionType"]["id"], options)
272
274
  if !network_interfaces.empty?
273
- payload[:networkInterfaces] = network_interfaces
275
+ payload['networkInterfaces'] = network_interfaces
274
276
  end
275
277
  rescue RestClient::Exception => e
276
278
  print_yellow_warning "Unable to load network options. Proceeding..."
277
279
  print_rest_exception(e, options) if Morpheus::Logging.debug?
278
280
  end
279
281
  end
280
- type_payload = {}
282
+
283
+
281
284
  if !layout['optionTypes'].nil? && !layout['optionTypes'].empty?
282
- type_payload = Morpheus::Cli::OptionTypes.prompt(layout['optionTypes'],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, zoneId: cloud_id, instanceTypeId: instance_type['id'], version: version_prompt['version']})
285
+ type_payload = Morpheus::Cli::OptionTypes.prompt(layout['optionTypes'],options[:options],@api_client,{groupId: group_id, cloudId: cloud_id, zoneId: cloud_id, instanceTypeId: instance_type['id'], version: version_prompt['version']})
286
+ payload.deep_merge!(type_payload)
283
287
  elsif !instance_type['optionTypes'].nil? && !instance_type['optionTypes'].empty?
284
- type_payload = Morpheus::Cli::OptionTypes.prompt(instance_type['optionTypes'],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, zoneId: cloud_id, instanceTypeId: instance_type['id'], version: version_prompt['version']})
288
+ type_payload = Morpheus::Cli::OptionTypes.prompt(instance_type['optionTypes'],options[:options],@api_client,{groupId: group_id, cloudId: cloud_id, zoneId: cloud_id, instanceTypeId: instance_type['id'], version: version_prompt['version']})
289
+ payload.deep_merge!(type_payload)
285
290
  end
286
- if !type_payload['config'].nil?
287
- payload.merge!(type_payload['config'])
288
- end
289
-
290
- provision_payload = {}
291
+
291
292
  if !layout['provisionType'].nil? && !layout['provisionType']['optionTypes'].nil? && !layout['provisionType']['optionTypes'].empty?
292
293
  instance_type_option_types = layout['provisionType']['optionTypes']
293
294
  # remove volume options if volumes were configured
294
- if !payload[:volumes].empty?
295
+ if !payload['volumes'].empty?
295
296
  instance_type_option_types = reject_volume_option_types(instance_type_option_types)
296
297
  end
297
298
  # remove networkId option if networks were configured above
298
- if !payload[:networkInterfaces].empty?
299
+ if !payload['networkInterfaces'].empty?
299
300
  instance_type_option_types = reject_networking_option_types(instance_type_option_types)
300
301
  end
302
+ #print "#{dark} #=> gathering instance type option types for layout provision type...#{reset}\n" if Morpheus::Logging.debug?
301
303
  provision_payload = Morpheus::Cli::OptionTypes.prompt(instance_type_option_types,options[:options],api_client,{groupId: group_id, cloudId: cloud_id, zoneId: cloud_id, instanceTypeId: instance_type['id'], version: version_prompt['version']})
302
- end
303
-
304
- payload[:config] = provision_payload['config'] || {}
305
- if provision_payload['server']
306
- payload[:server] = provision_payload['server'] || {}
304
+ payload.deep_merge!(provision_payload)
307
305
  end
308
306
 
309
307
  # prompt for environment variables
310
308
  evars = prompt_evars(options)
311
309
  if !evars.empty?
312
- payload[:evars] = evars
310
+ payload['evars'] = evars
313
311
  end
314
312
 
315
313
  return payload
@@ -844,10 +842,10 @@ module Morpheus::Cli::ProvisioningHelper
844
842
  network_interface['network']['id'] = v_prompt[field_context]['networkId'].to_i
845
843
  selected_network = networks.find {|it| it["id"] == network_interface['network']['id'] }
846
844
 
847
- # if !selected_network
848
- # print_red_alert "Network not found by id #{network_interface['network']['id']}!"
849
- # exit 1
850
- # end
845
+ if !selected_network
846
+ print_red_alert "Network not found by id #{network_interface['network']['id']}!"
847
+ exit 1
848
+ end
851
849
 
852
850
  # choose network interface type
853
851
  if enable_network_type_selection && !network_interface_type_options.empty?
@@ -41,23 +41,35 @@ module Morpheus
41
41
  value = nil
42
42
  value_found=false
43
43
  if option_type['fieldContext']
44
- results[option_type['fieldContext']] ||= {}
45
- context_map = results[option_type['fieldContext']]
46
- if options[option_type['fieldContext']] and options[option_type['fieldContext']].key?(option_type['fieldName'])
47
- value = options[option_type['fieldContext']][option_type['fieldName']]
44
+ cur_namespace = options
45
+ namespaces = option_type['fieldContext'].split(".")
46
+ namespaces.each do |ns|
47
+ next if ns.empty?
48
+ cur_namespace[ns.to_s] ||= {}
49
+ cur_namespace = cur_namespace[ns.to_s]
50
+ context_map[ns.to_s] ||= {}
51
+ context_map = context_map[ns.to_s]
52
+ end
53
+ # use the value passed in the options map
54
+ if cur_namespace.key?(option_type['fieldName'])
55
+ value = cur_namespace[option_type['fieldName']]
48
56
  if option_type['type'] == 'number'
49
57
  value = value.to_i
58
+ elsif option_type['type'] == 'select'
59
+ # this should just fall down through below, with the extra params no_prompt, use_value
60
+ value = select_prompt(option_type, api_client, api_params, true, value)
50
61
  end
51
62
  value_found = true
52
63
  end
53
- end
54
-
55
- if value_found == false && options.key?(option_type['fieldName'])
56
- value = options[option_type['fieldName']]
57
- if option_type['type'] == 'number'
58
- value = value.to_i
64
+ else
65
+ # no fieldContext
66
+ if value_found == false && options.key?(option_type['fieldName'])
67
+ value = options[option_type['fieldName']]
68
+ if option_type['type'] == 'number'
69
+ value = value.to_i
70
+ end
71
+ value_found = true
59
72
  end
60
- value_found = true
61
73
  end
62
74
 
63
75
  # no_prompt means skip prompting and instead
@@ -78,8 +90,8 @@ module Morpheus
78
90
  end
79
91
  if !value_found
80
92
  if option_type['required']
81
- print Term::ANSIColor.red, "\nMissing Required Option\n\n"
82
- print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - ", Term::ANSIColor.reset , "#{option_type['description']}\n"
93
+ print Term::ANSIColor.red, "\nMissing Required Option\n\n", Term::ANSIColor.reset
94
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
83
95
  print "\n"
84
96
  exit 1
85
97
  else
@@ -190,25 +202,41 @@ module Morpheus
190
202
  return value
191
203
  end
192
204
 
193
- def self.select_prompt(option_type,api_client, api_params={}, no_prompt=false)
205
+ def self.select_prompt(option_type,api_client, api_params={}, no_prompt=false, use_value=nil)
194
206
  value_found = false
195
207
  value = nil
208
+ # local array of options
196
209
  if option_type['selectOptions']
197
210
  select_options = option_type['selectOptions']
211
+ # remote optionSource aka /api/options/$optionSource?
198
212
  elsif option_type['optionSource']
199
213
  select_options = load_source_options(option_type['optionSource'],api_client,api_params)
200
214
  else
201
215
  raise "select_prompt() requires selectOptions or optionSource!"
202
216
  end
203
- if !select_options.nil? && select_options.count == 1 && option_type['skipSingleOption'] == true
217
+ # ensure the preselected value (passed as an option) is in the dropdown
218
+ if !use_value.nil?
219
+ matched_value = select_options.find {|opt| opt['value'].to_s == use_value.to_s }
220
+ if !matched_value.nil?
221
+ value = use_value
222
+ value_found = true
223
+ else
224
+ print Term::ANSIColor.red, "\nInvalid Option #{option_type['fieldLabel']}: [#{use_value}]\n\n", Term::ANSIColor.reset
225
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
226
+ display_select_options(select_options)
227
+ print "\n"
228
+ exit 1
229
+ end
230
+ elsif !select_options.nil? && select_options.count == 1 && option_type['skipSingleOption'] == true
204
231
  value_found = true
205
232
  value = select_options[0]['value']
206
233
  end
234
+
207
235
  if no_prompt
208
236
  if !value_found
209
237
  if option_type['required']
210
- print Term::ANSIColor.red, "\nMissing Required Option\n\n"
211
- print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - ", Term::ANSIColor.reset , "#{option_type['description']}\n"
238
+ print Term::ANSIColor.red, "\nMissing Required Option\n\n", Term::ANSIColor.reset
239
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
212
240
  display_select_options(select_options)
213
241
  print "\n"
214
242
  exit 1
@@ -369,7 +397,6 @@ module Morpheus
369
397
  select_options.each do |option|
370
398
  puts " * #{option['name']} [#{option['value']}]"
371
399
  end
372
- puts "\n\n"
373
400
  end
374
401
 
375
402
  def self.format_option_types_help(option_types)
@@ -475,8 +475,12 @@ class Morpheus::Cli::Remote
475
475
  end
476
476
 
477
477
  def save_appliances(new_config)
478
- File.open(appliances_file_path, 'w') {|f| f.write new_config.to_yaml } #Store
479
- FileUtils.chmod(0600, appliances_file_path)
478
+ fn = appliances_file_path
479
+ if !Dir.exists?(File.dirname(fn))
480
+ FileUtils.mkdir_p(File.dirname(fn))
481
+ end
482
+ File.open(fn, 'w') {|f| f.write new_config.to_yaml } #Store
483
+ FileUtils.chmod(0600, fn)
480
484
  #@@appliance_config = load_appliance_file
481
485
  @@appliance_config = new_config
482
486
  end
@@ -119,14 +119,14 @@ class Morpheus::Cli::Shell
119
119
  execute_command(cmd)
120
120
  end
121
121
  # skip logging of exit and !cmd
122
- unless input.strip.empty? || (["exit"].include?(input.strip)) || input.strip[0].to_s.chr == "!"
122
+ unless input.strip.empty? || (["exit", "history"].include?(input.strip)) || input.strip[0].to_s.chr == "!"
123
123
  log_history_command(input.strip)
124
124
  end
125
125
  end
126
126
 
127
127
  def execute_command(input)
128
128
  #puts "shell execute_command(#{input})"
129
- @command_options = {}
129
+ @command_options ||= {}
130
130
 
131
131
  input = input.to_s.strip
132
132
 
@@ -213,29 +213,35 @@ class Morpheus::Cli::Shell
213
213
  elsif input =~ /^\!.+/
214
214
  cmd_number = input.sub("!", "").to_i
215
215
  if cmd_number != 0
216
- input = @history[cmd_number]
217
- if !input
216
+ old_input = @history[cmd_number]
217
+ if !old_input
218
218
  puts "Command not found by number #{cmd_number}"
219
219
  return 0
220
220
  end
221
- #puts "executing history command: (#{cmd_number}) #{input}"
222
- execute_commands(input)
223
- return 0
221
+ #puts "executing history command: (#{cmd_number}) #{old_input}"
222
+ # log_history_command(old_input)
223
+ # remove this from readline, and replace it with the old command
224
+ Readline::HISTORY.pop
225
+ Readline::HISTORY << old_input
226
+ return execute_commands(old_input)
224
227
  end
225
228
 
226
229
  elsif input == "insecure"
227
230
  Morpheus::RestClient.enable_ssl_verification = false
228
231
  return 0
232
+
229
233
  # use log-level [debug|info]
230
234
  # elsif input =~ /^log_level/ # hidden for now
231
235
  # log_level = input.sub(/^log_level\s*/, '').strip
232
236
  # if log_level == ""
233
237
  # puts "#{Morpheus::Logging.log_level}"
234
- # elsif log_level == "debug"
235
- # #log_history_command(input)
236
- # @command_options[:debug] = true
237
- # Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::DEBUG)
238
- # ::RestClient.log = Morpheus::Logging.debug? ? STDOUT : nil
238
+ elsif input == "debug"
239
+ log_history_command(input)
240
+ Morpheus::Cli::LogLevelCommand.new.handle(["debug"])
241
+ @command_options[:debug] = true
242
+ return 0
243
+ # Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::DEBUG)
244
+ # ::RestClient.log = Morpheus::Logging.debug? ? STDOUT : nil
239
245
  # elsif log_level == "info"
240
246
  # #log_history_command(input)
241
247
  # @command_options.delete(:debug)
@@ -258,7 +264,7 @@ class Morpheus::Cli::Shell
258
264
  # return 0
259
265
  elsif ["hello","hi","hey","hola"].include?(input.strip.downcase)
260
266
  print "#{input.capitalize}, how may I #{cyan}help#{reset} you?\n"
261
- return
267
+ return 0
262
268
  # use morpheus coloring [on|off]
263
269
  # elsif input == "colorize"
264
270
  # Term::ANSIColor::coloring = true
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "2.10.2"
4
+ VERSION = "2.10.3"
5
5
  end
6
6
  end
@@ -0,0 +1,42 @@
1
+ # Provide deep_merge.
2
+ # Borrowed from rails active_support
3
+ # https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
4
+ #
5
+ class Hash
6
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
7
+ #
8
+ # h1 = { a: true, b: { c: [1, 2, 3] } }
9
+ # h2 = { a: false, b: { x: [3, 4, 5] } }
10
+ #
11
+ # h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
12
+ #
13
+ # Like with Hash#merge in the standard library, a block can be provided
14
+ # to merge values:
15
+ #
16
+ # h1 = { a: 100, b: 200, c: { c1: 100 } }
17
+ # h2 = { b: 250, c: { c1: 200 } }
18
+ # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
19
+ # # => { a: 100, b: 450, c: { c1: 300 } }
20
+ def deep_merge(other_hash, &block)
21
+ dup.deep_merge!(other_hash, &block)
22
+ end
23
+
24
+ # Same as +deep_merge+, but modifies +self+.
25
+ def deep_merge!(other_hash, &block)
26
+ other_hash.each_pair do |current_key, other_value|
27
+ this_value = self[current_key]
28
+
29
+ self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
30
+ this_value.deep_merge(other_value, &block)
31
+ else
32
+ if block_given? && key?(current_key)
33
+ block.call(current_key, this_value, other_value)
34
+ else
35
+ other_value
36
+ end
37
+ end
38
+ end
39
+
40
+ self
41
+ end
42
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: morpheus-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.2
4
+ version: 2.10.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Estes
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2017-03-07 00:00:00.000000000 Z
14
+ date: 2017-03-14 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -218,6 +218,7 @@ files:
218
218
  - lib/morpheus/cli/virtual_images.rb
219
219
  - lib/morpheus/cli/whoami.rb
220
220
  - lib/morpheus/cli/workflows.rb
221
+ - lib/morpheus/ext/hash.rb
221
222
  - lib/morpheus/ext/nil_class.rb
222
223
  - lib/morpheus/formatters.rb
223
224
  - lib/morpheus/logging.rb