morpheus-cli 5.5.2.1 → 5.5.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 +4 -4
- data/.gitignore +2 -1
- data/Dockerfile +1 -1
- data/README.md +57 -4
- data/Rakefile +9 -0
- data/bin/morpheus +4 -4
- data/lib/morpheus/api/api_client.rb +8 -2
- data/lib/morpheus/api/archive_buckets_interface.rb +1 -1
- data/lib/morpheus/api/archive_files_interface.rb +3 -3
- data/lib/morpheus/api/clients_interface.rb +2 -2
- data/lib/morpheus/api/clusters_interface.rb +8 -1
- data/lib/morpheus/api/containers_interface.rb +29 -16
- data/lib/morpheus/api/custom_instance_types_interface.rb +0 -2
- data/lib/morpheus/api/doc_interface.rb +8 -6
- data/lib/morpheus/api/file_copy_request_interface.rb +1 -1
- data/lib/morpheus/api/health_interface.rb +1 -1
- data/lib/morpheus/api/image_builder_interface.rb +3 -3
- data/lib/morpheus/api/instances_interface.rb +25 -0
- data/lib/morpheus/api/logs_interface.rb +2 -4
- data/lib/morpheus/api/monitoring_interface.rb +6 -6
- data/lib/morpheus/api/packages_interface.rb +1 -1
- data/lib/morpheus/api/reports_interface.rb +1 -1
- data/lib/morpheus/api/servers_interface.rb +9 -1
- data/lib/morpheus/api/storage_providers_interface.rb +2 -2
- data/lib/morpheus/api/virtual_images_interface.rb +1 -1
- data/lib/morpheus/api.rb +2 -0
- data/lib/morpheus/benchmarking.rb +1 -1
- data/lib/morpheus/cli/cli_command.rb +69 -36
- data/lib/morpheus/cli/cli_registry.rb +19 -10
- data/lib/morpheus/cli/commands/access_token_command.rb +1 -1
- data/lib/morpheus/cli/commands/apps.rb +1 -1
- data/lib/morpheus/cli/commands/archives_command.rb +25 -33
- data/lib/morpheus/cli/commands/blueprints_command.rb +10 -21
- data/lib/morpheus/cli/commands/boot_scripts_command.rb +2 -2
- data/lib/morpheus/cli/commands/cat_command.rb +1 -1
- data/lib/morpheus/cli/commands/catalog_item_types_command.rb +12 -12
- data/lib/morpheus/cli/commands/clouds.rb +3 -3
- data/lib/morpheus/cli/commands/clusters.rb +154 -3
- data/lib/morpheus/cli/commands/containers_command.rb +398 -253
- data/lib/morpheus/cli/commands/deployments.rb +1 -1
- data/lib/morpheus/cli/commands/deploys.rb +9 -9
- data/lib/morpheus/cli/commands/doc.rb +15 -16
- data/lib/morpheus/cli/commands/execution_request_command.rb +2 -2
- data/lib/morpheus/cli/commands/file_copy_request_command.rb +5 -5
- data/lib/morpheus/cli/commands/groups.rb +2 -2
- data/lib/morpheus/cli/commands/health_command.rb +4 -4
- data/lib/morpheus/cli/commands/hosts.rb +43 -5
- data/lib/morpheus/cli/commands/image_builder_command.rb +1 -1
- data/lib/morpheus/cli/commands/instances.rb +419 -148
- data/lib/morpheus/cli/commands/integrations_command.rb +22 -20
- data/lib/morpheus/cli/commands/key_pairs.rb +2 -2
- data/lib/morpheus/cli/commands/library_container_scripts_command.rb +2 -2
- data/lib/morpheus/cli/commands/library_container_templates_command.rb +2 -2
- data/lib/morpheus/cli/commands/library_instance_types_command.rb +3 -3
- data/lib/morpheus/cli/commands/library_spec_templates_command.rb +2 -2
- data/lib/morpheus/cli/commands/login.rb +1 -1
- data/lib/morpheus/cli/commands/man_command.rb +32 -18
- data/lib/morpheus/cli/commands/packages_command.rb +11 -11
- data/lib/morpheus/cli/commands/plugins.rb +1 -1
- data/lib/morpheus/cli/commands/policies_command.rb +4 -4
- data/lib/morpheus/cli/commands/preseed_scripts_command.rb +2 -2
- data/lib/morpheus/cli/commands/remote.rb +1 -1
- data/lib/morpheus/cli/commands/reports_command.rb +3 -3
- data/lib/morpheus/cli/commands/roles.rb +6 -3
- data/lib/morpheus/cli/commands/security_groups.rb +1 -1
- data/lib/morpheus/cli/commands/shell.rb +40 -62
- data/lib/morpheus/cli/commands/snapshots.rb +3 -5
- data/lib/morpheus/cli/commands/source_command.rb +8 -16
- data/lib/morpheus/cli/commands/storage_providers_command.rb +7 -7
- data/lib/morpheus/cli/commands/tasks.rb +2 -2
- data/lib/morpheus/cli/commands/vdi_pools_command.rb +6 -6
- data/lib/morpheus/cli/commands/view.rb +5 -1
- data/lib/morpheus/cli/commands/whitelabel_settings_command.rb +4 -4
- data/lib/morpheus/cli/commands/whoami.rb +2 -2
- data/lib/morpheus/cli/credentials.rb +30 -8
- data/lib/morpheus/cli/dot_file.rb +8 -15
- data/lib/morpheus/cli/error_handler.rb +16 -0
- data/lib/morpheus/cli/errors.rb +8 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +17 -13
- data/lib/morpheus/cli/mixins/rest_command.rb +18 -18
- data/lib/morpheus/cli/mixins/secondary_rest_command.rb +12 -12
- data/lib/morpheus/cli/option_parser.rb +5 -1
- data/lib/morpheus/cli/option_types.rb +59 -12
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli.rb +26 -16
- data/lib/morpheus/ext/rest_client.rb +3 -2
- data/lib/morpheus/formatters.rb +1 -1
- data/lib/morpheus/logging.rb +4 -4
- data/lib/morpheus/morpkg.rb +4 -4
- data/lib/morpheus/rest_client.rb +2 -2
- data/lib/morpheus/routes.rb +2 -2
- data/lib/morpheus/terminal.rb +65 -16
- data/lib/morpheus.rb +1 -1
- data/morpheus-cli.gemspec +1 -0
- data/test/api/containers_interface_test.rb +68 -0
- data/test/api/doc_interface_test.rb +35 -0
- data/test/api/instances_interface_test.rb +22 -0
- data/test/api/whoami_interface_test.rb +14 -0
- data/test/cli/access_token_test.rb +36 -0
- data/test/cli/auth_test.rb +82 -0
- data/test/cli/cli_test.rb +48 -0
- data/test/cli/containers_test.rb +92 -0
- data/test/cli/doc_test.rb +35 -0
- data/test/cli/help_test.rb +25 -0
- data/test/cli/instances_test.rb +36 -0
- data/test/cli/man_test.rb +14 -0
- data/test/cli/remote_test.rb +89 -0
- data/test/cli/roles_test.rb +34 -0
- data/test/cli/shell_test.rb +81 -0
- data/test/cli/version_test.rb +23 -0
- data/test/cli/view_test.rb +55 -0
- data/test/cli/whoami_test.rb +17 -0
- data/test/morpheus_test.rb +16 -0
- data/test/test_case.rb +338 -0
- data/test/test_config.rb +137 -0
- data/test/test_data_helper.rb +97 -0
- metadata +61 -3
|
@@ -260,7 +260,7 @@ class Morpheus::Cli::BlueprintsCommand
|
|
|
260
260
|
end
|
|
261
261
|
|
|
262
262
|
def update(args)
|
|
263
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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[:
|
|
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[:
|
|
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.
|
|
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.
|
|
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.
|
|
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[:
|
|
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[:
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|