morpheus-cli 4.2.14 → 4.2.19

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/README.md +8 -6
  4. data/lib/morpheus/api/api_client.rb +32 -14
  5. data/lib/morpheus/api/auth_interface.rb +4 -2
  6. data/lib/morpheus/api/backup_jobs_interface.rb +9 -0
  7. data/lib/morpheus/api/backups_interface.rb +16 -0
  8. data/lib/morpheus/api/deploy_interface.rb +25 -56
  9. data/lib/morpheus/api/deployments_interface.rb +44 -55
  10. data/lib/morpheus/api/doc_interface.rb +57 -0
  11. data/lib/morpheus/api/instances_interface.rb +5 -0
  12. data/lib/morpheus/api/rest_interface.rb +40 -0
  13. data/lib/morpheus/api/user_sources_interface.rb +0 -15
  14. data/lib/morpheus/api/users_interface.rb +2 -3
  15. data/lib/morpheus/benchmarking.rb +2 -2
  16. data/lib/morpheus/cli.rb +4 -1
  17. data/lib/morpheus/cli/access_token_command.rb +27 -10
  18. data/lib/morpheus/cli/apps.rb +21 -15
  19. data/lib/morpheus/cli/backup_jobs_command.rb +276 -0
  20. data/lib/morpheus/cli/backups_command.rb +271 -0
  21. data/lib/morpheus/cli/blueprints_command.rb +27 -61
  22. data/lib/morpheus/cli/boot_scripts_command.rb +1 -1
  23. data/lib/morpheus/cli/cli_command.rb +183 -45
  24. data/lib/morpheus/cli/cli_registry.rb +3 -0
  25. data/lib/morpheus/cli/clouds.rb +7 -10
  26. data/lib/morpheus/cli/clusters.rb +0 -18
  27. data/lib/morpheus/cli/commands/standard/benchmark_command.rb +23 -20
  28. data/lib/morpheus/cli/commands/standard/man_command.rb +1 -1
  29. data/lib/morpheus/cli/credentials.rb +13 -9
  30. data/lib/morpheus/cli/deploy.rb +374 -0
  31. data/lib/morpheus/cli/deployments.rb +521 -197
  32. data/lib/morpheus/cli/deploys.rb +271 -126
  33. data/lib/morpheus/cli/doc.rb +182 -0
  34. data/lib/morpheus/cli/error_handler.rb +23 -8
  35. data/lib/morpheus/cli/errors.rb +3 -2
  36. data/lib/morpheus/cli/image_builder_command.rb +2 -2
  37. data/lib/morpheus/cli/instances.rb +136 -17
  38. data/lib/morpheus/cli/invoices_command.rb +339 -225
  39. data/lib/morpheus/cli/jobs_command.rb +2 -2
  40. data/lib/morpheus/cli/library_layouts_command.rb +1 -1
  41. data/lib/morpheus/cli/library_option_lists_command.rb +61 -125
  42. data/lib/morpheus/cli/library_option_types_command.rb +32 -37
  43. data/lib/morpheus/cli/login.rb +9 -3
  44. data/lib/morpheus/cli/mixins/accounts_helper.rb +158 -100
  45. data/lib/morpheus/cli/mixins/backups_helper.rb +115 -0
  46. data/lib/morpheus/cli/mixins/deployments_helper.rb +135 -0
  47. data/lib/morpheus/cli/mixins/library_helper.rb +32 -0
  48. data/lib/morpheus/cli/mixins/option_source_helper.rb +1 -1
  49. data/lib/morpheus/cli/mixins/print_helper.rb +149 -84
  50. data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -2
  51. data/lib/morpheus/cli/mixins/whoami_helper.rb +19 -6
  52. data/lib/morpheus/cli/network_routers_command.rb +1 -1
  53. data/lib/morpheus/cli/option_parser.rb +48 -5
  54. data/lib/morpheus/cli/option_types.rb +46 -10
  55. data/lib/morpheus/cli/price_sets_command.rb +1 -1
  56. data/lib/morpheus/cli/projects_command.rb +7 -7
  57. data/lib/morpheus/cli/remote.rb +3 -2
  58. data/lib/morpheus/cli/roles.rb +49 -92
  59. data/lib/morpheus/cli/security_groups.rb +7 -1
  60. data/lib/morpheus/cli/service_plans_command.rb +10 -10
  61. data/lib/morpheus/cli/setup.rb +1 -1
  62. data/lib/morpheus/cli/shell.rb +7 -6
  63. data/lib/morpheus/cli/subnets_command.rb +1 -1
  64. data/lib/morpheus/cli/tasks.rb +24 -10
  65. data/lib/morpheus/cli/tenants_command.rb +133 -163
  66. data/lib/morpheus/cli/user_groups_command.rb +20 -65
  67. data/lib/morpheus/cli/user_settings_command.rb +115 -13
  68. data/lib/morpheus/cli/user_sources_command.rb +57 -24
  69. data/lib/morpheus/cli/users.rb +210 -186
  70. data/lib/morpheus/cli/version.rb +1 -1
  71. data/lib/morpheus/cli/whitelabel_settings_command.rb +29 -5
  72. data/lib/morpheus/cli/whoami.rb +113 -6
  73. data/lib/morpheus/cli/workflows.rb +11 -8
  74. data/lib/morpheus/ext/hash.rb +21 -0
  75. data/lib/morpheus/formatters.rb +7 -19
  76. data/lib/morpheus/terminal.rb +1 -0
  77. metadata +12 -3
  78. data/lib/morpheus/cli/auth_command.rb +0 -105
@@ -0,0 +1,271 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::BackupsCommand
4
+ include Morpheus::Cli::CliCommand
5
+ include Morpheus::Cli::BackupsHelper
6
+ include Morpheus::Cli::ProvisioningHelper
7
+ include Morpheus::Cli::OptionSourceHelper
8
+
9
+ set_command_hidden # hide until ready
10
+
11
+ set_command_name :'backups'
12
+
13
+ register_subcommands :list, :get, :add, :update, :remove, :run, :restore
14
+
15
+ def connect(opts)
16
+ @api_client = establish_remote_appliance_connection(opts)
17
+ @backups_interface = @api_client.backups
18
+ @backup_jobs_interface = @api_client.backup_jobs
19
+ end
20
+
21
+ def handle(args)
22
+ handle_subcommand(args)
23
+ end
24
+
25
+ def list(args)
26
+ options = {}
27
+ params = {}
28
+ ref_ids = []
29
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
30
+ opts.banner = subcommand_usage("[search]")
31
+ build_standard_list_options(opts, options)
32
+ opts.footer = "List backups."
33
+ end
34
+ optparse.parse!(args)
35
+ connect(options)
36
+ # verify_args!(args:args, optparse:optparse, count:0)
37
+ if args.count > 0
38
+ options[:phrase] = args.join(" ")
39
+ end
40
+ params.merge!(parse_list_options(options))
41
+ @backups_interface.setopts(options)
42
+ if options[:dry_run]
43
+ print_dry_run @backups_interface.dry.list(params)
44
+ return
45
+ end
46
+ json_response = @backups_interface.list(params)
47
+ backups = json_response['backups']
48
+ render_response(json_response, options, 'backups') do
49
+ print_h1 "Morpheus Backups", parse_list_subtitles(options), options
50
+ if backups.empty?
51
+ print cyan,"No backups found.",reset,"\n"
52
+ else
53
+ print as_pretty_table(backups, backup_column_definitions.upcase_keys!, options)
54
+ print_results_pagination(json_response)
55
+ end
56
+ print reset,"\n"
57
+ end
58
+ if backups.empty?
59
+ return 1, "no backups found"
60
+ else
61
+ return 0, nil
62
+ end
63
+ end
64
+
65
+ def get(args)
66
+ params = {}
67
+ options = {}
68
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
69
+ opts.banner = subcommand_usage("[backup]")
70
+ build_standard_get_options(opts, options)
71
+ opts.footer = <<-EOT
72
+ Get details about a specific backup.
73
+ [backup] is required. This is the name or id of a backup.
74
+ EOT
75
+ end
76
+ optparse.parse!(args)
77
+ verify_args!(args:args, optparse:optparse, min:1)
78
+ connect(options)
79
+ id_list = parse_id_list(args)
80
+ return run_command_for_each_arg(id_list) do |arg|
81
+ _get(arg, params, options)
82
+ end
83
+ end
84
+
85
+ def _get(id, options)
86
+ params = {}
87
+ @backups_interface.setopts(options)
88
+ if options[:dry_run]
89
+ print_dry_run @backups_interface.dry.get(id, params)
90
+ return
91
+ end
92
+ json_response = @backups_interface.get(id, params)
93
+ backup = json_response['backup']
94
+ render_response(json_response, options, 'backup') do
95
+ print_h1 "Backup Details", [], options
96
+ print cyan
97
+ print_description_list(backup_column_definitions, backup)
98
+ print reset,"\n"
99
+ end
100
+ return 0, nil
101
+ end
102
+
103
+ def add(args)
104
+ options = {}
105
+ params = {}
106
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
107
+ opts.banner = subcommand_usage("[name] [options]")
108
+ build_option_type_options(opts, options, add_backup_option_types)
109
+ build_option_type_options(opts, options, add_backup_advanced_option_types)
110
+ build_standard_add_options(opts, options)
111
+ opts.footer = <<-EOT
112
+ Create a new backup.
113
+ EOT
114
+ end
115
+ optparse.parse!(args)
116
+ verify_args!(args:args, optparse:optparse, min:0, max:1)
117
+ options[:options]['name'] = args[0] if args[0]
118
+ connect(options)
119
+ payload = {}
120
+ if options[:payload]
121
+ payload = options[:payload]
122
+ payload.deep_merge!({'backup' => parse_passed_options(options)})
123
+ else
124
+ payload.deep_merge!({'backup' => parse_passed_options(options)})
125
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(add_backup_option_types(), options[:options], @api_client, options[:params])
126
+ params.deep_merge!(v_prompt)
127
+ advanced_config = Morpheus::Cli::OptionTypes.no_prompt(add_backup_advanced_option_types, options[:options], @api_client, options[:params])
128
+ advanced_config.deep_compact!
129
+ params.deep_merge!(advanced_config)
130
+ payload['backup'].deep_merge!(params)
131
+ end
132
+ @backups_interface.setopts(options)
133
+ if options[:dry_run]
134
+ print_dry_run @backups_interface.dry.create(payload)
135
+ return 0, nil
136
+ end
137
+ json_response = @backups_interface.create(payload)
138
+ backup = json_response['backup']
139
+ render_response(json_response, options, 'backup') do
140
+ print_green_success "Added backup #{backup['name']}"
141
+ return _get(backup["id"], {}, options)
142
+ end
143
+ return 0, nil
144
+ end
145
+
146
+ def update(args)
147
+ options = {}
148
+ params = {}
149
+ payload = {}
150
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
151
+ opts.banner = subcommand_usage("[backup] [options]")
152
+ build_option_type_options(opts, options, update_backup_option_types)
153
+ build_option_type_options(opts, options, update_backup_advanced_option_types)
154
+ build_standard_update_options(opts, options)
155
+ opts.footer = <<-EOT
156
+ Update a backup.
157
+ [backup] is required. This is the name or id of a backup.
158
+ EOT
159
+ end
160
+ optparse.parse!(args)
161
+ verify_args!(args:args, optparse:optparse, count:1)
162
+ connect(options)
163
+ backup = find_backup_by_name_or_id(args[0])
164
+ return 1 if backup.nil?
165
+ payload = {}
166
+ if options[:payload]
167
+ payload = options[:payload]
168
+ payload.deep_merge!({'backup' => parse_passed_options(options)})
169
+ else
170
+ payload.deep_merge!({'backup' => parse_passed_options(options)})
171
+ # do not prompt on update
172
+ v_prompt = Morpheus::Cli::OptionTypes.no_prompt(update_backup_option_types, options[:options], @api_client, options[:params])
173
+ v_prompt.deep_compact!
174
+ params.deep_merge!(v_prompt)
175
+ advanced_config = Morpheus::Cli::OptionTypes.no_prompt(update_backup_advanced_option_types, options[:options], @api_client, options[:params])
176
+ advanced_config.deep_compact!
177
+ params.deep_merge!(advanced_config)
178
+ payload.deep_merge!({'backup' => params})
179
+ if payload['backup'].empty? # || options[:no_prompt]
180
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
181
+ end
182
+ end
183
+ @backups_interface.setopts(options)
184
+ if options[:dry_run]
185
+ print_dry_run @backups_interface.dry.update(backup['id'], payload)
186
+ return
187
+ end
188
+ json_response = @backups_interface.update(backup['id'], payload)
189
+ backup = json_response['backup']
190
+ render_response(json_response, options, 'backup') do
191
+ print_green_success "Updated backup #{backup['name']}"
192
+ return _get(backup["id"], {}, options)
193
+ end
194
+ return 0, nil
195
+ end
196
+
197
+ def remove(args)
198
+ options = {}
199
+ params = {}
200
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
201
+ opts.banner = subcommand_usage("[backup] [options]")
202
+ build_standard_remove_options(opts, options)
203
+ opts.footer = <<-EOT
204
+ Delete a backup.
205
+ [backup] is required. This is the name or id of a backup.
206
+ EOT
207
+ end
208
+ optparse.parse!(args)
209
+ verify_args!(args:args, optparse:optparse, count:1)
210
+ connect(options)
211
+ backup = find_backup_by_name_or_id(args[0])
212
+ return 1 if backup.nil?
213
+ @backups_interface.setopts(options)
214
+ if options[:dry_run]
215
+ print_dry_run @backups_interface.dry.destroy(backup['id'], params)
216
+ return
217
+ end
218
+ json_response = @backups_interface.destroy(backup['id'], params)
219
+ render_response(json_response, options) do
220
+ print_green_success "Removed backup #{backup['name']}"
221
+ end
222
+ return 0, nil
223
+ end
224
+
225
+ private
226
+
227
+ def backup_column_definitions()
228
+ {
229
+ "ID" => 'id',
230
+ "Name" => 'name',
231
+ "Schedule" => lambda {|it| it['schedule']['name'] rescue '' },
232
+ "Backup Job" => lambda {|it| it['job']['name'] rescue '' },
233
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
234
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
235
+ }
236
+ end
237
+
238
+ # this is not so simple, need to first choose select instance, host or provider
239
+ def add_backup_option_types
240
+ [
241
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
242
+ {'fieldName' => 'jobId', 'fieldLabel' => 'Backup Job', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
243
+ # @options_interface.options_for_source("licenseTypes", {})['data']
244
+ @backup_jobs_interface.list({max:10000})['backupJobs'].collect {|backup_job|
245
+ {'name' => backup_job['name'], 'value' => backup_job['id'], 'id' => backup_job['id']}
246
+ }
247
+ }, 'required' => true, 'displayOrder' => 3},
248
+ ]
249
+ end
250
+
251
+ def add_backup_advanced_option_types
252
+ []
253
+ end
254
+
255
+ def update_backup_option_types
256
+ add_backup_option_types.collect {|it|
257
+ it.delete('required')
258
+ it.delete('defaultValue')
259
+ it
260
+ }
261
+ end
262
+
263
+ def update_backup_advanced_option_types
264
+ add_backup_advanced_option_types.collect {|it|
265
+ it.delete('required')
266
+ it.delete('defaultValue')
267
+ it
268
+ }
269
+ end
270
+
271
+ end
@@ -62,11 +62,15 @@ class Morpheus::Cli::BlueprintsCommand
62
62
  params['ownerId'] << val
63
63
  end
64
64
  opts.add_hidden_option('--created-by')
65
- build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
65
+ build_standard_list_options(opts, options)
66
66
  opts.footer = "List blueprints."
67
67
  end
68
68
  optparse.parse!(args)
69
69
  connect(options)
70
+ # verify_args!(args:args, optparse:optparse, count:0)
71
+ if args.count > 0
72
+ options[:phrase] = args.join(" ")
73
+ end
70
74
  begin
71
75
  if params['ownerId']
72
76
  params['ownerId'] = params['ownerId'].collect do |owner_id|
@@ -125,7 +129,7 @@ class Morpheus::Cli::BlueprintsCommand
125
129
  opts.on( '-c', '--config', "Display raw config only. Default is YAML. Combine with -j for JSON instead." ) do
126
130
  options[:show_config] = true
127
131
  end
128
- build_common_options(opts, options, [:json, :yaml, :csv, :fields, :outfile, :dry_run, :remote])
132
+ build_standard_get_options(opts, options)
129
133
  opts.footer = "Get details about a blueprint.\n" +
130
134
  "[blueprint] is required. This is the name or id of a blueprint. Supports 1-N [instance] arguments."
131
135
  end
@@ -154,8 +158,9 @@ class Morpheus::Cli::BlueprintsCommand
154
158
  end
155
159
  @blueprints_interface.setopts(options)
156
160
  blueprint = find_blueprint_by_name_or_id(arg)
157
- exit 1 if blueprint.nil?
158
-
161
+ if blueprint.nil?
162
+ return 1, "blueprint not found"
163
+ end
159
164
  json_response = {'blueprint' => blueprint} # skip redundant request
160
165
  #json_response = @blueprints_interface.get(blueprint['id'])
161
166
  blueprint = json_response['blueprint']
@@ -165,43 +170,13 @@ class Morpheus::Cli::BlueprintsCommand
165
170
  unless options[:json] || options[:yaml] || options[:csv]
166
171
  options[:yaml] = true
167
172
  end
168
- blueprint_config = blueprint['config']
169
- render_result = render_with_format(blueprint_config, options)
170
- return 0 if render_result
173
+ return render_with_format(blueprint['config'], options)
171
174
  end
172
-
173
- render_result = render_with_format(json_response, options, 'blueprint')
174
- return 0 if render_result
175
-
176
- print_h1 "Blueprint Details"
177
-
178
- print_blueprint_details(blueprint)
179
-
180
- if blueprint['resourcePermission'].nil?
181
- #print "\n", "No group access found", "\n"
182
- else
183
- # print_h2 "Group Access"
184
- # rows = []
185
- # if blueprint['resourcePermission']['allSites'] || blueprint['resourcePermission']['all']
186
- # rows.push({"name" => 'All'})
187
- # end
188
- # if blueprint['resourcePermission']['sites']
189
- # blueprint['resourcePermission']['sites'].each do |site|
190
- # rows.push(site)
191
- # end
192
- # end
193
- # rows = rows.collect do |site|
194
- # {group: site['name'], default: site['default'] ? 'Yes' : ''}
195
- # end
196
- # # columns = [:group, :default]
197
- # columns = [:group]
198
- # print cyan
199
- # print as_pretty_table(rows, columns)
200
- # print reset,"\n"
201
- end
202
-
203
- #print reset,"\n"
204
- return 0
175
+ render_response(json_response, options, 'blueprint') do
176
+ print_h1 "Blueprint Details"
177
+ print_blueprint_details(blueprint)
178
+ end
179
+ return 0, nil
205
180
  rescue RestClient::Exception => e
206
181
  print_rest_exception(e, options)
207
182
  exit 1
@@ -216,17 +191,12 @@ class Morpheus::Cli::BlueprintsCommand
216
191
  opts.on('-t', '--type TYPE', String, "Blueprint Type. Default is morpheus.") do |val|
217
192
  options[:blueprint_type] = parse_blueprint_type(val.to_s)
218
193
  end
219
- build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
194
+ build_standard_add_options(opts, options)
220
195
  opts.footer = "Create a new blueprint.\n" +
221
196
  "[name] is required. This is the name of the new blueprint."
222
197
  end
223
198
  optparse.parse!(args)
224
- if args.count > 1
225
- print_error Morpheus::Terminal.angry_prompt
226
- puts_error "#{command_name} add expects 0-1 arguments and received #{args.count}: #{args}\n#{optparse}"
227
- return 1
228
- end
229
- options[:options] ||= {}
199
+ verify_args!(args:args, optparse:optparse, min:0, max:1)
230
200
  if args[0]
231
201
  options[:options]['name'] = args[0]
232
202
  end
@@ -248,9 +218,7 @@ class Morpheus::Cli::BlueprintsCommand
248
218
  end
249
219
  params = Morpheus::Cli::OptionTypes.prompt(add_blueprint_option_types, options[:options], @api_client, options[:params])
250
220
  params.deep_compact!
251
- #blueprint_payload = params.select {|k,v| ['name', 'description', 'category'].include?(k) }
252
221
  # expects no namespace, just the config
253
- #payload = blueprint_payload
254
222
  payload.deep_merge!(params)
255
223
  end
256
224
  @blueprints_interface.setopts(options)
@@ -260,12 +228,8 @@ class Morpheus::Cli::BlueprintsCommand
260
228
  end
261
229
 
262
230
  json_response = @blueprints_interface.create(payload)
263
-
264
- if options[:json]
265
- print JSON.pretty_generate(json_response)
266
- print "\n"
267
- elsif !options[:quiet]
268
- blueprint = json_response["blueprint"]
231
+ blueprint = json_response['blueprint']
232
+ render_response(json_response, options, 'blueprint') do
269
233
  print_green_success "Added blueprint #{blueprint['name']}"
270
234
  if !options[:no_prompt]
271
235
  if options[:payload].nil? && ::Morpheus::Cli::OptionTypes::confirm("Would you like to add a tier now?", options.merge({default: false}))
@@ -275,11 +239,11 @@ class Morpheus::Cli::BlueprintsCommand
275
239
  end
276
240
  else
277
241
  # print details
278
- get([blueprint['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
242
+ return _get(blueprint["id"], options)
279
243
  end
280
244
  end
281
245
  end
282
- return 0
246
+ return 0, nil
283
247
  rescue RestClient::Exception => e
284
248
  print_rest_exception(e, options)
285
249
  exit 1
@@ -294,7 +258,7 @@ class Morpheus::Cli::BlueprintsCommand
294
258
  opts.on( '--owner USER', "Owner Username or ID" ) do |val|
295
259
  options[:owner] = val == 'null' ? nil : val
296
260
  end
297
- build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
261
+ build_standard_update_options(opts, options)
298
262
  opts.footer = "Update a blueprint.\n" +
299
263
  "[blueprint] is required. This is the name or id of a blueprint."
300
264
  end
@@ -388,7 +352,7 @@ class Morpheus::Cli::BlueprintsCommand
388
352
  options[:owner] = val == 'null' ? nil : val
389
353
  end
390
354
  build_option_type_options(opts, options, update_blueprint_option_types(false))
391
- build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
355
+ build_standard_update_options(opts, options)
392
356
  opts.footer = "Update a blueprint permissions.\n" +
393
357
  "[blueprint] is required. This is the name or id of a blueprint."
394
358
  end
@@ -586,7 +550,7 @@ class Morpheus::Cli::BlueprintsCommand
586
550
  options = {}
587
551
  optparse = Morpheus::Cli::OptionParser.new do |opts|
588
552
  opts.banner = subcommand_usage("[blueprint]")
589
- build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
553
+ build_standard_remove_options(opts, options)
590
554
  opts.footer = "Delete a blueprint." + "\n" +
591
555
  "[blueprint] is required. This is the name or id of a blueprint."
592
556
  end
@@ -2123,6 +2087,7 @@ class Morpheus::Cli::BlueprintsCommand
2123
2087
  }
2124
2088
 
2125
2089
  print_description_list(description_cols, blueprint)
2090
+ print reset,"\n"
2126
2091
  # print_h2 "Tiers"
2127
2092
  if blueprint["config"] && blueprint["config"]["tiers"] && blueprint["config"]["tiers"].keys.size != 0
2128
2093
  print cyan
@@ -2213,7 +2178,8 @@ class Morpheus::Cli::BlueprintsCommand
2213
2178
  else
2214
2179
  #print white,"\nTemplate is empty, use `blueprints add-tier \"#{blueprint['name']}\"`",reset,"\n"
2215
2180
  end
2216
- print reset,"\n"
2181
+ # print reset,"\n"
2182
+ print reset
2217
2183
  end
2218
2184
 
2219
2185
  # this parses the environments => groups => clouds tree structure