morpheus-cli 5.5.2.1 → 5.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/Dockerfile +1 -1
  4. data/README.md +57 -4
  5. data/Rakefile +9 -0
  6. data/bin/morpheus +4 -4
  7. data/lib/morpheus/api/api_client.rb +8 -2
  8. data/lib/morpheus/api/archive_buckets_interface.rb +1 -1
  9. data/lib/morpheus/api/archive_files_interface.rb +3 -3
  10. data/lib/morpheus/api/clients_interface.rb +2 -2
  11. data/lib/morpheus/api/clusters_interface.rb +8 -1
  12. data/lib/morpheus/api/containers_interface.rb +29 -16
  13. data/lib/morpheus/api/custom_instance_types_interface.rb +0 -2
  14. data/lib/morpheus/api/doc_interface.rb +8 -6
  15. data/lib/morpheus/api/file_copy_request_interface.rb +1 -1
  16. data/lib/morpheus/api/health_interface.rb +1 -1
  17. data/lib/morpheus/api/image_builder_interface.rb +3 -3
  18. data/lib/morpheus/api/instances_interface.rb +25 -0
  19. data/lib/morpheus/api/logs_interface.rb +2 -4
  20. data/lib/morpheus/api/monitoring_interface.rb +6 -6
  21. data/lib/morpheus/api/packages_interface.rb +1 -1
  22. data/lib/morpheus/api/reports_interface.rb +1 -1
  23. data/lib/morpheus/api/servers_interface.rb +9 -1
  24. data/lib/morpheus/api/storage_providers_interface.rb +2 -2
  25. data/lib/morpheus/api/virtual_images_interface.rb +1 -1
  26. data/lib/morpheus/api.rb +2 -0
  27. data/lib/morpheus/benchmarking.rb +1 -1
  28. data/lib/morpheus/cli/cli_command.rb +69 -36
  29. data/lib/morpheus/cli/cli_registry.rb +19 -10
  30. data/lib/morpheus/cli/commands/access_token_command.rb +1 -1
  31. data/lib/morpheus/cli/commands/apps.rb +1 -1
  32. data/lib/morpheus/cli/commands/archives_command.rb +25 -33
  33. data/lib/morpheus/cli/commands/blueprints_command.rb +10 -21
  34. data/lib/morpheus/cli/commands/boot_scripts_command.rb +2 -2
  35. data/lib/morpheus/cli/commands/cat_command.rb +1 -1
  36. data/lib/morpheus/cli/commands/catalog_item_types_command.rb +12 -12
  37. data/lib/morpheus/cli/commands/clouds.rb +3 -3
  38. data/lib/morpheus/cli/commands/clusters.rb +154 -3
  39. data/lib/morpheus/cli/commands/containers_command.rb +398 -253
  40. data/lib/morpheus/cli/commands/deployments.rb +1 -1
  41. data/lib/morpheus/cli/commands/deploys.rb +9 -9
  42. data/lib/morpheus/cli/commands/doc.rb +15 -16
  43. data/lib/morpheus/cli/commands/execution_request_command.rb +2 -2
  44. data/lib/morpheus/cli/commands/file_copy_request_command.rb +5 -5
  45. data/lib/morpheus/cli/commands/groups.rb +2 -2
  46. data/lib/morpheus/cli/commands/health_command.rb +4 -4
  47. data/lib/morpheus/cli/commands/hosts.rb +43 -5
  48. data/lib/morpheus/cli/commands/image_builder_command.rb +1 -1
  49. data/lib/morpheus/cli/commands/instances.rb +419 -148
  50. data/lib/morpheus/cli/commands/integrations_command.rb +22 -20
  51. data/lib/morpheus/cli/commands/key_pairs.rb +2 -2
  52. data/lib/morpheus/cli/commands/library_container_scripts_command.rb +2 -2
  53. data/lib/morpheus/cli/commands/library_container_templates_command.rb +2 -2
  54. data/lib/morpheus/cli/commands/library_instance_types_command.rb +3 -3
  55. data/lib/morpheus/cli/commands/library_spec_templates_command.rb +2 -2
  56. data/lib/morpheus/cli/commands/login.rb +1 -1
  57. data/lib/morpheus/cli/commands/man_command.rb +32 -18
  58. data/lib/morpheus/cli/commands/packages_command.rb +11 -11
  59. data/lib/morpheus/cli/commands/plugins.rb +1 -1
  60. data/lib/morpheus/cli/commands/policies_command.rb +4 -4
  61. data/lib/morpheus/cli/commands/preseed_scripts_command.rb +2 -2
  62. data/lib/morpheus/cli/commands/remote.rb +1 -1
  63. data/lib/morpheus/cli/commands/reports_command.rb +3 -3
  64. data/lib/morpheus/cli/commands/roles.rb +6 -3
  65. data/lib/morpheus/cli/commands/security_groups.rb +1 -1
  66. data/lib/morpheus/cli/commands/shell.rb +40 -62
  67. data/lib/morpheus/cli/commands/snapshots.rb +3 -5
  68. data/lib/morpheus/cli/commands/source_command.rb +8 -16
  69. data/lib/morpheus/cli/commands/storage_providers_command.rb +7 -7
  70. data/lib/morpheus/cli/commands/tasks.rb +2 -2
  71. data/lib/morpheus/cli/commands/vdi_pools_command.rb +6 -6
  72. data/lib/morpheus/cli/commands/view.rb +5 -1
  73. data/lib/morpheus/cli/commands/whitelabel_settings_command.rb +4 -4
  74. data/lib/morpheus/cli/commands/whoami.rb +2 -2
  75. data/lib/morpheus/cli/credentials.rb +30 -8
  76. data/lib/morpheus/cli/dot_file.rb +8 -15
  77. data/lib/morpheus/cli/error_handler.rb +16 -0
  78. data/lib/morpheus/cli/errors.rb +8 -1
  79. data/lib/morpheus/cli/mixins/print_helper.rb +17 -13
  80. data/lib/morpheus/cli/mixins/rest_command.rb +18 -18
  81. data/lib/morpheus/cli/mixins/secondary_rest_command.rb +12 -12
  82. data/lib/morpheus/cli/option_parser.rb +5 -1
  83. data/lib/morpheus/cli/option_types.rb +59 -12
  84. data/lib/morpheus/cli/version.rb +1 -1
  85. data/lib/morpheus/cli.rb +26 -16
  86. data/lib/morpheus/ext/rest_client.rb +3 -2
  87. data/lib/morpheus/formatters.rb +1 -1
  88. data/lib/morpheus/logging.rb +4 -4
  89. data/lib/morpheus/morpkg.rb +4 -4
  90. data/lib/morpheus/rest_client.rb +2 -2
  91. data/lib/morpheus/routes.rb +2 -2
  92. data/lib/morpheus/terminal.rb +65 -16
  93. data/lib/morpheus.rb +1 -1
  94. data/morpheus-cli.gemspec +1 -0
  95. data/test/api/containers_interface_test.rb +68 -0
  96. data/test/api/doc_interface_test.rb +35 -0
  97. data/test/api/instances_interface_test.rb +22 -0
  98. data/test/api/whoami_interface_test.rb +14 -0
  99. data/test/cli/access_token_test.rb +36 -0
  100. data/test/cli/auth_test.rb +82 -0
  101. data/test/cli/cli_test.rb +48 -0
  102. data/test/cli/containers_test.rb +92 -0
  103. data/test/cli/doc_test.rb +35 -0
  104. data/test/cli/help_test.rb +25 -0
  105. data/test/cli/instances_test.rb +36 -0
  106. data/test/cli/man_test.rb +14 -0
  107. data/test/cli/remote_test.rb +89 -0
  108. data/test/cli/roles_test.rb +34 -0
  109. data/test/cli/shell_test.rb +81 -0
  110. data/test/cli/version_test.rb +23 -0
  111. data/test/cli/view_test.rb +55 -0
  112. data/test/cli/whoami_test.rb +17 -0
  113. data/test/morpheus_test.rb +16 -0
  114. data/test/test_case.rb +338 -0
  115. data/test/test_config.rb +137 -0
  116. data/test/test_data_helper.rb +97 -0
  117. metadata +61 -3
@@ -260,7 +260,7 @@ class Morpheus::Cli::BlueprintsCommand
260
260
  end
261
261
 
262
262
  def update(args)
263
- params, payload, options = {}, {}, {}
263
+ payload, options = {}, {}
264
264
  optparse = Morpheus::Cli::OptionParser.new do |opts|
265
265
  opts.banner = subcommand_usage("[blueprint] [options]")
266
266
  build_option_type_options(opts, options, update_blueprint_option_types(false))
@@ -283,7 +283,6 @@ class Morpheus::Cli::BlueprintsCommand
283
283
  blueprint = find_blueprint_by_name_or_id(args[0])
284
284
  return 1 if blueprint.nil?
285
285
  payload = {}
286
- passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
287
286
  if options[:payload]
288
287
  payload = options[:payload]
289
288
  payload.deep_merge!(parse_passed_options(options))
@@ -341,7 +340,7 @@ class Morpheus::Cli::BlueprintsCommand
341
340
  end
342
341
 
343
342
  def update_permissions(args)
344
- params, payload, options = {}, {}, {}
343
+ payload, options = {}, {}
345
344
  group_access_all = nil
346
345
  group_access_list = nil
347
346
  group_defaults_list = nil
@@ -453,7 +452,6 @@ class Morpheus::Cli::BlueprintsCommand
453
452
 
454
453
 
455
454
  def upload_image(args)
456
- image_type_name = nil
457
455
  options = {}
458
456
  optparse = Morpheus::Cli::OptionParser.new do |opts|
459
457
  opts.banner = subcommand_usage("[blueprint] [file]")
@@ -889,7 +887,7 @@ class Morpheus::Cli::BlueprintsCommand
889
887
  end
890
888
 
891
889
  def remove_instance_config(args)
892
- instance_index = nil
890
+ #instance_index = nil
893
891
  options = {}
894
892
  optparse = Morpheus::Cli::OptionParser.new do |opts|
895
893
  opts.banner = subcommand_usage("[blueprint] [tier] [instance] -g GROUP -c CLOUD")
@@ -1081,7 +1079,7 @@ class Morpheus::Cli::BlueprintsCommand
1081
1079
  end
1082
1080
 
1083
1081
  def remove_instance(args)
1084
- instance_index = nil
1082
+ #instance_index = nil
1085
1083
  options = {}
1086
1084
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1087
1085
  opts.banner = subcommand_usage("[blueprint] [tier] [instance]")
@@ -1402,7 +1400,6 @@ class Morpheus::Cli::BlueprintsCommand
1402
1400
  new_tier_name = v_prompt['name']
1403
1401
  end
1404
1402
  if new_tier_name && new_tier_name != tier_name
1405
- old_tier_name = tier_name
1406
1403
  if tiers[new_tier_name]
1407
1404
  print_red_alert "A tier named #{tier_name} already exists."
1408
1405
  return 1
@@ -1415,7 +1412,6 @@ class Morpheus::Cli::BlueprintsCommand
1415
1412
  v['linkedTiers'] = v['linkedTiers'].map {|it| it == tier_name ? new_tier_name : it }
1416
1413
  end
1417
1414
  end
1418
- # old_tier_name = tier_name
1419
1415
  tier_name = new_tier_name
1420
1416
  end
1421
1417
 
@@ -1577,15 +1573,9 @@ class Morpheus::Cli::BlueprintsCommand
1577
1573
  tiers = blueprint["config"]["tiers"]
1578
1574
 
1579
1575
  if !tiers || tiers.keys.size == 0
1580
- error_msg = "Blueprint #{blueprint['name']} has no tiers."
1581
- # print_red_alert "Blueprint #{blueprint['name']} has no tiers."
1582
- # raise_command_error "Blueprint #{blueprint['name']} has no tiers."
1583
- print_error Morpheus::Terminal.angry_prompt
1584
- puts_error "Blueprint #{blueprint['name']} has no tiers."
1585
- return 1
1576
+ raise_command_error "Blueprint #{blueprint['name']} has no tiers."
1586
1577
  end
1587
1578
 
1588
- connect_tiers = []
1589
1579
  tier1 = tiers[tier1_name]
1590
1580
  tier2 = tiers[tier2_name]
1591
1581
  # uhh support N args
@@ -1682,7 +1672,6 @@ class Morpheus::Cli::BlueprintsCommand
1682
1672
  return 1
1683
1673
  end
1684
1674
 
1685
- connect_tiers = []
1686
1675
  tier1 = tiers[tier1_name]
1687
1676
  tier2 = tiers[tier2_name]
1688
1677
  # uhh support N args
@@ -1859,7 +1848,7 @@ class Morpheus::Cli::BlueprintsCommand
1859
1848
  @available_blueprint_types = results['types'].collect {|it|
1860
1849
  {"name" => (it["name"] || it["code"]), "value" => (it["code"] || it["value"])}
1861
1850
  }
1862
- rescue RestClient::Exception => e
1851
+ rescue RestClient::Exception
1863
1852
  # older version
1864
1853
  @available_blueprint_types = [{"name" => "Morpheus", "value" => "morpheus"}, {"name" => "Terraform", "value" => "terraform"}, {"name" => "CloudFormation", "value" => "cloudFormation"}, {"name" => "ARM template", "value" => "arm"}]
1865
1854
  end
@@ -1935,7 +1924,7 @@ class Morpheus::Cli::BlueprintsCommand
1935
1924
  table_color = opts[:color] || cyan
1936
1925
  rows = blueprints.collect do |blueprint|
1937
1926
  #instance_type_names = (blueprint['instanceTypes'] || []).collect {|it| it['name'] }.join(', ')
1938
- instance_type_names = []
1927
+ #instance_type_names = []
1939
1928
  # if blueprint['config'] && blueprint['config']["tiers"]
1940
1929
  # blueprint['config']["tiers"]
1941
1930
  # end
@@ -2086,7 +2075,7 @@ class Morpheus::Cli::BlueprintsCommand
2086
2075
  end
2087
2076
  sorted_tiers = tiers.collect {|k,v| [k,v] }.sort {|a,b| a[1]['tierIndex'] <=> b[1]['tierIndex'] }
2088
2077
  sorted_tiers.each do |tier_obj|
2089
- tier_name = tier_obj[0]
2078
+ #tier_name = tier_obj[0]
2090
2079
  tier_config = tier_obj[1]
2091
2080
  if tier_config && tier_config['instances']
2092
2081
  tier_config['instances'].each_with_index do |instance_config, instance_index|
@@ -2145,7 +2134,7 @@ class Morpheus::Cli::BlueprintsCommand
2145
2134
  config_list.each do |config_obj|
2146
2135
  # puts " = #{config_obj[:scope].inspect}"
2147
2136
  config_scope = config_obj[:scope]
2148
- scoped_instance_config = config_obj[:config]
2137
+ #scoped_instance_config = config_obj[:config]
2149
2138
  config_description = ""
2150
2139
  config_items = []
2151
2140
  if config_scope[:environment]
@@ -2167,7 +2156,7 @@ class Morpheus::Cli::BlueprintsCommand
2167
2156
  print white," Instance has no configs, use `blueprints add-instance-config \"#{blueprint['name']}\" \"#{tier_name}\" \"#{instance_name.to_s.empty? ? instance_type_code : instance_name}\"`",reset,"\n"
2168
2157
  end
2169
2158
  rescue => err
2170
- #puts_error "Failed to parse instance scoped instance configs for blueprint #{blueprint['id']} #{blueprint['name']} Exception: #{err.class} #{err.message}"
2159
+ Morpheus::Logging::DarkPrinter.puts "Failed to parse instance config at index #{instance_index}. Exception: #{err.class} #{err.message}" if Morpheus::Logging.debug?
2171
2160
  end
2172
2161
  print "\n"
2173
2162
  #puts as_yaml(instance_config)
@@ -173,7 +173,7 @@ class Morpheus::Cli::BootScriptsCommand
173
173
  params = Morpheus::Cli::OptionTypes.prompt(my_options, options[:options], @api_client, options[:params])
174
174
  script_file = params.delete('file')
175
175
  if script_file
176
- if !File.exists?(script_file)
176
+ if !File.exist?(script_file)
177
177
  print_red_alert "File not found: #{script_file}"
178
178
  return 1
179
179
  end
@@ -244,7 +244,7 @@ class Morpheus::Cli::BootScriptsCommand
244
244
  # params = Morpheus::Cli::OptionTypes.prompt(my_options, options[:options], @api_client, options[:params])
245
245
  script_file = params.delete('file')
246
246
  if script_file
247
- if !File.exists?(script_file)
247
+ if !File.exist?(script_file)
248
248
  print_red_alert "File not found: #{script_file}"
249
249
  return 1
250
250
  end
@@ -25,7 +25,7 @@ class Morpheus::Cli::CatCommand
25
25
  arg_files = args
26
26
  arg_files.each do |arg_file|
27
27
  arg_file = File.expand_path(arg_file)
28
- if !File.exists?(arg_file)
28
+ if !File.exist?(arg_file)
29
29
  print_error Morpheus::Terminal.angry_prompt
30
30
  puts_error "#{command_name}: file not found: '#{arg_file}'"
31
31
  #print_red_alert "morpheus cat: file not found: '#{arg_file}'"
@@ -248,7 +248,7 @@ EOT
248
248
  logo_file = 'null' # clear it
249
249
  else
250
250
  filename = File.expand_path(filename)
251
- if !File.exists?(filename)
251
+ if !File.exist?(filename)
252
252
  raise_command_error "File not found: #{filename}"
253
253
  end
254
254
  logo_file = File.new(filename, 'rb')
@@ -261,7 +261,7 @@ EOT
261
261
  dark_logo_file = 'null' # clear it
262
262
  else
263
263
  filename = File.expand_path(filename)
264
- if !File.exists?(filename)
264
+ if !File.exist?(filename)
265
265
  raise_command_error "File not found: #{filename}"
266
266
  end
267
267
  dark_logo_file = File.new(filename, 'rb')
@@ -271,7 +271,7 @@ EOT
271
271
  options[:config_file] = val.to_s
272
272
  file_content = nil
273
273
  full_filename = File.expand_path(options[:config_file])
274
- if File.exists?(full_filename)
274
+ if File.exist?(full_filename)
275
275
  file_content = File.read(full_filename)
276
276
  else
277
277
  print_red_alert "File not found: #{full_filename}"
@@ -281,7 +281,7 @@ EOT
281
281
  config_map = parse_result[:data]
282
282
  if config_map.nil?
283
283
  # todo: bubble up JSON.parse error message
284
- raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:err]}"
284
+ raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:error]}"
285
285
  #raise_command_error "Failed to parse config as valid YAML or JSON."
286
286
  else
287
287
  params['config'] = config_map
@@ -345,7 +345,7 @@ EOT
345
345
  config_map = parse_result[:data]
346
346
  if config_map.nil?
347
347
  # todo: bubble up JSON.parse error message
348
- raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:err]}"
348
+ raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:error]}"
349
349
  #raise_command_error "Failed to parse config as valid YAML or JSON."
350
350
  else
351
351
  params['config'] = config_map
@@ -405,7 +405,7 @@ EOT
405
405
  logo_file = 'null' # clear it
406
406
  else
407
407
  filename = File.expand_path(filename)
408
- if !File.exists?(filename)
408
+ if !File.exist?(filename)
409
409
  raise_command_error "File not found: #{filename}"
410
410
  end
411
411
  logo_file = File.new(filename, 'rb')
@@ -418,7 +418,7 @@ EOT
418
418
  dark_logo_file = 'null' # clear it
419
419
  else
420
420
  filename = File.expand_path(filename)
421
- if !File.exists?(filename)
421
+ if !File.exist?(filename)
422
422
  raise_command_error "File not found: #{filename}"
423
423
  end
424
424
  dark_logo_file = File.new(filename, 'rb')
@@ -428,7 +428,7 @@ EOT
428
428
  options[:config_file] = val.to_s
429
429
  file_content = nil
430
430
  full_filename = File.expand_path(options[:config_file])
431
- if File.exists?(full_filename)
431
+ if File.exist?(full_filename)
432
432
  file_content = File.read(full_filename)
433
433
  else
434
434
  print_red_alert "File not found: #{full_filename}"
@@ -438,7 +438,7 @@ EOT
438
438
  config_map = parse_result[:data]
439
439
  if config_map.nil?
440
440
  # todo: bubble up JSON.parse error message
441
- raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:err]}"
441
+ raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:error]}"
442
442
  #raise_command_error "Failed to parse config as valid YAML or JSON."
443
443
  else
444
444
  params['config'] = config_map
@@ -495,7 +495,7 @@ EOT
495
495
  config_map = parse_result[:data]
496
496
  if config_map.nil?
497
497
  # todo: bubble up JSON.parse error message
498
- raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:err]}"
498
+ raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:error]}"
499
499
  #raise_command_error "Failed to parse config as valid YAML or JSON."
500
500
  else
501
501
  params['config'] = config_map
@@ -564,7 +564,7 @@ EOT
564
564
  logo_file = 'null' # clear it
565
565
  else
566
566
  filename = File.expand_path(filename)
567
- if !File.exists?(filename)
567
+ if !File.exist?(filename)
568
568
  raise_command_error "File not found: #{filename}"
569
569
  end
570
570
  logo_file = File.new(filename, 'rb')
@@ -603,7 +603,7 @@ EOT
603
603
  dark_logo_file = 'null' # clear it
604
604
  else
605
605
  filename = File.expand_path(filename)
606
- if !File.exists?(filename)
606
+ if !File.exist?(filename)
607
607
  raise_command_error "File not found: #{filename}"
608
608
  end
609
609
  dark_logo_file = File.new(filename, 'rb')
@@ -891,7 +891,7 @@ class Morpheus::Cli::Clouds
891
891
  build_option_type_options(opts, options, update_wiki_page_option_types)
892
892
  opts.on('--file FILE', "File containing the wiki content. This can be used instead of --content") do |filename|
893
893
  full_filename = File.expand_path(filename)
894
- if File.exists?(full_filename)
894
+ if File.exist?(full_filename)
895
895
  params['content'] = File.read(full_filename)
896
896
  else
897
897
  print_red_alert "File not found: #{full_filename}"
@@ -987,7 +987,7 @@ EOT
987
987
  logo_file = 'null' # clear it
988
988
  else
989
989
  filename = File.expand_path(filename)
990
- if !File.exists?(filename)
990
+ if !File.exist?(filename)
991
991
  print_red_alert "File not found: #{filename}"
992
992
  exit 1
993
993
  end
@@ -1037,7 +1037,7 @@ EOT
1037
1037
  dark_logo_file = 'null' # clear it
1038
1038
  else
1039
1039
  filename = File.expand_path(filename)
1040
- if !File.exists?(filename)
1040
+ if !File.exist?(filename)
1041
1041
  print_red_alert "File not found: #{filename}"
1042
1042
  exit 1
1043
1043
  end
@@ -23,6 +23,7 @@ class Morpheus::Cli::Clusters
23
23
  register_subcommands :update_permissions
24
24
  register_subcommands :api_config, :view_api_token, :view_kube_config
25
25
  register_subcommands :wiki, :update_wiki
26
+ register_subcommands :apply_template
26
27
 
27
28
  def connect(opts)
28
29
  @api_client = establish_remote_appliance_connection(opts)
@@ -42,6 +43,7 @@ class Morpheus::Cli::Clusters
42
43
  @user_groups_interface = @api_client.user_groups
43
44
  @accounts_interface = @api_client.accounts
44
45
  @logs_interface = @api_client.logs
46
+ @execution_request_interface = @api_client.execution_request
45
47
  #@active_security_group = ::Morpheus::Cli::SecurityGroups.load_security_group_file
46
48
  end
47
49
 
@@ -3242,7 +3244,7 @@ class Morpheus::Cli::Clusters
3242
3244
  build_option_type_options(opts, options, update_wiki_page_option_types)
3243
3245
  opts.on('--file FILE', "File containing the wiki content. This can be used instead of --content") do |filename|
3244
3246
  full_filename = File.expand_path(filename)
3245
- if File.exists?(full_filename)
3247
+ if File.exist?(full_filename)
3246
3248
  params['content'] = File.read(full_filename)
3247
3249
  else
3248
3250
  print_red_alert "File not found: #{full_filename}"
@@ -3562,6 +3564,125 @@ class Morpheus::Cli::Clusters
3562
3564
  end
3563
3565
  end
3564
3566
 
3567
+ def apply_template(args)
3568
+ options = {}
3569
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3570
+ opts.banner = subcommand_usage( "[cluster] --specTemplate --serviceUrl")
3571
+ opts.on("--specTemplate [TEXT]", String, "Name or ID of desired Spec Template to apply to cluster") do |val|
3572
+ options[:specTemplate] = val.to_s
3573
+ end
3574
+ opts.on("--serviceUrl [TEXT]", String, "Url of template to apply to Cluster") do |val|
3575
+ options[:serviceUrl] = val.to_s
3576
+ end
3577
+ opts.on("--specYaml [TEXT]", String, "Yaml to apply to Cluster") do |val|
3578
+ options[:specYaml] = val.to_s
3579
+ end
3580
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
3581
+ opts.footer = "Apply a Template to a Cluster.\n" +
3582
+ "[cluster] is required. This is the name or id of an existing cluster."
3583
+ end
3584
+
3585
+ optparse.parse!(args)
3586
+ if args.count != 1
3587
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
3588
+ end
3589
+ connect(options)
3590
+
3591
+ begin
3592
+ payload = nil
3593
+ cluster = nil
3594
+
3595
+ if options[:payload]
3596
+ payload = options[:payload]
3597
+ # support -O OPTION switch on top of --payload
3598
+ if options[:options]
3599
+ payload['cluster'] ||= {}
3600
+ payload['cluster'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
3601
+ end
3602
+
3603
+ if !payload['cluster'].empty?
3604
+ cluster = find_cluster_by_name_or_id(payload['cluster']['id'] || payload['cluster']['name'])
3605
+ end
3606
+ else
3607
+ cluster = find_cluster_by_name_or_id(args[0])
3608
+ cluster_payload = {}
3609
+ cluster_payload['specTemplate'] = options[:specTemplate] if !options[:specTemplate].empty?
3610
+ cluster_payload['serviceUrl'] = options[:serviceUrl] if !options[:serviceUrl].empty?
3611
+ cluster_payload['specYaml'] = options[:specYaml] if !options[:specYaml].empty?
3612
+ payload = cluster_payload
3613
+ end
3614
+
3615
+ if !cluster
3616
+ print_red_alert "No clusters available for update"
3617
+ exit 1
3618
+ end
3619
+
3620
+ if cluster_payload.empty?
3621
+ type = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'type' => 'select', 'fieldLabel' => "Type", 'selectOptions' => apply_temp_options, 'required' => true, 'description' => 'Choose type of template being used.'}])['type']
3622
+ if type == 'specTemplate'
3623
+ cluster_payload['specTemplate'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'specTemplate', 'type' => 'select', 'fieldLabel' => "Spec Template", 'selectOptions' => available_kube_templates, 'required' => true, 'description' => 'Choose a template.'}], options[:options])['specTemplate']
3624
+ elsif type == 'yaml'
3625
+ file_params = Morpheus::Cli::OptionTypes.file_content_prompt({'fieldName' => 'source', 'fieldLabel' => 'File Content', 'type' => 'file-content', 'required' => true}, {'source' => {'source' => 'local'}}, nil, {})
3626
+ cluster_payload['specYaml'] = file_params['content']
3627
+ else
3628
+ cluster_payload['specUrl'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'specUrl', 'type' => 'text', 'fieldLabel' => 'Spec Url', 'required' => true, 'description' => 'Url of template.'}])['specUrl']
3629
+ end
3630
+ end
3631
+
3632
+ if options[:dry_run]
3633
+ print_dry_run @clusters_interface.dry.apply_template(cluster['id'], cluster_payload)
3634
+ return
3635
+ end
3636
+
3637
+ json_response = @clusters_interface.apply_template(cluster['id'], cluster_payload)
3638
+ if options[:json]
3639
+ print JSON.pretty_generate(json_response)
3640
+ print "\n"
3641
+ elsif json_response['msg'] != nil
3642
+ print_red_alert "There was an error #{json_response['msg']}"
3643
+ else
3644
+ print_green_success 'Template applied to Cluster. Check Execution Request for results'
3645
+ json_response = @execution_request_interface.get(json_response['executionId'], {})
3646
+
3647
+ if json_response['executionRequest'] && json_response['executionRequest']['errorMessage']
3648
+ print_red_alert "There was an error: #{json_response['executionRequest']['errorMessage']}"
3649
+ print_red_alert "execution request id: #{json_response['executionRequest']['uniqueId']}"
3650
+ else
3651
+ execution_request = json_response['executionRequest']
3652
+ print_h1 "Execution Request Details"
3653
+ print cyan
3654
+ description_cols = {
3655
+ #"ID" => lambda {|it| it['id'] },
3656
+ "Unique ID" => lambda {|it| it['uniqueId'] },
3657
+ "Server ID" => lambda {|it| it['serverId'] },
3658
+ "Instance ID" => lambda {|it| it['instanceId'] },
3659
+ "Container ID" => lambda {|it| it['containerId'] },
3660
+ "Expires At" => lambda {|it| format_local_dt it['expiresAt'] },
3661
+ "Exit Code" => lambda {|it| it['exitCode'] },
3662
+ "Status" => lambda {|it| format_execution_request_status(it) },
3663
+ #"Created By" => lambda {|it| it['createdById'] },
3664
+ #"Subdomain" => lambda {|it| it['subdomain'] },
3665
+ }
3666
+ description_cols.delete("Server ID") if execution_request['serverId'].nil?
3667
+ description_cols.delete("Instance ID") if execution_request['instanceId'].nil?
3668
+ description_cols.delete("Container ID") if execution_request['containerId'].nil?
3669
+ description_cols.delete("Exit Code") if execution_request['exitCode'].nil?
3670
+ print_description_list(description_cols, execution_request)
3671
+
3672
+ if execution_request['stdErr'].to_s.strip != '' && execution_request['stdErr'] != "stdin: is not a tty\n"
3673
+ print_h2 "Error"
3674
+ puts execution_request['stdErr'].to_s.strip
3675
+ end
3676
+ if execution_request['stdOut']
3677
+ print_h2 "Output"
3678
+ puts execution_request['stdOut'].to_s.strip
3679
+ end
3680
+ print reset, "\n"
3681
+ end
3682
+ end
3683
+ end
3684
+ end
3685
+
3565
3686
  def history_event_details(args)
3566
3687
  options = {}
3567
3688
  process_event_id = nil
@@ -4057,7 +4178,7 @@ class Morpheus::Cli::Clusters
4057
4178
  end
4058
4179
  opts.on('--volumes-file FILE', String, "Volumes Config from a local JSON or YAML file") do |val|
4059
4180
  config_file = File.expand_path(val)
4060
- if !File.exists?(config_file) || !File.file?(config_file)
4181
+ if !File.exist?(config_file) || !File.file?(config_file)
4061
4182
  print_red_alert "Specified volumes file not found: #{config_file}"
4062
4183
  exit 1
4063
4184
  end
@@ -4093,7 +4214,7 @@ class Morpheus::Cli::Clusters
4093
4214
  end
4094
4215
  opts.on('--network-interfaces-file FILE', String, "Network Interfaces Config from a local JSON or YAML file") do |val|
4095
4216
  config_file = File.expand_path(val)
4096
- if !File.exists?(config_file) || !File.file?(config_file)
4217
+ if !File.exist?(config_file) || !File.file?(config_file)
4097
4218
  print_red_alert "Specified network interfaces file not found: #{config_file}"
4098
4219
  exit 1
4099
4220
  end
@@ -4235,4 +4356,34 @@ class Morpheus::Cli::Clusters
4235
4356
  it
4236
4357
  end
4237
4358
  end
4359
+
4360
+ def available_kube_templates
4361
+ option_results = options_interface.options_for_source('availableKubeTemplates')
4362
+ available_templates = option_results['data'].collect {|it|
4363
+ {"id" => it["value"], "name" => it["name"], "value" => it["value"]}
4364
+ }
4365
+
4366
+ return available_templates
4367
+ end
4368
+
4369
+ def apply_temp_options
4370
+ [
4371
+ {"id" => "specYaml", "name" => "YAML", "value" => "yaml"},
4372
+ {"id" => 'specTemplate', "name" => "Spec Template", "value" => 'specTemplate'},
4373
+ {"id" => 'url', "name" => 'Url of Template', "value" => 'url'}
4374
+ ]
4375
+ end
4376
+
4377
+ def format_execution_request_status(execution_request, return_color=cyan)
4378
+ out = ""
4379
+ status_str = execution_request['status']
4380
+ if status_str == 'complete'
4381
+ out << "#{green}#{status_str.upcase}#{return_color}"
4382
+ elsif status_str == 'failed' || status_str == 'expired'
4383
+ out << "#{red}#{status_str.upcase}#{return_color}"
4384
+ else
4385
+ out << "#{cyan}#{status_str.upcase}#{return_color}"
4386
+ end
4387
+ out
4388
+ end
4238
4389
  end