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.
@@ -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, "Tags") do |val|
377
- options[:tags] = val
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
- tags = options[:tags]
520
+ labels = options[:labels]
514
521
 
515
- if !tags && !options[:no_prompt]
516
- tags = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tags', 'type' => 'text', 'fieldLabel' => 'Resource Labels', 'required' => false, 'description' => 'Resource Tags.'}],options[:options],@api_client,{})['tags']
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['tags'] = tags if tags
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.merge({'defaultAddFirstDataVolume': true}), @api_client, api_params)
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| !type['enabled'] || !type['creatable'] || type['fieldComponent'] } rescue []))
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
- server_payload['config']['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']
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.merge({'defaultAddFirstDataVolume': true}), @api_client, {zoneId: cloud['id'], siteId: group['id']})
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['exitCode'] || ['complete','failed','expired'].include?(execution_request['status'])
86
+ if !(execution_request['status'] == 'pending' || execution_request['status'] == 'new')
87
87
  # it is finished
88
88
  else
89
89
  print cyan
90
- print "Execution request has not yet finished. Refreshing every #{options[:refresh_interval]} seconds"
91
- while execution_request['exitCode'].nil? do
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
- print "File copy request has not yet finished. Refreshing every #{options[:refresh_interval]} seconds"
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 apply(args)
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
- Apply an instance.
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
- verify_args!(args:args, optparse:optparse, count:1)
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
- # return _get(instance['id'], options)
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
- # todo: print state summary
4601
- def state_summary(args)
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},