morpheus-cli 5.4.4.2 → 5.4.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/apps_interface.rb +18 -4
- data/lib/morpheus/api/instances_interface.rb +16 -2
- data/lib/morpheus/cli/commands/apps.rb +366 -47
- data/lib/morpheus/cli/commands/clusters.rb +70 -10
- data/lib/morpheus/cli/commands/dashboard_command.rb +1 -1
- data/lib/morpheus/cli/commands/execution_request_command.rb +9 -4
- data/lib/morpheus/cli/commands/file_copy_request_command.rb +2 -1
- data/lib/morpheus/cli/commands/instances.rb +297 -14
- data/lib/morpheus/cli/commands/library_layouts_command.rb +15 -0
- data/lib/morpheus/cli/commands/provisioning_licenses_command.rb +1 -1
- data/lib/morpheus/cli/mixins/execution_request_helper.rb +50 -0
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +4 -4
- data/lib/morpheus/cli/option_types.rb +7 -1
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/formatters.rb +12 -0
- metadata +3 -2
@@ -373,8 +373,15 @@ class Morpheus::Cli::Clusters
|
|
373
373
|
opts.on( '--resource-name NAME', "Resource Name" ) do |val|
|
374
374
|
options[:resourceName] = val.to_s
|
375
375
|
end
|
376
|
-
opts.on('--tags LIST', String, "
|
377
|
-
options[:
|
376
|
+
opts.on('--tags LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
|
377
|
+
options[:metadata] = val
|
378
|
+
end
|
379
|
+
opts.on('--metadata LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
|
380
|
+
options[:metadata] = val
|
381
|
+
end
|
382
|
+
opts.add_hidden_option('--metadata')
|
383
|
+
opts.on('--labels LIST', String, "Tags") do |val|
|
384
|
+
options[:labels] = val
|
378
385
|
end
|
379
386
|
opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
|
380
387
|
options[:group] = val
|
@@ -510,13 +517,13 @@ class Morpheus::Cli::Clusters
|
|
510
517
|
description = options[:description] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'type' => 'text', 'fieldLabel' => 'Description', 'required' => false, 'description' => 'Resource Description.'}],options[:options],@api_client,{})['description']
|
511
518
|
cluster_payload['description'] = description if description
|
512
519
|
|
513
|
-
|
520
|
+
labels = options[:labels]
|
514
521
|
|
515
|
-
if !
|
516
|
-
|
522
|
+
if !labels && !options[:no_prompt]
|
523
|
+
labels = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'labels', 'type' => 'text', 'fieldLabel' => 'Resource Labels', 'required' => false, 'description' => 'Resource Labels.'}],options[:options],@api_client,{})['labels']
|
517
524
|
end
|
518
525
|
|
519
|
-
server_payload['
|
526
|
+
server_payload['labels'] = labels if labels
|
520
527
|
|
521
528
|
# Cloud / Zone
|
522
529
|
cloud_id = nil
|
@@ -595,7 +602,7 @@ class Morpheus::Cli::Clusters
|
|
595
602
|
|
596
603
|
# Multi-disk / prompt for volumes
|
597
604
|
if provision_type['hasVolumes']
|
598
|
-
volumes = options[:volumes] || prompt_volumes(service_plan, provision_type, options
|
605
|
+
volumes = options[:volumes] || prompt_volumes(service_plan, provision_type, options, @api_client, api_params)
|
599
606
|
if !volumes.empty?
|
600
607
|
server_payload['volumes'] = volumes
|
601
608
|
end
|
@@ -605,7 +612,13 @@ class Morpheus::Cli::Clusters
|
|
605
612
|
option_type_list =
|
606
613
|
((controller_type.nil? ? [] : controller_type['optionTypes'].reject { |type| !type['enabled'] || type['fieldComponent'] } rescue []) +
|
607
614
|
layout['optionTypes'] +
|
608
|
-
(cluster_type['optionTypes'].reject { |type|
|
615
|
+
(cluster_type['optionTypes'].reject { |type|
|
616
|
+
!type['enabled'] || !type['creatable'] || type['fieldComponent']
|
617
|
+
} rescue []))
|
618
|
+
|
619
|
+
# remove metadata option_type , prompt manually for that field 'tags' instead of 'metadata'
|
620
|
+
metadata_option_type = option_type_list.find {|type| type['fieldName'] == 'metadata' }
|
621
|
+
option_type_list = option_type_list.reject {|type| type['fieldName'] == 'metadata' }
|
609
622
|
|
610
623
|
# KLUDGE: google zone required for network selection
|
611
624
|
if option_type = option_type_list.find {|type| type['code'] == 'computeServerType.googleLinux.googleZoneId'}
|
@@ -633,10 +646,23 @@ class Morpheus::Cli::Clusters
|
|
633
646
|
# Server options
|
634
647
|
server_payload.deep_merge!(Morpheus::Cli::OptionTypes.prompt(option_type_list, options[:options].deep_merge({:context_map => {'domain' => ''}}), @api_client, api_params, options[:no_prompt], true))
|
635
648
|
|
649
|
+
# Metadata Tags
|
650
|
+
if metadata_option_type
|
651
|
+
if options[:metadata]
|
652
|
+
metadata = parse_metadata(options[:metadata])
|
653
|
+
server_payload['tags'] = metadata if !metadata.empty?
|
654
|
+
else
|
655
|
+
metadata = prompt_metadata(options)
|
656
|
+
server_payload['tags'] = metadata if !metadata.empty?
|
657
|
+
end
|
658
|
+
end
|
659
|
+
|
636
660
|
# Worker count
|
637
661
|
if !['manual', 'external'].include?(provision_type['code'])
|
638
662
|
default_node_count = layout['computeServers'] ? (layout['computeServers'].find {|it| it['nodeType'] == 'worker'} || {'nodeCount' => 3})['nodeCount'] : 3
|
639
|
-
|
663
|
+
nodeCount = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => "config.nodeCount", 'type' => 'number', 'fieldLabel' => "#{['docker-cluster', 'kvm-cluster'].include?(cluster_type['code']) ? 'Host' : 'Worker'} Count", 'required' => true, 'defaultValue' => default_node_count > 0 ? default_node_count : 3}], options[:options], @api_client, api_params, options[:no_prompt])['config']['nodeCount']
|
664
|
+
server_payload['config']['nodeCount'] = nodeCount
|
665
|
+
server_payload['nodeCount'] = nodeCount
|
640
666
|
end
|
641
667
|
|
642
668
|
# Create User
|
@@ -1092,6 +1118,16 @@ class Morpheus::Cli::Clusters
|
|
1092
1118
|
opts.on("--description [TEXT]", String, "Description") do |val|
|
1093
1119
|
options[:description] = val.to_s
|
1094
1120
|
end
|
1121
|
+
opts.on('--tags LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
|
1122
|
+
options[:metadata] = val
|
1123
|
+
end
|
1124
|
+
opts.on('--metadata LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
|
1125
|
+
options[:metadata] = val
|
1126
|
+
end
|
1127
|
+
opts.add_hidden_option('--metadata')
|
1128
|
+
opts.on('--labels LIST', String, "Tags") do |val|
|
1129
|
+
options[:labels] = val
|
1130
|
+
end
|
1095
1131
|
add_server_options(opts, options)
|
1096
1132
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
1097
1133
|
opts.footer = "Add worker to a cluster.\n" +
|
@@ -1153,6 +1189,14 @@ class Morpheus::Cli::Clusters
|
|
1153
1189
|
# Description
|
1154
1190
|
server_payload['description'] = options[:description] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'description' => 'Worker Description'}], options[:options], @api_client)['description']
|
1155
1191
|
|
1192
|
+
# Labels
|
1193
|
+
labels = options[:labels]
|
1194
|
+
if !labels && !options[:no_prompt]
|
1195
|
+
labels = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'labels', 'type' => 'text', 'fieldLabel' => 'Resource Labels', 'required' => false, 'description' => 'Resource Labels.'}],options[:options],@api_client,{})['labels']
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
server_payload['labels'] = labels if labels
|
1199
|
+
|
1156
1200
|
# Cloud
|
1157
1201
|
available_clouds = options_interface.options_for_source('clouds', {groupId: cluster['site']['id'], clusterId: cluster['id'], ownerOnly: true})['data']
|
1158
1202
|
cloud_id = nil
|
@@ -1192,7 +1236,7 @@ class Morpheus::Cli::Clusters
|
|
1192
1236
|
end
|
1193
1237
|
|
1194
1238
|
# Multi-disk / prompt for volumes
|
1195
|
-
volumes = options[:volumes] || prompt_volumes(service_plan, provision_type, options
|
1239
|
+
volumes = options[:volumes] || prompt_volumes(service_plan, provision_type, options, @api_client, {zoneId: cloud['id'], siteId: group['id']})
|
1196
1240
|
|
1197
1241
|
if !volumes.empty?
|
1198
1242
|
server_payload['volumes'] = volumes
|
@@ -1218,8 +1262,24 @@ class Morpheus::Cli::Clusters
|
|
1218
1262
|
(type['fieldContext'] == 'instance.networkDomain' && type['fieldName'] == 'id')
|
1219
1263
|
} rescue [])
|
1220
1264
|
|
1265
|
+
# remove metadata option_type , prompt manually for that field 'tags' instead of 'metadata'
|
1266
|
+
#metadata_option_type = option_type_list.find {|type| type['fieldName'] == 'metadata' }
|
1267
|
+
metadata_option_type = cluster_type['optionTypes'].find {|type| type['fieldName'] == 'metadata' }
|
1268
|
+
option_type_list = option_type_list.reject {|type| type['fieldName'] == 'metadata' }
|
1269
|
+
|
1221
1270
|
server_payload.deep_merge!(Morpheus::Cli::OptionTypes.prompt(option_type_list, options[:options], @api_client, {zoneId: cloud['id'], siteId: group['id'], layoutId: layout['id']}))
|
1222
1271
|
|
1272
|
+
# Metadata Tags
|
1273
|
+
if metadata_option_type
|
1274
|
+
if options[:metadata]
|
1275
|
+
metadata = parse_metadata(options[:metadata])
|
1276
|
+
server_payload['tags'] = metadata if !metadata.empty?
|
1277
|
+
else
|
1278
|
+
metadata = prompt_metadata(options)
|
1279
|
+
server_payload['tags'] = metadata if !metadata.empty?
|
1280
|
+
end
|
1281
|
+
end
|
1282
|
+
|
1223
1283
|
# Create User
|
1224
1284
|
if !options[:createUser].nil?
|
1225
1285
|
server_payload['config']['createUser'] = options[:createUser]
|
@@ -135,7 +135,7 @@ This includes instance and backup counts, favorite instances, monitoring and rec
|
|
135
135
|
print as_pretty_table([json_response], monitoring_column_definitions.upcase_keys!, options)
|
136
136
|
|
137
137
|
|
138
|
-
if json_response['logStats']
|
138
|
+
if json_response['logStats'] && json_response['logStats']['data']
|
139
139
|
# todo: should come from monitoring.startMs-endMs
|
140
140
|
log_period_display = "7 Days"
|
141
141
|
print_h2 "Logs (#{log_period_display})", options
|
@@ -83,12 +83,13 @@ class Morpheus::Cli::ExecutionRequestCommand
|
|
83
83
|
if options[:refresh_interval].nil? || options[:refresh_interval].to_f < 0
|
84
84
|
options[:refresh_interval] = default_refresh_interval
|
85
85
|
end
|
86
|
-
if execution_request['
|
86
|
+
if !(execution_request['status'] == 'pending' || execution_request['status'] == 'new')
|
87
87
|
# it is finished
|
88
88
|
else
|
89
89
|
print cyan
|
90
|
-
|
91
|
-
|
90
|
+
refresh_display_seconds = options[:refresh_interval] % 1.0 == 0 ? options[:refresh_interval].to_i : options[:refresh_interval]
|
91
|
+
print "Execution request has not yet finished. Refreshing every #{refresh_display_seconds} seconds"
|
92
|
+
while execution_request['status'] == 'pending' || execution_request['status'] == 'new' do
|
92
93
|
sleep(options[:refresh_interval])
|
93
94
|
print cyan,".",reset
|
94
95
|
json_response = @execution_request_interface.get(execution_request_id, params)
|
@@ -114,9 +115,13 @@ class Morpheus::Cli::ExecutionRequestCommand
|
|
114
115
|
#"Created By" => lambda {|it| it['createdById'] },
|
115
116
|
#"Subdomain" => lambda {|it| it['subdomain'] },
|
116
117
|
}
|
118
|
+
description_cols.delete("Server ID") if execution_request['serverId'].nil?
|
119
|
+
description_cols.delete("Instance ID") if execution_request['instanceId'].nil?
|
120
|
+
description_cols.delete("Container ID") if execution_request['containerId'].nil?
|
121
|
+
description_cols.delete("Exit Code") if execution_request['exitCode'].nil?
|
117
122
|
print_description_list(description_cols, execution_request)
|
118
123
|
|
119
|
-
if execution_request['stdErr'] && execution_request['stdErr'] != "stdin: is not a tty\n"
|
124
|
+
if execution_request['stdErr'].to_s.strip != '' && execution_request['stdErr'] != "stdin: is not a tty\n"
|
120
125
|
print_h2 "Error"
|
121
126
|
puts execution_request['stdErr'].to_s.strip
|
122
127
|
end
|
@@ -80,7 +80,8 @@ class Morpheus::Cli::FileCopyRequestCommand
|
|
80
80
|
# it is finished
|
81
81
|
else
|
82
82
|
print cyan
|
83
|
-
|
83
|
+
refresh_display_seconds = options[:refresh_interval] % 1.0 == 0 ? options[:refresh_interval].to_i : options[:refresh_interval]
|
84
|
+
print "File copy request has not yet finished. Refreshing every #{refresh_display_seconds} seconds"
|
84
85
|
while !['complete','failed','expired'].include?(file_copy_request['status']) do
|
85
86
|
sleep(options[:refresh_interval])
|
86
87
|
print cyan,".",reset
|
@@ -8,6 +8,7 @@ class Morpheus::Cli::Instances
|
|
8
8
|
include Morpheus::Cli::DeploymentsHelper
|
9
9
|
include Morpheus::Cli::ProcessesHelper
|
10
10
|
include Morpheus::Cli::LogsHelper
|
11
|
+
include Morpheus::Cli::ExecutionRequestHelper
|
11
12
|
|
12
13
|
set_command_name :instances
|
13
14
|
set_command_description "View and manage instances."
|
@@ -24,12 +25,15 @@ class Morpheus::Cli::Instances
|
|
24
25
|
:wiki, :update_wiki,
|
25
26
|
{:exec => :execution_request},
|
26
27
|
:deploys,
|
27
|
-
:refresh, :apply
|
28
|
+
:refresh, :prepare_apply, :plan, :apply, :state
|
28
29
|
#register_subcommands :firewall_disable, :firewall_enable
|
29
30
|
# register_subcommands {:'lb-update' => :load_balancer_update}
|
30
31
|
alias_subcommand :details, :get
|
31
32
|
set_default_subcommand :list
|
32
33
|
|
34
|
+
# hide these for now
|
35
|
+
set_subcommands_hidden :prepare_apply
|
36
|
+
|
33
37
|
def initialize()
|
34
38
|
#@appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
35
39
|
end
|
@@ -4549,19 +4553,22 @@ EOT
|
|
4549
4553
|
end
|
4550
4554
|
end
|
4551
4555
|
|
4552
|
-
def
|
4556
|
+
def prepare_apply(args)
|
4553
4557
|
params, payload, options = {}, {}, {}
|
4554
4558
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
4555
4559
|
opts.banner = subcommand_usage("[instance] [options]")
|
4556
4560
|
build_standard_update_options(opts, options, [:auto_confirm])
|
4557
4561
|
opts.footer = <<-EOT
|
4558
|
-
|
4562
|
+
Prepare to apply an instance.
|
4559
4563
|
[instance] is required. This is the name or id of an instance.
|
4564
|
+
Displays the current configuration data used by the apply command.
|
4560
4565
|
This is only supported by certain types of instances such as terraform.
|
4561
4566
|
EOT
|
4562
4567
|
end
|
4563
4568
|
optparse.parse!(args)
|
4564
|
-
|
4569
|
+
if args.count != 1
|
4570
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(', ')}\n#{optparse}"
|
4571
|
+
end
|
4565
4572
|
connect(options)
|
4566
4573
|
|
4567
4574
|
begin
|
@@ -4577,10 +4584,131 @@ EOT
|
|
4577
4584
|
payload.deep_merge!(parse_passed_options(options))
|
4578
4585
|
# raise_command_error "Specify at least one option to update.\n#{optparse}" if payload.empty?
|
4579
4586
|
end
|
4580
|
-
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to apply this instance: #{instance['name']}?")
|
4581
|
-
return 9, "aborted command"
|
4582
|
-
end
|
4583
4587
|
@instances_interface.setopts(options)
|
4588
|
+
if options[:dry_run]
|
4589
|
+
print_dry_run @instances_interface.dry.prepare_apply(instance["id"], params)
|
4590
|
+
return
|
4591
|
+
end
|
4592
|
+
json_response = @instances_interface.prepare_apply(instance["id"], params)
|
4593
|
+
render_result = render_with_format(json_response, options)
|
4594
|
+
return 0 if render_result
|
4595
|
+
# print_green_success "Prepared to apply instance: #{instance['name']}"
|
4596
|
+
print_h1 "Prepared Instance: #{instance['name']}"
|
4597
|
+
instance_config = json_response['data']
|
4598
|
+
# instance_config = json_response if instance_config.nil?
|
4599
|
+
puts as_yaml(instance_config, options)
|
4600
|
+
#return get([app['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
|
4601
|
+
print "\n", reset
|
4602
|
+
return 0
|
4603
|
+
rescue RestClient::Exception => e
|
4604
|
+
print_rest_exception(e, options)
|
4605
|
+
exit 1
|
4606
|
+
end
|
4607
|
+
end
|
4608
|
+
|
4609
|
+
def apply(args)
|
4610
|
+
default_refresh_interval = 15
|
4611
|
+
params, payload, options = {}, {}, {}
|
4612
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
4613
|
+
opts.banner = subcommand_usage("[instance] [options]")
|
4614
|
+
opts.on( '-p', '--parameter NAME=VALUE', "Template parameter name and value" ) do |val|
|
4615
|
+
k, v = val.split("=")
|
4616
|
+
options[:options]['templateParameter'] ||= {}
|
4617
|
+
options[:options]['templateParameter'][k] = v
|
4618
|
+
end
|
4619
|
+
opts.on('--refresh [SECONDS]', String, "Refresh until execution is complete. Default interval is #{default_refresh_interval} seconds.") do |val|
|
4620
|
+
options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
|
4621
|
+
end
|
4622
|
+
opts.on(nil, '--no-refresh', "Do not refresh" ) do
|
4623
|
+
options[:no_refresh] = true
|
4624
|
+
end
|
4625
|
+
opts.on(nil, '--no-validate', "Do not validate planned changes before apply" ) do
|
4626
|
+
options[:no_validate] = true
|
4627
|
+
end
|
4628
|
+
opts.on(nil, '--validate-only', "Only validate planned changes, do not execute the apply command." ) do
|
4629
|
+
options[:validate_only] = true
|
4630
|
+
end
|
4631
|
+
build_standard_update_options(opts, options, [:auto_confirm])
|
4632
|
+
opts.footer = <<-EOT
|
4633
|
+
Apply an instance.
|
4634
|
+
[instance] is required. This is the name or id of an instance.
|
4635
|
+
This is only supported by certain types of instances such as terraform.
|
4636
|
+
By default this executes two requests to validate and then apply the changes.
|
4637
|
+
The first request corresponds to the terraform plan command only.
|
4638
|
+
Use --no-validate to skip this step apply changes in one step.
|
4639
|
+
EOT
|
4640
|
+
end
|
4641
|
+
optparse.parse!(args)
|
4642
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
4643
|
+
connect(options)
|
4644
|
+
|
4645
|
+
instance = find_instance_by_name_or_id(args[0])
|
4646
|
+
return 1 if instance.nil?
|
4647
|
+
# construct request
|
4648
|
+
params.merge!(parse_query_options(options))
|
4649
|
+
payload = {}
|
4650
|
+
if options[:payload]
|
4651
|
+
payload = options[:payload]
|
4652
|
+
payload.deep_merge!(parse_passed_options(options))
|
4653
|
+
else
|
4654
|
+
payload.deep_merge!(parse_passed_options(options))
|
4655
|
+
# attempt to load prepare-apply to get templateParameter values and prompt for them
|
4656
|
+
# ok, actually use options/layoutParameters to get the list of parameters
|
4657
|
+
begin
|
4658
|
+
prepare_apply_json_response = @instances_interface.prepare_apply(instance["id"])
|
4659
|
+
config = prepare_apply_json_response['data']
|
4660
|
+
variable_map = config['templateParameter']
|
4661
|
+
api_params = {layoutId: instance['layout']['id'], instanceId: instance['id'], zoneId: instance['cloud']['id'], siteId: instance['group']['id']}
|
4662
|
+
layout_parameters = @options_interface.options_for_source('layoutParameters',api_params)['data']
|
4663
|
+
|
4664
|
+
if layout_parameters && !layout_parameters.empty?
|
4665
|
+
variable_option_types = []
|
4666
|
+
i = 0
|
4667
|
+
layout_parameters.each do |layout_parameter|
|
4668
|
+
var_label = layout_parameter['displayName'] || layout_parameter['name']
|
4669
|
+
var_name = layout_parameter['name']
|
4670
|
+
var_value = variable_map ? variable_map[var_name] : layout_parameter['defaultValue']
|
4671
|
+
if var_value.nil? && layout_parameter['defaultValue']
|
4672
|
+
var_value = layout_parameter['defaultValue']
|
4673
|
+
end
|
4674
|
+
var_type = (layout_parameter['passwordType'] || layout_parameter['sensitive']) ? 'password' : 'text'
|
4675
|
+
option_type = {'fieldContext' => 'templateParameter', 'fieldName' => var_name, 'fieldLabel' => var_label, 'type' => var_type, 'required' => true, 'defaultValue' => (var_value.to_s.empty? ? nil : var_value.to_s), 'displayOrder' => (i+1) }
|
4676
|
+
variable_option_types << option_type
|
4677
|
+
i+=1
|
4678
|
+
end
|
4679
|
+
blueprint_type_display = format_blueprint_type(instance['layout']['provisionTypeCode'])
|
4680
|
+
if blueprint_type_display == "terraform"
|
4681
|
+
blueprint_type_display = "Terraform"
|
4682
|
+
end
|
4683
|
+
print_h2 "#{blueprint_type_display} Variables"
|
4684
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt(variable_option_types, options[:options], @api_client)
|
4685
|
+
v_prompt.deep_compact!
|
4686
|
+
payload.deep_merge!(v_prompt)
|
4687
|
+
end
|
4688
|
+
rescue RestClient::Exception => ex
|
4689
|
+
# if e.response && e.response.code == 404
|
4690
|
+
Morpheus::Logging::DarkPrinter.puts "Unable to load config for instance apply, skipping parameter prompting" if Morpheus::Logging.debug?
|
4691
|
+
# print_rest_exception(ex, options)
|
4692
|
+
# end
|
4693
|
+
end
|
4694
|
+
end
|
4695
|
+
|
4696
|
+
@instances_interface.setopts(options)
|
4697
|
+
if options[:validate_only]
|
4698
|
+
# validate only
|
4699
|
+
if options[:dry_run]
|
4700
|
+
print_dry_run @instances_interface.dry.validate_apply(instance["id"], params, payload)
|
4701
|
+
return
|
4702
|
+
end
|
4703
|
+
json_response = @instances_interface.validate_apply(instance["id"], params, payload)
|
4704
|
+
print_green_success "Validating instance #{instance['name']}"
|
4705
|
+
execution_id = json_response['executionId']
|
4706
|
+
if !options[:no_refresh]
|
4707
|
+
#Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
|
4708
|
+
validate_execution_request = wait_for_execution_request(execution_id, options)
|
4709
|
+
end
|
4710
|
+
elsif options[:no_validate]
|
4711
|
+
# skip validate, apply only
|
4584
4712
|
if options[:dry_run]
|
4585
4713
|
print_dry_run @instances_interface.dry.apply(instance["id"], params, payload)
|
4586
4714
|
return
|
@@ -4588,17 +4716,172 @@ EOT
|
|
4588
4716
|
json_response = @instances_interface.apply(instance["id"], params, payload)
|
4589
4717
|
render_response(json_response, options) do
|
4590
4718
|
print_green_success "Applying instance #{instance['name']}"
|
4591
|
-
|
4719
|
+
execution_id = json_response['executionId']
|
4720
|
+
if !options[:no_refresh]
|
4721
|
+
#Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
|
4722
|
+
apply_execution_request = wait_for_execution_request(execution_id, options)
|
4723
|
+
end
|
4724
|
+
end
|
4725
|
+
else
|
4726
|
+
# validate and then apply
|
4727
|
+
if options[:dry_run]
|
4728
|
+
print_dry_run @instances_interface.dry.validate_apply(instance["id"], params, payload)
|
4729
|
+
print_dry_run @instances_interface.dry.apply(instance["id"], params, payload)
|
4730
|
+
return
|
4731
|
+
end
|
4732
|
+
json_response = @instances_interface.validate_apply(instance["id"], params, payload)
|
4733
|
+
print_green_success "Validating instance #{instance['name']}"
|
4734
|
+
execution_id = json_response['executionId']
|
4735
|
+
validate_execution_request = wait_for_execution_request(execution_id, options)
|
4736
|
+
if validate_execution_request['status'] != 'complete'
|
4737
|
+
print_red_alert "Validation failed. Changes will not be applied."
|
4738
|
+
return 1, "Validation failed. Changes will not be applied."
|
4739
|
+
else
|
4740
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to apply these changes?")
|
4741
|
+
return 9, "aborted command"
|
4742
|
+
end
|
4743
|
+
json_response = @instances_interface.apply(instance["id"], params, payload)
|
4744
|
+
render_response(json_response, options) do
|
4745
|
+
print_green_success "Applying instance #{instance['name']}"
|
4746
|
+
execution_id = json_response['executionId']
|
4747
|
+
if !options[:no_refresh]
|
4748
|
+
#Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
|
4749
|
+
apply_execution_request = wait_for_execution_request(execution_id, options)
|
4750
|
+
end
|
4751
|
+
end
|
4592
4752
|
end
|
4593
|
-
return 0, nil
|
4594
|
-
rescue RestClient::Exception => e
|
4595
|
-
print_rest_exception(e, options)
|
4596
|
-
exit 1
|
4597
4753
|
end
|
4754
|
+
return 0, nil
|
4598
4755
|
end
|
4599
4756
|
|
4600
|
-
|
4601
|
-
|
4757
|
+
def state(args)
|
4758
|
+
params, payload, options = {}, {}, {}
|
4759
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
4760
|
+
opts.banner = subcommand_usage("[instance] [options]")
|
4761
|
+
opts.on('--data', "Display State Data") do
|
4762
|
+
options[:include_state_data] = true
|
4763
|
+
end
|
4764
|
+
opts.on('--specs', "Display Spec Templates") do
|
4765
|
+
options[:include_spec_templates] = true
|
4766
|
+
end
|
4767
|
+
opts.on('--plan', "Display Plan Data") do
|
4768
|
+
options[:include_plan_data] = true
|
4769
|
+
end
|
4770
|
+
opts.on('--input', "Display Input") do
|
4771
|
+
options[:include_input] = true
|
4772
|
+
end
|
4773
|
+
opts.on('--output', "Display Output") do
|
4774
|
+
options[:include_output] = true
|
4775
|
+
end
|
4776
|
+
opts.on('-a','--all', "Display All Details") do
|
4777
|
+
options[:include_state_data] = true
|
4778
|
+
options[:include_spec_templates] = true
|
4779
|
+
options[:include_plan_data] = true
|
4780
|
+
options[:include_input] = true
|
4781
|
+
options[:include_output] = true
|
4782
|
+
options[:details] = true
|
4783
|
+
end
|
4784
|
+
build_standard_get_options(opts, options)
|
4785
|
+
opts.footer = <<-EOT
|
4786
|
+
View state of an instance.
|
4787
|
+
[instance] is required. This is the name or id of an instance.
|
4788
|
+
This is only supported by certain types of apps such as terraform.
|
4789
|
+
EOT
|
4790
|
+
end
|
4791
|
+
optparse.parse!(args)
|
4792
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
4793
|
+
connect(options)
|
4794
|
+
instance = find_instance_by_name_or_id(args[0])
|
4795
|
+
return 1 if instance.nil?
|
4796
|
+
# construct request
|
4797
|
+
params.merge!(parse_query_options(options))
|
4798
|
+
@instances_interface.setopts(options)
|
4799
|
+
if options[:dry_run]
|
4800
|
+
print_dry_run @instances_interface.dry.state(instance["id"], params)
|
4801
|
+
return
|
4802
|
+
end
|
4803
|
+
json_response = @instances_interface.state(instance["id"], params)
|
4804
|
+
render_result = render_with_format(json_response, options)
|
4805
|
+
return 0 if render_result
|
4806
|
+
print_h1 "Instance State: #{instance['name']}", options
|
4807
|
+
# print_h2 "Workloads", options
|
4808
|
+
if json_response['workloads'] && !json_response['workloads'].empty?
|
4809
|
+
workload_columns = {
|
4810
|
+
"Name" => lambda {|it| it['subRefName'].to_s.empty? ? "#{it['refName']}" : "#{it['refName']} - #{it['subRefName']}" },
|
4811
|
+
"Last Check" => lambda {|it| format_local_dt(it['stateDate']) },
|
4812
|
+
"Status" => lambda {|it| format_ok_status(it['status'] || 'ok') },
|
4813
|
+
"Drift Status" => lambda {|it| it['iacDrift'] ? "Drift" : "No Drift" }
|
4814
|
+
}
|
4815
|
+
print as_pretty_table(json_response['workloads'], workload_columns.upcase_keys!, options)
|
4816
|
+
else
|
4817
|
+
print cyan,"No workloads found.",reset,"\n"
|
4818
|
+
end
|
4819
|
+
if options[:include_state_data]
|
4820
|
+
print_h2 "State Data", options
|
4821
|
+
puts json_response['stateData']
|
4822
|
+
end
|
4823
|
+
if options[:include_spec_templates]
|
4824
|
+
print_h2 "Spec Templates", options
|
4825
|
+
spec_templates_columns = {
|
4826
|
+
"Resource Spec" => lambda {|it| it['name'] || (it['template'] ? it['template']['name'] : nil) },
|
4827
|
+
"Attached to Source Template" => lambda {|it| format_boolean(!it['isolated']) },
|
4828
|
+
"Source Spec Template" => lambda {|it| (it['template'] ? it['template']['name'] : nil) || it['name'] }
|
4829
|
+
}
|
4830
|
+
print as_pretty_table(json_response['specs'], spec_templates_columns.upcase_keys!, options)
|
4831
|
+
# print "\n", reset
|
4832
|
+
end
|
4833
|
+
if options[:include_plan_data]
|
4834
|
+
# print_h2 "Plan Data", options
|
4835
|
+
if instance['type'] == 'terraform' || instance['layout']['provisionTypeCode'] == 'terraform'
|
4836
|
+
print_h2 "Terraform Plan", options
|
4837
|
+
else
|
4838
|
+
print_h2 "Plan Data", options
|
4839
|
+
end
|
4840
|
+
puts json_response['planData']
|
4841
|
+
# print "\n", reset
|
4842
|
+
end
|
4843
|
+
if options[:include_input]
|
4844
|
+
# print_h2 "Input"
|
4845
|
+
if json_response['input'] && json_response['input']['variables']
|
4846
|
+
print_h2 "VARIABLES", options
|
4847
|
+
input_variable_columns = {
|
4848
|
+
"Name" => lambda {|it| it['name'] },
|
4849
|
+
"Value" => lambda {|it| it['value'] }
|
4850
|
+
}
|
4851
|
+
print as_pretty_table(json_response['input']['variables'], input_variable_columns.upcase_keys!, options)
|
4852
|
+
end
|
4853
|
+
if json_response['input'] && json_response['input']['providers']
|
4854
|
+
print_h2 "PROVIDERS", options
|
4855
|
+
input_provider_columns = {
|
4856
|
+
"Name" => lambda {|it| it['name'] }
|
4857
|
+
}
|
4858
|
+
print as_pretty_table(json_response['input']['providers'], input_provider_columns.upcase_keys!, options)
|
4859
|
+
end
|
4860
|
+
if json_response['input'] && json_response['input']['data']
|
4861
|
+
print_h2 "DATA", options
|
4862
|
+
input_data_columns = {
|
4863
|
+
"Type" => lambda {|it| it['type'] },
|
4864
|
+
"Key" => lambda {|it| it['key'] },
|
4865
|
+
"Name" => lambda {|it| it['name'] }
|
4866
|
+
}
|
4867
|
+
print as_pretty_table(json_response['input']['data'], input_data_columns.upcase_keys!, options)
|
4868
|
+
end
|
4869
|
+
# print "\n", reset
|
4870
|
+
end
|
4871
|
+
if options[:include_output]
|
4872
|
+
# print_h2 "Output", options
|
4873
|
+
if json_response['output'] && json_response['output']['outputs']
|
4874
|
+
print_h2 "OUTPUTS", options
|
4875
|
+
input_variable_columns = {
|
4876
|
+
"Name" => lambda {|it| it['name'] },
|
4877
|
+
"Value" => lambda {|it| it['value'] }
|
4878
|
+
}
|
4879
|
+
print as_pretty_table(json_response['output']['outputs'], input_variable_columns.upcase_keys!, options)
|
4880
|
+
end
|
4881
|
+
# print "\n", reset
|
4882
|
+
end
|
4883
|
+
print "\n", reset
|
4884
|
+
return 0
|
4602
4885
|
end
|
4603
4886
|
|
4604
4887
|
private
|
@@ -187,6 +187,7 @@ EOT
|
|
187
187
|
""
|
188
188
|
end
|
189
189
|
},
|
190
|
+
"Tfvar Secret" => lambda {|it| it['tfvarSecret'] },
|
190
191
|
# "Category" => lambda {|it| it['category'].to_s.capitalize },
|
191
192
|
# # "Logo" => lambda {|it| it['logo'].to_s },
|
192
193
|
# "Visiblity" => lambda {|it| it['visibility'].to_s.capitalize },
|
@@ -200,6 +201,7 @@ EOT
|
|
200
201
|
# "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
201
202
|
# "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
202
203
|
}
|
204
|
+
description_cols.delete("Tfvar Secret") if !(layout['provisionType'] && layout['provisionType']['code'] == 'terraform')
|
203
205
|
print_description_list(description_cols, layout)
|
204
206
|
|
205
207
|
|
@@ -338,6 +340,9 @@ EOT
|
|
338
340
|
params['specTemplates'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
339
341
|
end
|
340
342
|
end
|
343
|
+
opts.on('--tfvar-secret VALUE', String, "Tfvar Secret name, eg. 'tfvars/dev-key'") do |val|
|
344
|
+
params['tfvarSecret'] = val == 'null' ? nil : val
|
345
|
+
end
|
341
346
|
add_perms_options(opts, options, layout_permission_excludes)
|
342
347
|
build_standard_add_options(opts, options)
|
343
348
|
opts.footer = <<-EOT
|
@@ -453,6 +458,13 @@ EOT
|
|
453
458
|
return 1
|
454
459
|
end
|
455
460
|
|
461
|
+
# TFVAR SECRET
|
462
|
+
if !params['tfvarSecret']
|
463
|
+
if provision_type && provision_type['code'] == 'terraform'
|
464
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tfvarSecret', 'type' => 'select', 'optionSource' => 'tfvarSecrets', 'fieldLabel' => 'Tfvar Secret', 'required' => false}], options[:options], @api_client)
|
465
|
+
params['tfvarSecret'] = v_prompt['tfvarSecret'] if v_prompt['tfvarSecret']
|
466
|
+
end
|
467
|
+
end
|
456
468
|
|
457
469
|
payload = {'instanceTypeLayout' => params}
|
458
470
|
|
@@ -544,6 +556,9 @@ EOT
|
|
544
556
|
params['specTemplates'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
545
557
|
end
|
546
558
|
end
|
559
|
+
opts.on('--tfvar-secret VALUE', String, "Tfvar Secret name, eg. 'tfvars/dev-key'") do |val|
|
560
|
+
params['tfvarSecret'] = val == 'null' ? nil : val
|
561
|
+
end
|
547
562
|
add_perms_options(opts, options, layout_permission_excludes)
|
548
563
|
build_standard_update_options(opts, options)
|
549
564
|
opts.footer = <<-EOT
|
@@ -489,7 +489,7 @@ class Morpheus::Cli::ProvisioningLicensesCommand
|
|
489
489
|
{'fieldName' => 'licenseVersion', 'fieldLabel' => 'Version', 'type' => 'text', 'displayOrder' => 6},
|
490
490
|
{'fieldName' => 'copies', 'fieldLabel' => 'Copies', 'type' => 'number', 'required' => true, 'defaultValue' => 1, 'displayOrder' => 7},
|
491
491
|
{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 8},
|
492
|
-
{'fieldName' => 'virtualImages', 'fieldLabel' => 'Virtual Images', 'type' => 'multiSelect', 'optionSource' => lambda {
|
492
|
+
{'fieldName' => 'virtualImages', 'fieldLabel' => 'Virtual Images', 'type' => 'multiSelect', 'optionSource' => lambda { |api_client, api_params|
|
493
493
|
# @options_interface.options_for_source("virtualImages", {})['data']
|
494
494
|
get_virtual_images_dropdown()
|
495
495
|
}, 'displayOrder' => 9},
|