morpheus-cli 3.6.9 → 3.6.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/lib/morpheus/api/api_client.rb +0 -4
  3. data/lib/morpheus/api/deploy_interface.rb +12 -8
  4. data/lib/morpheus/cli.rb +0 -1
  5. data/lib/morpheus/cli/access_token_command.rb +3 -4
  6. data/lib/morpheus/cli/account_groups_command.rb +8 -3
  7. data/lib/morpheus/cli/apps.rb +10 -5
  8. data/lib/morpheus/cli/archives_command.rb +20 -6
  9. data/lib/morpheus/cli/boot_scripts_command.rb +5 -2
  10. data/lib/morpheus/cli/change_password_command.rb +1 -1
  11. data/lib/morpheus/cli/cli_command.rb +2 -4
  12. data/lib/morpheus/cli/cloud_datastores_command.rb +3 -2
  13. data/lib/morpheus/cli/commands/standard/alias_command.rb +10 -14
  14. data/lib/morpheus/cli/commands/standard/history_command.rb +0 -20
  15. data/lib/morpheus/cli/commands/standard/man_command.rb +8 -8
  16. data/lib/morpheus/cli/containers_command.rb +9 -1
  17. data/lib/morpheus/cli/credentials.rb +2 -1
  18. data/lib/morpheus/cli/cypher_command.rb +10 -2
  19. data/lib/morpheus/cli/dashboard_command.rb +1 -0
  20. data/lib/morpheus/cli/deployments.rb +5 -1
  21. data/lib/morpheus/cli/deploys.rb +26 -9
  22. data/lib/morpheus/cli/execute_schedules_command.rb +9 -1
  23. data/lib/morpheus/cli/execution_request_command.rb +3 -2
  24. data/lib/morpheus/cli/file_copy_request_command.rb +3 -2
  25. data/lib/morpheus/cli/hosts.rb +8 -6
  26. data/lib/morpheus/cli/image_builder_command.rb +14 -10
  27. data/lib/morpheus/cli/instance_types.rb +2 -1
  28. data/lib/morpheus/cli/instances.rb +9 -4
  29. data/lib/morpheus/cli/key_pairs.rb +33 -21
  30. data/lib/morpheus/cli/library_container_scripts_command.rb +5 -2
  31. data/lib/morpheus/cli/library_container_templates_command.rb +5 -2
  32. data/lib/morpheus/cli/library_container_types_command.rb +5 -3
  33. data/lib/morpheus/cli/library_instance_types_command.rb +6 -2
  34. data/lib/morpheus/cli/library_layouts_command.rb +5 -3
  35. data/lib/morpheus/cli/library_option_lists_command.rb +5 -1
  36. data/lib/morpheus/cli/library_option_types_command.rb +5 -0
  37. data/lib/morpheus/cli/library_upgrades_command.rb +5 -3
  38. data/lib/morpheus/cli/license.rb +3 -0
  39. data/lib/morpheus/cli/load_balancers.rb +10 -1
  40. data/lib/morpheus/cli/mixins/processes_helper.rb +10 -4
  41. data/lib/morpheus/cli/monitoring_apps_command.rb +32 -26
  42. data/lib/morpheus/cli/monitoring_checks_command.rb +35 -27
  43. data/lib/morpheus/cli/monitoring_contacts_command.rb +19 -16
  44. data/lib/morpheus/cli/monitoring_groups_command.rb +32 -26
  45. data/lib/morpheus/cli/monitoring_incidents_command.rb +39 -30
  46. data/lib/morpheus/cli/network_domains_command.rb +5 -2
  47. data/lib/morpheus/cli/network_groups_command.rb +5 -2
  48. data/lib/morpheus/cli/network_pool_servers_command.rb +5 -2
  49. data/lib/morpheus/cli/network_pools_command.rb +5 -2
  50. data/lib/morpheus/cli/network_proxies_command.rb +5 -2
  51. data/lib/morpheus/cli/network_services_command.rb +1 -0
  52. data/lib/morpheus/cli/networks_command.rb +5 -2
  53. data/lib/morpheus/cli/packages_command.rb +6 -3
  54. data/lib/morpheus/cli/policies_command.rb +17 -3
  55. data/lib/morpheus/cli/power_schedules_command.rb +9 -1
  56. data/lib/morpheus/cli/preseed_scripts_command.rb +5 -2
  57. data/lib/morpheus/cli/processes_command.rb +17 -6
  58. data/lib/morpheus/cli/recent_activity_command.rb +1 -0
  59. data/lib/morpheus/cli/remote.rb +1 -0
  60. data/lib/morpheus/cli/roles.rb +14 -5
  61. data/lib/morpheus/cli/security_group_rules.rb +8 -4
  62. data/lib/morpheus/cli/security_groups.rb +4 -0
  63. data/lib/morpheus/cli/shell.rb +13 -23
  64. data/lib/morpheus/cli/storage_providers_command.rb +12 -6
  65. data/lib/morpheus/cli/tasks.rb +6 -0
  66. data/lib/morpheus/cli/tenants_command.rb +13 -9
  67. data/lib/morpheus/cli/user_groups_command.rb +7 -1
  68. data/lib/morpheus/cli/user_settings_command.rb +7 -1
  69. data/lib/morpheus/cli/user_sources_command.rb +10 -8
  70. data/lib/morpheus/cli/users.rb +7 -2
  71. data/lib/morpheus/cli/version.rb +1 -1
  72. data/lib/morpheus/cli/virtual_images.rb +8 -1
  73. data/lib/morpheus/cli/whoami.rb +34 -15
  74. data/lib/morpheus/cli/workflows.rb +5 -0
  75. data/lib/morpheus/formatters.rb +4 -0
  76. data/lib/morpheus/rest_client.rb +6 -1
  77. metadata +2 -4
  78. data/lib/morpheus/api/app_templates_interface.rb +0 -64
  79. data/lib/morpheus/cli/app_templates.rb +0 -2019
@@ -68,7 +68,7 @@ class Morpheus::Cli::Whoami
68
68
  opts.on('-t','--token-only', "Print your access token only") do
69
69
  access_token_only = true
70
70
  end
71
- build_common_options(opts, options, [:json, :remote, :dry_run])
71
+ build_common_options(opts, options, [:json, :remote, :dry_run, :quiet])
72
72
  end
73
73
  optparse.parse!(args)
74
74
  connect(options)
@@ -83,10 +83,14 @@ class Morpheus::Cli::Whoami
83
83
  end
84
84
 
85
85
  if options[:dry_run]
86
- print_dry_run @api_client.whoami.dry.get(params, options)
86
+ print_dry_run @api_client.whoami.setopts(options).dry.get(params)
87
87
  return 0
88
88
  end
89
89
 
90
+ # todo: fix potential issue here, should be able to use --remote-url or --username
91
+ # in which case you do not have to be logged in (saved credentials)...
92
+ # maybe just update connect() to do @api_client = establish_remote_appliance_connection(opts)
93
+ # wallet = Morpheus::Cli::Credentials.new(@appliance_name, @appliance_url).request_credentials(options)
90
94
  wallet = Morpheus::Cli::Credentials.new(@appliance_name, @appliance_url).load_saved_credentials()
91
95
  token = wallet ? wallet['access_token'] : nil
92
96
  if !token
@@ -102,16 +106,30 @@ class Morpheus::Cli::Whoami
102
106
  end
103
107
  end
104
108
 
105
- json_response = load_whoami()
106
-
107
- user = @current_user # from load_whoami() meh
109
+ #json_response = load_whoami()
110
+
111
+ whoami_interface = @api_client.whoami.setopts(options)
112
+ whoami_response = whoami_interface.get()
113
+ json_response = whoami_response
114
+ @current_user = whoami_response["user"]
115
+ if @current_user.empty?
116
+ print_red_alert "Unauthenticated. Please login."
117
+ exit 1
118
+ end
119
+ @is_master_account = whoami_response["isMasterAccount"]
120
+ @user_permissions = whoami_response["permissions"]
108
121
 
122
+ if whoami_response["appliance"]
123
+ @appliance_build_verison = whoami_response["appliance"]["buildVersion"]
124
+ else
125
+ @appliance_build_verison = nil
126
+ end
109
127
 
110
128
  if access_token_only
111
129
  if options[:quiet]
112
- return 1
130
+ return @current_user ? 0 : 1
113
131
  end
114
- if !@access_token
132
+ if @access_token.nil?
115
133
  print yellow,"\n","No access token. Please login",reset,"\n"
116
134
  return false
117
135
  end
@@ -121,14 +139,15 @@ class Morpheus::Cli::Whoami
121
139
 
122
140
  if username_only
123
141
  if options[:quiet]
124
- return 1
142
+ return @current_user ? 0 : 1
125
143
  end
126
- if !user
127
- puts "(logged out)" # "(anonymous)" || ""
144
+ if @current_user.nil?
145
+ puts_error "(logged out)" # "(anonymous)" || ""
128
146
  return 1
147
+ else
148
+ print cyan,@current_user['username'].to_s,reset,"\n"
149
+ return 0
129
150
  end
130
- print cyan,user['username'].to_s,reset,"\n"
131
- return 0
132
151
  end
133
152
 
134
153
 
@@ -145,7 +164,7 @@ class Morpheus::Cli::Whoami
145
164
  print JSON.pretty_generate(json_response)
146
165
  print "\n"
147
166
  else
148
- if !user
167
+ if @current_user.nil?
149
168
  print yellow,"\n","No active session. Please login",reset,"\n"
150
169
  exit 1
151
170
  end
@@ -162,7 +181,7 @@ class Morpheus::Cli::Whoami
162
181
  "Username" => 'username',
163
182
  "Email" => 'email',
164
183
  "Role" => lambda {|it| format_user_role_names(it) }
165
- }, user)
184
+ }, @current_user)
166
185
  print cyan
167
186
 
168
187
  if options[:include_feature_access]
@@ -211,7 +230,7 @@ class Morpheus::Cli::Whoami
211
230
  begin
212
231
  now = Time.now.to_i
213
232
  app_map = ::Morpheus::Cli::Remote.load_remote(@appliance_name)
214
- app_map[:username] = user['username']
233
+ app_map[:username] = @current_user['username']
215
234
  app_map[:authenticated] = true
216
235
  app_map[:status] = 'ready'
217
236
  app_map[:build_version] = @appliance_build_verison if @appliance_build_verison
@@ -38,6 +38,7 @@ class Morpheus::Cli::Workflows
38
38
  begin
39
39
  params = {}
40
40
  params.merge!(parse_list_options(options))
41
+ @task_sets_interface.setopts(options)
41
42
  if options[:dry_run]
42
43
  print_dry_run @task_sets_interface.dry.get(params)
43
44
  return
@@ -127,6 +128,7 @@ class Morpheus::Cli::Workflows
127
128
  payload['taskSet']['tasks'] = tasks
128
129
  end
129
130
  end
131
+ @task_sets_interface.setopts(options)
130
132
  if options[:dry_run]
131
133
  print_dry_run @task_sets_interface.dry.create(payload)
132
134
  return
@@ -166,6 +168,7 @@ class Morpheus::Cli::Workflows
166
168
  def _get(id, options)
167
169
  workflow_name = id
168
170
  begin
171
+ @task_sets_interface.setopts(options)
169
172
  if options[:dry_run]
170
173
  if workflow_name.to_s =~ /\A\d{1,}\Z/
171
174
  print_dry_run @task_sets_interface.dry.get(workflow_name.to_i)
@@ -288,6 +291,7 @@ class Morpheus::Cli::Workflows
288
291
  payload['taskSet']['tasks'] = tasks
289
292
  end
290
293
  end
294
+ @task_sets_interface.setopts(options)
291
295
  if options[:dry_run]
292
296
  print_dry_run @task_sets_interface.dry.update(workflow['id'], payload)
293
297
  return
@@ -324,6 +328,7 @@ class Morpheus::Cli::Workflows
324
328
  unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the workflow #{workflow['name']}?")
325
329
  exit 1
326
330
  end
331
+ @task_sets_interface.setopts(options)
327
332
  if options[:dry_run]
328
333
  print_dry_run @task_sets_interface.dry.destroy(workflow['id'])
329
334
  return
@@ -305,3 +305,7 @@ def format_bytes_short(bytes)
305
305
  end
306
306
  return out
307
307
  end
308
+
309
+ def no_colors(str)
310
+ str.to_s.gsub /\e\[\d+m/, ""
311
+ end
@@ -18,7 +18,12 @@ module Morpheus
18
18
  end
19
19
 
20
20
  def execute(options)
21
- opts = {timeout: 30}.merge(options)
21
+ default_opts = {}
22
+ # only read requests get default 30 second timeout.
23
+ if options[:method] == :get || options[:method] == :head
24
+ default_opts[:timeout] = 30
25
+ end
26
+ opts = default_opts.merge(options)
22
27
  unless ssl_verification_enabled?
23
28
  opts[:verify_ssl] = OpenSSL::SSL::VERIFY_NONE
24
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: morpheus-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.9
4
+ version: 3.6.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Estes
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2019-02-21 00:00:00.000000000 Z
14
+ date: 2019-02-22 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -173,7 +173,6 @@ files:
173
173
  - lib/morpheus/api/account_groups_interface.rb
174
174
  - lib/morpheus/api/accounts_interface.rb
175
175
  - lib/morpheus/api/api_client.rb
176
- - lib/morpheus/api/app_templates_interface.rb
177
176
  - lib/morpheus/api/apps_interface.rb
178
177
  - lib/morpheus/api/archive_buckets_interface.rb
179
178
  - lib/morpheus/api/archive_files_interface.rb
@@ -250,7 +249,6 @@ files:
250
249
  - lib/morpheus/cli/access_token_command.rb
251
250
  - lib/morpheus/cli/account_groups_command.rb
252
251
  - lib/morpheus/cli/accounts.rb
253
- - lib/morpheus/cli/app_templates.rb
254
252
  - lib/morpheus/cli/apps.rb
255
253
  - lib/morpheus/cli/archives_command.rb
256
254
  - lib/morpheus/cli/auth_command.rb
@@ -1,64 +0,0 @@
1
- require 'morpheus/api/api_client'
2
-
3
- class Morpheus::AppTemplatesInterface < Morpheus::APIClient
4
- def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
5
- @access_token = access_token
6
- @refresh_token = refresh_token
7
- @base_url = base_url
8
- @expires_at = expires_at
9
- end
10
-
11
- def get(id)
12
- raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
13
- url = "#{@base_url}/api/app-templates/#{id}"
14
- headers = { params: {}, authorization: "Bearer #{@access_token}" }
15
- execute(method: :get, url: url, headers: headers)
16
- end
17
-
18
- def list(params={})
19
- url = "#{@base_url}/api/app-templates"
20
- headers = { params: params, authorization: "Bearer #{@access_token}" }
21
- execute(method: :get, url: url, headers: headers)
22
- end
23
-
24
- def create(payload)
25
- url = "#{@base_url}/api/app-templates"
26
- headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
27
- execute(method: :post, url: url, headers: headers, payload: payload.to_json)
28
- end
29
-
30
- def update(id, payload)
31
- url = "#{@base_url}/api/app-templates/#{id}"
32
- headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
33
- execute(method: :put, url: url, headers: headers, payload: payload.to_json)
34
- end
35
-
36
- # multipart image upload
37
- def save_image(id, image_file, params={})
38
- url = "#{@base_url}/api/app-templates/#{id}/image"
39
- headers = { :params => params, :authorization => "Bearer #{@access_token}"}
40
- payload = {}
41
- payload[:templateImage] = image_file
42
- payload[:multipart] = true
43
- execute(method: :post, url: url, headers: headers, payload: payload)
44
- end
45
-
46
- def duplicate(id, payload)
47
- url = "#{@base_url}/api/app-templates/#{id}/duplicate"
48
- headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
49
- execute(method: :post, url: url, headers: headers, payload: payload.to_json)
50
- end
51
-
52
- def destroy(id)
53
- url = "#{@base_url}/api/app-templates/#{id}"
54
- headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
55
- execute(method: :delete, url: url, headers: headers)
56
- end
57
-
58
- def list_tiers(params={})
59
- url = "#{@base_url}/api/app-templates/tiers"
60
- headers = { params: params, authorization: "Bearer #{@access_token}" }
61
- execute(method: :get, url: url, headers: headers)
62
- end
63
-
64
- end
@@ -1,2019 +0,0 @@
1
- require 'io/console'
2
- require 'rest_client'
3
- require 'optparse'
4
- require 'morpheus/cli/cli_command'
5
- require 'morpheus/cli/option_types'
6
- require 'json'
7
-
8
- # deprecated and replaced with blueprints, hidden for now
9
- class Morpheus::Cli::AppTemplates
10
- include Morpheus::Cli::CliCommand
11
- include Morpheus::Cli::ProvisioningHelper
12
-
13
- set_command_hidden
14
-
15
- register_subcommands :list, :get, :add, :update, :remove
16
- register_subcommands :duplicate
17
- register_subcommands :'upload-image' => :upload_image
18
- register_subcommands :'available-tiers'
19
- register_subcommands :'add-tier', :'update-tier', :'remove-tier', :'connect-tiers', :'disconnect-tiers'
20
- register_subcommands :'add-instance'
21
- #register_subcommands :'update-instance'
22
- register_subcommands :'remove-instance'
23
- register_subcommands :'add-instance-config'
24
- #register_subcommands :'update-instance-config'
25
- register_subcommands :'remove-instance-config'
26
- # alias_subcommand :details, :get
27
- # set_default_subcommand :list
28
-
29
- def initialize()
30
- # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
31
- end
32
-
33
- def connect(opts)
34
- @api_client = establish_remote_appliance_connection(opts)
35
- @app_templates_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).app_templates
36
- @groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).groups
37
- @instances_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instances
38
- @instance_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
39
- @options_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).options
40
- @active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
41
- end
42
-
43
- def handle(args)
44
- handle_subcommand(args)
45
- end
46
-
47
- def list(args)
48
- options = {}
49
- optparse = Morpheus::Cli::OptionParser.new do |opts|
50
- opts.banner = subcommand_usage()
51
- build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :dry_run, :remote])
52
- opts.footer = "List app templates."
53
- end
54
- optparse.parse!(args)
55
- connect(options)
56
- begin
57
- params = {}
58
- [:phrase, :offset, :max, :sort, :direction].each do |k|
59
- params[k] = options[k] unless options[k].nil?
60
- end
61
- if options[:dry_run]
62
- print_dry_run @app_templates_interface.dry.list(params)
63
- return
64
- end
65
-
66
- json_response = @app_templates_interface.list(params)
67
- app_templates = json_response['appTemplates']
68
-
69
- if options[:json]
70
- puts as_json(json_response, options, "appTemplates")
71
- return 0
72
- elsif options[:csv]
73
- puts records_as_csv(json_response['appTemplates'], options)
74
- return 0
75
- elsif options[:yaml]
76
- puts as_yaml(json_response, options, "appTemplates")
77
- return 0
78
- end
79
-
80
-
81
- title = "Morpheus App Templates"
82
- subtitles = []
83
- if params[:phrase]
84
- subtitles << "Search: #{params[:phrase]}".strip
85
- end
86
- print_h1 title, subtitles
87
- if app_templates.empty?
88
- print cyan,"No app templates found.",reset,"\n"
89
- else
90
- print_app_templates_table(app_templates, options)
91
- print_results_pagination(json_response)
92
- end
93
- print reset,"\n"
94
- return 0
95
-
96
- rescue RestClient::Exception => e
97
- print_rest_exception(e, options)
98
- exit 1
99
- end
100
- end
101
-
102
- def get(args)
103
- options = {}
104
- optparse = Morpheus::Cli::OptionParser.new do |opts|
105
- opts.banner = subcommand_usage("[template]")
106
- opts.on( '-c', '--config', "Display raw config only. Default is YAML. Combine with -j for JSON instead." ) do
107
- options[:show_config] = true
108
- end
109
- build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
110
- opts.footer = "Get details about an app template.\n" +
111
- "[template] is required. This is the name or id of an app template."
112
- end
113
- optparse.parse!(args)
114
- if args.count < 1
115
- puts optparse
116
- exit 1
117
- end
118
- connect(options)
119
- begin
120
- if options[:dry_run]
121
- if args[0].to_s =~ /\A\d{1,}\Z/
122
- print_dry_run @app_templates_interface.dry.get(args[0].to_i)
123
- else
124
- print_dry_run @app_templates_interface.dry.list({name:args[0]})
125
- end
126
- return
127
- end
128
- app_template = find_app_template_by_name_or_id(args[0])
129
- exit 1 if app_template.nil?
130
-
131
- json_response = {'appTemplate' => app_template} # skip redundant request
132
- #json_response = @app_templates_interface.get(app_template['id'])
133
- app_template = json_response['appTemplate']
134
-
135
- if options[:show_config]
136
- #print_h2 "RAW"
137
- if options[:json]
138
- print cyan
139
- print "// JSON config for Morpheus App Template: #{app_template['name']}","\n"
140
- print reset
141
- puts as_json(app_template["config"])
142
- else
143
- print cyan
144
- print "# YAML config for Morpheus App Template: #{app_template['name']}","\n"
145
- print reset
146
- puts as_yaml(app_template["config"])
147
- end
148
- return 0
149
- end
150
-
151
- if options[:json]
152
- puts as_json(json_response, options, "appTemplate")
153
- return 0
154
- elsif options[:yaml]
155
- puts as_yaml(json_response, options, "appTemplate")
156
- return 0
157
- elsif options[:csv]
158
- puts records_as_csv([json_response['appTemplate']], options)
159
- return 0
160
- end
161
-
162
- print_h1 "App Template Details"
163
-
164
- print_app_template_details(app_template)
165
-
166
- print reset,"\n"
167
-
168
- rescue RestClient::Exception => e
169
- print_rest_exception(e, options)
170
- exit 1
171
- end
172
- end
173
-
174
- def add(args)
175
- options = {}
176
- optparse = Morpheus::Cli::OptionParser.new do |opts|
177
- opts.banner = subcommand_usage("[name] [options]")
178
- opts.on('--config JSON', String, "App Template Config JSON") do |val|
179
- options['config'] = JSON.parse(val.to_s)
180
- end
181
- opts.on('--config-yaml YAML', String, "App Template Config YAML") do |val|
182
- options['config'] = YAML.load(val.to_s)
183
- end
184
- opts.on('--config-file FILE', String, "App Template Config from a local JSON or YAML file") do |val|
185
- options['configFile'] = val.to_s
186
- end
187
- build_option_type_options(opts, options, add_app_template_option_types(false))
188
- build_common_options(opts, options, [:options, :json, :dry_run, :remote])
189
- opts.footer = "Create a new app template.\n" +
190
- "[name] is optional and can be passed as --name or inside the config instead."
191
- "[--config] or [--config-file] can be used to define the app template."
192
- end
193
- optparse.parse!(args)
194
- if args.count > 1
195
- print_error Morpheus::Terminal.angry_prompt
196
- puts_error "#{command_name} add expects 0-1 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
197
- return 1
198
- end
199
- options[:options] ||= {}
200
- if args[0] && !options[:options]['name']
201
- options[:options]['name'] = args[0]
202
- end
203
- connect(options)
204
- begin
205
- request_payload = nil
206
- config_payload = {}
207
- if options['config']
208
- config_payload = options['config']
209
- request_payload = config_payload
210
- elsif options['configFile']
211
- config_file = File.expand_path(options['configFile'])
212
- if !File.exists?(config_file) || !File.file?(config_file)
213
- print_red_alert "File not found: #{config_file}"
214
- return false
215
- end
216
- if config_file =~ /\.ya?ml\Z/
217
- config_payload = YAML.load_file(config_file)
218
- else
219
- config_payload = JSON.parse(File.read(config_file))
220
- end
221
- request_payload = config_payload
222
- else
223
- params = Morpheus::Cli::OptionTypes.prompt(add_app_template_option_types, options[:options], @api_client, options[:params])
224
- app_template_payload = params.select {|k,v| ['name', 'description', 'category'].include?(k) }
225
- # expects no namespace, just the config
226
- request_payload = app_template_payload
227
- end
228
-
229
- if options[:dry_run]
230
- print_dry_run @app_templates_interface.dry.create(request_payload)
231
- return
232
- end
233
-
234
- json_response = @app_templates_interface.create(request_payload)
235
-
236
- if options[:json]
237
- print JSON.pretty_generate(json_response)
238
- print "\n"
239
- else
240
- app_template = json_response["appTemplate"]
241
- print_green_success "Added app template #{app_template['name']}"
242
- if !options[:no_prompt]
243
- if ::Morpheus::Cli::OptionTypes::confirm("Would you like to add a tier now?", options.merge({default: false}))
244
- add_tier([app_template['id']])
245
- while ::Morpheus::Cli::OptionTypes::confirm("Add another tier?", options.merge({default: false})) do
246
- add_tier([app_template['id']])
247
- end
248
- else
249
- # print details
250
- get([app_template['id']])
251
- end
252
- end
253
- end
254
-
255
- rescue RestClient::Exception => e
256
- print_rest_exception(e, options)
257
- exit 1
258
- end
259
- end
260
-
261
- def update(args)
262
- options = {}
263
- optparse = Morpheus::Cli::OptionParser.new do |opts|
264
- opts.banner = subcommand_usage("[template] [options]")
265
- opts.on('--config JSON', String, "App Template Config JSON") do |val|
266
- options['config'] = JSON.parse(val.to_s)
267
- end
268
- opts.on('--config-yaml YAML', String, "App Template Config YAML") do |val|
269
- options['config'] = YAML.load(val.to_s)
270
- end
271
- opts.on('--config-file FILE', String, "App Template Config from a local JSON or YAML file") do |val|
272
- options['configFile'] = val.to_s
273
- end
274
- build_option_type_options(opts, options, update_app_template_option_types(false))
275
- build_common_options(opts, options, [:options, :json, :dry_run, :quiet, :remote])
276
- opts.footer = "Update an app template.\n" +
277
- "[template] is required. This is the name or id of an app template.\n" +
278
- "[options] Available options include --name and --description. This will update only the specified values.\n" +
279
- "[--config] or [--config-file] can be used to replace the entire app template."
280
- end
281
- optparse.parse!(args)
282
-
283
- if args.count < 1
284
- puts optparse
285
- exit 1
286
- end
287
-
288
- connect(options)
289
-
290
- begin
291
-
292
- app_template = find_app_template_by_name_or_id(args[0])
293
- exit 1 if app_template.nil?
294
-
295
- request_payload = nil
296
- config_payload = {}
297
- if options['config']
298
- config_payload = options['config']
299
- request_payload = config_payload
300
- elsif options['configFile']
301
- config_file = options['configFile']
302
- if !File.exists?(config_file)
303
- print_red_alert "File not found: #{config_file}"
304
- return false
305
- end
306
- if config_file =~ /\.ya?ml\Z/
307
- config_payload = YAML.load_file(config_file)
308
- else
309
- config_payload = JSON.parse(File.read(config_file))
310
- end
311
- request_payload = config_payload
312
- else
313
- # update just name,description,category
314
- # preserve all other attributes of the config..
315
-
316
- #params = Morpheus::Cli::OptionTypes.prompt(update_app_template_option_types, options[:options], @api_client, options[:params])
317
- params = options[:options] || {}
318
-
319
- if params.empty?
320
- # print_red_alert "Specify atleast one option to update"
321
- print_red_alert "Specify atleast one option to update.\nOr use --config or --config-file to replace the entire config."
322
- puts optparse
323
- exit 1
324
- end
325
-
326
- #puts "parsed params is : #{params.inspect}"
327
- app_template_payload = params.select {|k,v| ['name','description','category'].include?(k) }
328
- # expects no namespace, just the config
329
- # preserve all other attributes of the config.
330
- request_payload = app_template["config"].merge(app_template_payload)
331
- # todo maybe: put name, description and category at the front.
332
- # request_payload = app_template_payload.merge(app_template["config"].merge(app_template_payload))
333
- end
334
-
335
- if options[:dry_run]
336
- print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
337
- return
338
- end
339
-
340
- json_response = @app_templates_interface.update(app_template['id'], request_payload)
341
-
342
- if options[:json]
343
- print JSON.pretty_generate(json_response)
344
- print "\n"
345
- else
346
- unless options[:quiet]
347
- app_template = json_response['appTemplate']
348
- print_green_success "Updated app template #{app_template['name']}"
349
- details_options = [app_template['id']]
350
- get(details_options)
351
- end
352
- end
353
-
354
- rescue RestClient::Exception => e
355
- print_rest_exception(e, options)
356
- exit 1
357
- end
358
- end
359
-
360
-
361
- def upload_image(args)
362
- image_type_name = nil
363
- options = {}
364
- optparse = Morpheus::Cli::OptionParser.new do |opts|
365
- opts.banner = subcommand_usage("[template] [file]")
366
- build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
367
- opts.footer = "Upload an image file to be used as the icon for an app template.\n" +
368
- "[template] is required. This is the name or id of an app template.\n" +
369
- "[file] is required. This is the local path of a file to upload [png|jpg|svg]."
370
- end
371
- optparse.parse!(args)
372
- if args.count != 2
373
- print_error Morpheus::Terminal.angry_prompt
374
- puts_error "#{command_name} upload-image expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
375
- return 1
376
- end
377
- app_template_name = args[0]
378
- filename = File.expand_path(args[1].to_s)
379
- image_file = nil
380
- if filename && File.file?(filename)
381
- # maybe validate it's an image file? [.png|jpg|svg]
382
- image_file = File.new(filename, 'rb')
383
- else
384
- print_red_alert "File not found: #{filename}"
385
- # print_error Morpheus::Terminal.angry_prompt
386
- # puts_error "bad argument [file] - File not found: #{filename}\n#{optparse}"
387
- return 1
388
- end
389
- connect(options)
390
- begin
391
- app_template = find_app_template_by_name_or_id(app_template_name)
392
- exit 1 if app_template.nil?
393
- if options[:dry_run]
394
- print_dry_run @app_templates_interface.dry.save_image(app_template['id'], image_file)
395
- return 0
396
- end
397
- unless options[:quiet] || options[:json]
398
- print cyan, "Uploading file #{filename} ...", reset, "\n"
399
- end
400
- json_response = @app_templates_interface.save_image(app_template['id'], image_file)
401
- if options[:json]
402
- print JSON.pretty_generate(json_response)
403
- elsif !options[:quiet]
404
- app_template = json_response['appTemplate']
405
- new_image_url = app_template['image']
406
- print cyan, "Updated app template #{app_template['name']} image.\nNew image url is: #{new_image_url}", reset, "\n\n"
407
- get([app_template['id']])
408
- end
409
- return 0
410
- rescue RestClient::Exception => e
411
- print_rest_exception(e, options)
412
- return 1
413
- end
414
- end
415
-
416
- def duplicate(args)
417
- options = {}
418
- optparse = Morpheus::Cli::OptionParser.new do |opts|
419
- opts.banner = subcommand_usage("[template] [new name]")
420
- build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
421
- opts.footer = "Duplicate an app template." + "\n" +
422
- "[template] is required. This is the name or id of an app template." + "\n" +
423
- "[new name] is required. This is the name for the clone."
424
- end
425
- optparse.parse!(args)
426
-
427
- if args.count < 1
428
- puts optparse
429
- exit 1
430
- end
431
-
432
- request_payload = {"appTemplate" => {}}
433
- if args[1]
434
- request_payload["appTemplate"]["name"] = args[1]
435
- end
436
-
437
- connect(options)
438
- begin
439
- app_template = find_app_template_by_name_or_id(args[0])
440
- exit 1 if app_template.nil?
441
- # unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to duplicate the app template #{app_template['name']}?")
442
- # exit
443
- # end
444
- if options[:dry_run]
445
- print_dry_run @app_templates_interface.dry.duplicate(app_template['id'], request_payload)
446
- return
447
- end
448
- json_response = @app_templates_interface.duplicate(app_template['id'], request_payload)
449
-
450
- if options[:json]
451
- print JSON.pretty_generate(json_response)
452
- print "\n"
453
- else
454
- new_app_template = json_response["appTemplate"] || {}
455
- print_green_success "Created duplicate app template '#{new_app_template['name']}'"
456
- #get([new_app_template["id"]])
457
- end
458
-
459
- rescue RestClient::Exception => e
460
- print_rest_exception(e, options)
461
- exit 1
462
- end
463
- end
464
-
465
- def remove(args)
466
- options = {}
467
- optparse = Morpheus::Cli::OptionParser.new do |opts|
468
- opts.banner = subcommand_usage("[template]")
469
- build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
470
- opts.footer = "Delete an app template." + "\n" +
471
- "[template] is required. This is the name or id of an app template."
472
- end
473
- optparse.parse!(args)
474
-
475
- if args.count < 1
476
- puts optparse
477
- exit 1
478
- end
479
-
480
- connect(options)
481
- begin
482
- app_template = find_app_template_by_name_or_id(args[0])
483
- exit 1 if app_template.nil?
484
- unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the app template #{app_template['name']}?")
485
- exit
486
- end
487
- if options[:dry_run]
488
- print_dry_run @app_templates_interface.dry.destroy(app_template['id'])
489
- return
490
- end
491
- json_response = @app_templates_interface.destroy(app_template['id'])
492
-
493
- if options[:json]
494
- print JSON.pretty_generate(json_response)
495
- print "\n"
496
- else
497
- print_green_success "Removed app template #{app_template['name']}"
498
- end
499
-
500
- rescue RestClient::Exception => e
501
- print_rest_exception(e, options)
502
- exit 1
503
- end
504
- end
505
-
506
- def add_instance(args)
507
- options = {}
508
- optparse = Morpheus::Cli::OptionParser.new do |opts|
509
- opts.banner = subcommand_usage("[template] [tier] [instance-type]")
510
- # opts.on( '-g', '--group GROUP', "Group" ) do |val|
511
- # options[:group] = val
512
- # end
513
- # opts.on( '-c', '--cloud CLOUD', "Cloud" ) do |val|
514
- # options[:cloud] = val
515
- # end
516
- opts.on('--name VALUE', String, "Instance Name") do |val|
517
- options[:instance_name] = val
518
- end
519
- build_common_options(opts, options, [:options, :json, :dry_run, :remote])
520
- opts.footer = "Update an app template, adding an instance." + "\n" +
521
- "[template] is required. This is the name or id of an app template." + "\n" +
522
- "[tier] is required and will be prompted for. This is the name of the tier." + "\n" +
523
- "[instance-type] is required and will be prompted for. This is the type of instance."
524
- end
525
- optparse.parse!(args)
526
-
527
- if args.count < 1
528
- print_error Morpheus::Terminal.angry_prompt
529
- puts_error "#{command_name} add-instance expects 3 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
530
- return 1
531
- end
532
-
533
- connect(options)
534
-
535
- begin
536
- app_template_name = args[0]
537
- tier_name = args[1]
538
- instance_type_code = args[2]
539
- # we also need consider when there is multiple instances of the same type in
540
- # a template/tier.. so maybe split instance_type_code as [type-code]:[index].. or...errr
541
-
542
- app_template = find_app_template_by_name_or_id(app_template_name)
543
- return 1 if app_template.nil?
544
-
545
- if !tier_name
546
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tierName', 'fieldLabel' => 'Tier Name', 'type' => 'text', 'required' => true, 'description' => 'Enter the name of the tier'}], options[:options])
547
- tier_name = v_prompt['tierName']
548
- end
549
-
550
- if !instance_type_code
551
- instance_type_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'type' => 'select', 'fieldLabel' => 'Type', 'optionSource' => 'instanceTypes', 'required' => true, 'description' => 'Select Instance Type.'}],options[:options],api_client,{})
552
- instance_type_code = instance_type_prompt['type']
553
- end
554
- instance_type = find_instance_type_by_code(instance_type_code)
555
- return 1 if instance_type.nil?
556
-
557
- tier_config = nil
558
- instance_config = nil
559
-
560
- app_template["config"] ||= {}
561
- tiers = app_template["config"]["tiers"]
562
- tiers ||= {}
563
- # tier identified by name, case sensitive...
564
- if !tiers[tier_name]
565
- tiers[tier_name] = {}
566
- end
567
- tier_config = tiers[tier_name]
568
-
569
- tier_config['instances'] ||= []
570
- instance_config = tier_config['instances'].find {|it| it["instance"] && it["instance"]["type"] && it["instance"]["type"] == instance_type["code"] }
571
- if !instance_config
572
- instance_config = {'instance' => {'type' => instance_type['code']} }
573
- tier_config['instances'].push(instance_config)
574
- end
575
- instance_config['instance'] ||= {}
576
-
577
- # just prompts for Instance Name (optional)
578
- instance_name = nil
579
- if options[:instance_name]
580
- instance_name = options[:instance_name]
581
- else
582
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Instance Name', 'type' => 'text', 'defaultValue' => instance_config['instance']['name']}])
583
- instance_name = v_prompt['name'] || ''
584
- end
585
-
586
- if instance_name
587
- if instance_name.to_s == 'null'
588
- instance_config['instance'].delete('name')
589
- # instance_config['instance']['name'] = ''
590
- else
591
- instance_config['instance']['name'] = instance_name
592
- end
593
- end
594
-
595
- # ok, make api request
596
- app_template["config"]["tiers"] = tiers
597
- request_payload = {appTemplate: app_template}
598
-
599
- if options[:dry_run]
600
- print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
601
- return 0
602
- end
603
-
604
- json_response = @app_templates_interface.update(app_template['id'], request_payload)
605
-
606
- if options[:json]
607
- puts JSON.pretty_generate(json_response)
608
- elsif !options[:quiet]
609
- print_green_success "Instance added to app template #{app_template['name']}"
610
- # prompt for new instance
611
- if !options[:no_prompt]
612
- if ::Morpheus::Cli::OptionTypes::confirm("Would you like to add a config now?", options.merge({default: true}))
613
- # todo: this needs to work by index, because you can have multiple instances of the same type
614
- add_instance_config([app_template['id'], tier_name, instance_type['code']])
615
- while ::Morpheus::Cli::OptionTypes::confirm("Add another config?", options.merge({default: false})) do
616
- add_instance_config([app_template['id'], tier_name, instance_type['code']])
617
- end
618
- else
619
- # print details
620
- get([app_template['name']])
621
- end
622
- end
623
- end
624
- return 0
625
-
626
- rescue RestClient::Exception => e
627
- print_rest_exception(e, options)
628
- return 1
629
- end
630
-
631
- end
632
-
633
- def add_instance_config(args)
634
- options = {}
635
- optparse = Morpheus::Cli::OptionParser.new do |opts|
636
- opts.banner = subcommand_usage("[template] [tier] [instance]")
637
- opts.on( '-g', '--group GROUP', "Group" ) do |val|
638
- options[:group] = val
639
- end
640
- opts.on( '-c', '--cloud CLOUD', "Cloud" ) do |val|
641
- options[:cloud] = val
642
- end
643
- opts.on( '-e', '--env ENVIRONMENT', "Environment" ) do |val|
644
- options[:environment] = val
645
- end
646
- opts.on('--name VALUE', String, "Instance Name") do |val|
647
- options[:instance_name] = val
648
- end
649
- build_common_options(opts, options, [:options, :json, :dry_run, :remote])
650
- opts.footer = "Update an app template, adding an instance config." + "\n" +
651
- "[template] is required. This is the name or id of an app template." + "\n" +
652
- "[tier] is required. This is the name of the tier." + "\n" +
653
- "[instance] is required. This is the type of instance."
654
- end
655
- optparse.parse!(args)
656
-
657
- if args.count < 3
658
- print_error Morpheus::Terminal.angry_prompt
659
- puts_error "Wrong number of arguments"
660
- puts_error optparse
661
- return 1
662
- end
663
-
664
- connect(options)
665
-
666
- begin
667
-
668
- app_template_name = args[0]
669
- tier_name = args[1]
670
- instance_type_code = args[2]
671
- # we also need consider when there is multiple instances of the same type in
672
- # a template/tier.. so maybe split instance_type_code as [type-code]:[index].. or...errr
673
-
674
- app_template = find_app_template_by_name_or_id(app_template_name)
675
- return 1 if app_template.nil?
676
-
677
- instance_type = find_instance_type_by_code(instance_type_code)
678
- return 1 if instance_type.nil?
679
-
680
- tier_config = nil
681
- instance_config = nil
682
-
683
- app_template["config"] ||= {}
684
- tiers = app_template["config"]["tiers"]
685
- tiers ||= {}
686
- # tier identified by name, case sensitive...
687
- if !tiers[tier_name]
688
- tiers[tier_name] = {}
689
- end
690
- tier_config = tiers[tier_name]
691
-
692
- tier_config['instances'] ||= []
693
- instance_config = tier_config['instances'].find {|it| it["instance"] && it["instance"]["type"] && it["instance"]["type"] == instance_type["code"] }
694
- if !instance_config
695
- instance_config = {'instance' => {'type' => instance_type['code']} }
696
- tier_config['instances'].push(instance_config)
697
- end
698
- instance_config['instance'] ||= {}
699
-
700
- # group prompt
701
-
702
- # use active group by default
703
- options[:group] ||= @active_group_id
704
-
705
-
706
- # available_groups = get_available_groups()
707
- # group_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'selectOptions' => get_available_groups(), 'required' => true, 'defaultValue' => @active_group_id}],options[:options],@api_client,{})
708
-
709
- # group_id = group_prompt['group']
710
- # the_group = find_group_by_name_or_id_for_provisioning(group_id)
711
-
712
- # # cloud prompt
713
- # cloud_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'cloud', 'type' => 'select', 'fieldLabel' => 'Cloud', 'optionSource' => 'clouds', 'required' => true, 'description' => 'Select Cloud.'}],options[:options],@api_client,{groupId: group_id})
714
- # cloud_id = cloud_prompt['cloud']
715
-
716
- # look for existing config for group + cloud
717
-
718
- options[:name_required] = false
719
- options[:instance_type_code] = instance_type["code"]
720
-
721
- #options[:options].deep_merge!(specific_config)
722
- # this provisioning helper method handles all (most) of the parsing and prompting
723
- instance_config_payload = prompt_new_instance(options)
724
-
725
- # strip all empty string and nil, would be problematic for update()
726
- instance_config_payload.deep_compact!
727
-
728
- # puts "INSTANCE CONFIG YAML:"
729
- # puts as_yaml(instance_config_payload)
730
-
731
- selected_environment = instance_config_payload.delete('instanceContext') || instance_config_payload.delete('environment')
732
- # groom provision instance payload for template purposes
733
- selected_cloud_id = instance_config_payload.delete('zoneId')
734
- selected_site = instance_config_payload['instance'].delete('site')
735
- selected_site_id = selected_site['id']
736
-
737
- selected_group = find_group_by_name_or_id_for_provisioning(selected_site_id)
738
- selected_cloud = find_cloud_by_name_or_id_for_provisioning(selected_group['id'], selected_cloud_id)
739
-
740
- # store config in environments => env => groups => groupname => clouds => cloudname =>
741
- current_config = instance_config
742
- if selected_environment.to_s != ''
743
- instance_config['environments'] ||= {}
744
- instance_config['environments'][selected_environment] ||= {}
745
- current_config = instance_config['environments'][selected_environment]
746
- end
747
-
748
- current_config['groups'] ||= {}
749
- current_config['groups'][selected_group['name']] ||= {}
750
- current_config['groups'][selected_group['name']]['clouds'] ||= {}
751
- current_config['groups'][selected_group['name']]['clouds'][selected_cloud['name']] = instance_config_payload
752
-
753
- # ok, make api request
754
- app_template["config"]["tiers"] = tiers
755
- request_payload = {appTemplate: app_template}
756
-
757
- if options[:dry_run]
758
- print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
759
- return 0
760
- end
761
-
762
- json_response = @app_templates_interface.update(app_template['id'], request_payload)
763
-
764
- if options[:json]
765
- puts JSON.pretty_generate(json_response)
766
- else
767
- print_green_success "Instance added to app template #{app_template['name']}"
768
- get([app_template['name']])
769
- end
770
- return 0
771
-
772
- rescue RestClient::Exception => e
773
- print_rest_exception(e, options)
774
- return 1
775
- end
776
-
777
- end
778
-
779
- def remove_instance_config(args)
780
- instance_index = nil
781
- options = {}
782
- optparse = Morpheus::Cli::OptionParser.new do |opts|
783
- opts.banner = subcommand_usage("[template] [tier] [instance] -g GROUP -c CLOUD")
784
- opts.on( '-g', '--group GROUP', "Group" ) do |val|
785
- options[:group] = val
786
- end
787
- opts.on( '-c', '--cloud CLOUD', "Cloud" ) do |val|
788
- options[:cloud] = val
789
- end
790
- opts.on( '-e', '--env ENV', "Environment" ) do |val|
791
- options[:environment] = val
792
- end
793
- # opts.on( nil, '--index NUMBER', "The index of the instance to remove, starting with 0." ) do |val|
794
- # instance_index = val.to_i
795
- # end
796
- build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
797
- opts.footer = "Update an app template, removing a specified instance config." + "\n" +
798
- "[template] is required. This is the name or id of an app template." + "\n" +
799
- "[tier] is required. This is the name of the tier." + "\n" +
800
- "[instance] is required. This is the type of instance." + "\n" +
801
- "The config scope is specified with the -g GROUP, -c CLOUD and -e ENV. The -g and -c options are required."
802
- end
803
- optparse.parse!(args)
804
-
805
- if args.count < 3
806
- print_error Morpheus::Terminal.angry_prompt
807
- puts_error "Wrong number of arguments"
808
- puts_error optparse
809
- return 1
810
- end
811
- if !options[:group]
812
- print_error Morpheus::Terminal.angry_prompt
813
- puts_error "Missing required argument -g GROUP"
814
- puts_error optparse
815
- return 1
816
- end
817
- if !options[:cloud]
818
- print_error Morpheus::Terminal.angry_prompt
819
- puts_error "Missing required argument -g CLOUD"
820
- puts_error optparse
821
- return 1
822
- end
823
- connect(options)
824
-
825
- begin
826
-
827
- app_template_name = args[0]
828
- tier_name = args[1]
829
- instance_type_code = args[2]
830
- # we also need consider when there is multiple instances of the same type in
831
- # a template/tier.. so maybe split instance_type_code as [type-code]:[index].. or...errr
832
-
833
- app_template = find_app_template_by_name_or_id(app_template_name)
834
- return 1 if app_template.nil?
835
-
836
- instance_type = find_instance_type_by_code(instance_type_code)
837
- return 1 if instance_type.nil?
838
-
839
- tier_config = nil
840
- # instance_config = nil
841
-
842
- app_template["config"] ||= {}
843
- tiers = app_template["config"]["tiers"]
844
- tiers ||= {}
845
- # tier identified by name, case sensitive...
846
- if !tiers[tier_name]
847
- print_red_alert "Tier not found by name #{tier_name}"
848
- return 1
849
- end
850
- tier_config = tiers[tier_name]
851
-
852
- if !tier_config
853
- print_red_alert "Tier not found by name #{tier1_name}!"
854
- return 1
855
- elsif tier_config['instances'].nil? || tier_config['instances'].empty?
856
- print_red_alert "Tier #{tier_name} is empty!"
857
- return 1
858
- end
859
-
860
- matching_indices = []
861
- if tier_config['instances']
862
- if instance_index
863
- matching_indices = [instance_index].compact
864
- else
865
- tier_config['instances'].each_with_index do |instance_config, index|
866
- is_match = instance_config['instance'] && instance_config['instance']['type'] == instance_type['code']
867
- if is_match
868
- matching_indices << index
869
- end
870
- end
871
- end
872
- end
873
-
874
- if matching_indices.size == 0
875
- print_red_alert "Instance not found by tier: #{tier_name}, type: #{instance_type_code}"
876
- return 1
877
- elsif matching_indices.size > 1
878
- #print_error Morpheus::Terminal.angry_prompt
879
- print_red_alert "More than one instance found by tier: #{tier_name}, type: #{instance_type_code}"
880
- puts_error "Try using the --index option to identify the instance you wish to remove."
881
- puts_error optparse
882
- return 1
883
- end
884
-
885
- # ok, find the specified config
886
- instance_config = tier_config['instances'][matching_indices[0]]
887
- parent_config = nil
888
- current_config = instance_config
889
- delete_key = nil
890
-
891
- config_description = "type: #{instance_type['code']}"
892
- config_description << " environment: #{options[:environment]}" if options[:environment]
893
- config_description << " group: #{options[:group]}" if options[:group]
894
- config_description << " cloud: #{options[:cloud]}" if options[:cloud]
895
- config_description = config_description.strip
896
-
897
-
898
- # find config in environments => env => groups => groupname => clouds => cloudname =>
899
- if options[:environment]
900
- if current_config && current_config['environments'] && current_config['environments'][options[:environment]]
901
- parent_config = current_config['environments']
902
- delete_key = options[:environment]
903
- current_config = parent_config[delete_key]
904
- else
905
- print_red_alert "Instance config not found for scope #{config_description}"
906
- return 1
907
- end
908
- end
909
- if options[:group]
910
- if current_config && current_config['groups'] && current_config['groups'][options[:group]]
911
- parent_config = current_config['groups']
912
- delete_key = options[:group]
913
- current_config = parent_config[delete_key]
914
- else
915
- print_red_alert "Instance config not found for scope #{config_description}"
916
- return 1
917
- end
918
- end
919
- if options[:cloud]
920
- if current_config && current_config['clouds'] && current_config['clouds'][options[:cloud]]
921
- parent_config = current_config['clouds']
922
- delete_key = options[:cloud]
923
- current_config = parent_config[delete_key]
924
- else
925
- print_red_alert "Instance config not found for scope #{config_description}"
926
- return 1
927
- end
928
- end
929
-
930
- # remove it
931
- parent_config.delete(delete_key)
932
-
933
- unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete this instance config #{config_description} ?")
934
- return 9
935
- end
936
-
937
- # ok, make api request
938
- app_template["config"]["tiers"] = tiers
939
- request_payload = {appTemplate: app_template}
940
-
941
- if options[:dry_run]
942
- print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
943
- return
944
- end
945
- json_response = @app_templates_interface.update(app_template['id'], request_payload)
946
-
947
- if options[:json]
948
- puts JSON.pretty_generate(json_response)
949
- else
950
- print_green_success "Removed instance from app template."
951
- get([app_template['id']])
952
- end
953
- return 0
954
-
955
- rescue RestClient::Exception => e
956
- print_rest_exception(e, options)
957
- exit 1
958
- end
959
- end
960
-
961
- def update_instance(args)
962
- print_red_alert "NOT YET SUPPORTED"
963
- return 5
964
- end
965
-
966
- def update_instance_config(args)
967
- print_red_alert "NOT YET SUPPORTED"
968
- return 5
969
- end
970
-
971
- def remove_instance(args)
972
- instance_index = nil
973
- options = {}
974
- optparse = Morpheus::Cli::OptionParser.new do |opts|
975
- opts.banner = subcommand_usage("[template] [tier] [instance]")
976
- # opts.on('--index NUMBER', Number, "Identify Instance by index within tier, starting with 0." ) do |val|
977
- # instance_index = val.to_i
978
- # end
979
- build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
980
- end
981
- optparse.parse!(args)
982
-
983
- if args.count < 3
984
- print_error Morpheus::Terminal.angry_prompt
985
- puts_error "Wrong number of arguments"
986
- puts_error optparse
987
- return 1
988
- end
989
-
990
- connect(options)
991
-
992
- begin
993
-
994
- app_template_name = args[0]
995
- tier_name = args[1]
996
- instance_identier = args[2]
997
-
998
- # instance_type_code = args[2]
999
- # we also need consider when there is multiple instances of the same type in
1000
- # a template/tier.. so maybe split instance_type_code as [type-code]:[index].. or...errr
1001
-
1002
- app_template = find_app_template_by_name_or_id(app_template_name)
1003
- return 1 if app_template.nil?
1004
-
1005
- # instance_type = find_instance_type_by_code(instance_type_code)
1006
- # return 1 if instance_type.nil?
1007
-
1008
- tier_config = nil
1009
- # instance_config = nil
1010
-
1011
- app_template["config"] ||= {}
1012
- tiers = app_template["config"]["tiers"]
1013
- tiers ||= {}
1014
- # tier identified by name, case sensitive...
1015
- if !tiers[tier_name]
1016
- print_red_alert "Tier not found by name #{tier_name}"
1017
- return 1
1018
- end
1019
- tier_config = tiers[tier_name]
1020
-
1021
- if tier_config['instances'].nil? || tier_config['instances'].empty?
1022
- print_red_alert "Tier #{tier_name} is empty!"
1023
- return 1
1024
- end
1025
-
1026
- # find instance
1027
- matching_indices = []
1028
- if tier_config['instances']
1029
- if instance_identier.to_s =~ /\A\d{1,}\Z/
1030
- matching_indices = [instance_identier.to_i].compact
1031
- else
1032
- tier_config['instances'].each_with_index do |instance_config, index|
1033
- if instance_config['instance'] && instance_config['instance']['type'] == instance_identier
1034
- matching_indices << index
1035
- elsif instance_config['instance'] && instance_config['instance']['name'] == instance_identier
1036
- matching_indices << index
1037
- end
1038
- end
1039
- end
1040
- end
1041
- if matching_indices.size == 0
1042
- print_red_alert "Instance not found by tier: #{tier_name}, instance: #{instance_identier}"
1043
- return 1
1044
- elsif matching_indices.size > 1
1045
- #print_error Morpheus::Terminal.angry_prompt
1046
- print_red_alert "More than one instance matched tier: #{tier_name}, instance: #{instance_identier}"
1047
- puts_error "Instance can be identified type, name or index within the tier."
1048
- puts_error optparse
1049
- return 1
1050
- end
1051
-
1052
- # remove it
1053
- tier_config['instances'].delete_at(matching_indices[0])
1054
-
1055
- unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete this instance #{instance_type_code} instance from tier: #{tier_name}?")
1056
- return 9
1057
- end
1058
-
1059
- # ok, make api request
1060
- app_template["config"]["tiers"] = tiers
1061
- request_payload = {appTemplate: app_template}
1062
-
1063
- if options[:dry_run]
1064
- print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
1065
- return
1066
- end
1067
- json_response = @app_templates_interface.update(app_template['id'], request_payload)
1068
-
1069
- if options[:json]
1070
- puts JSON.pretty_generate(json_response)
1071
- else
1072
- print_green_success "Removed instance from app template."
1073
- get([app_template['id']])
1074
- end
1075
- return 0
1076
-
1077
- rescue RestClient::Exception => e
1078
- print_rest_exception(e, options)
1079
- exit 1
1080
- end
1081
- end
1082
-
1083
- def add_tier(args)
1084
- options = {}
1085
- boot_order = nil
1086
- linked_tiers = nil
1087
- optparse = Morpheus::Cli::OptionParser.new do |opts|
1088
- opts.banner = subcommand_usage("[template] [tier]")
1089
- opts.on('--name VALUE', String, "Tier Name") do |val|
1090
- options[:name] = val
1091
- end
1092
- opts.on('--bootOrder NUMBER', String, "Boot Order" ) do |val|
1093
- boot_order = val
1094
- end
1095
- opts.on('--linkedTiers x,y,z', Array, "Connected Tiers.") do |val|
1096
- linked_tiers = val
1097
- end
1098
- build_common_options(opts, options, [:options, :json, :dry_run, :remote])
1099
- end
1100
- optparse.parse!(args)
1101
-
1102
- if args.count < 1
1103
- print_error Morpheus::Terminal.angry_prompt
1104
- puts_error "#{command_name} add-tier requires argument: [template]\n#{optparse}"
1105
- # puts optparse
1106
- return 1
1107
- end
1108
- app_template_name = args[0]
1109
- tier_name = args[1]
1110
-
1111
- connect(options)
1112
-
1113
- begin
1114
- app_template = find_app_template_by_name_or_id(app_template_name)
1115
- return 1 if app_template.nil?
1116
-
1117
- app_template["config"] ||= {}
1118
- app_template["config"]["tiers"] ||= {}
1119
- tiers = app_template["config"]["tiers"]
1120
-
1121
- # prompt new tier
1122
- # Name
1123
- # {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1, 'description' => 'A unique name for the app template.'},
1124
- # {'fieldName' => 'bootOrder', 'fieldLabel' => 'Boot Order', 'type' => 'text', 'required' => false, 'displayOrder' => 2, 'description' => 'Boot Order'}
1125
- if !tier_name
1126
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Tier Name', 'type' => 'text', 'required' => true, 'description' => 'Enter the name of the tier'}], options[:options])
1127
- tier_name = v_prompt['name']
1128
- end
1129
- # case insensitive match
1130
- existing_tier_names = tiers.keys
1131
- matching_tier_name = existing_tier_names.find {|k| k.downcase == tier_name.downcase }
1132
- if matching_tier_name
1133
- # print_red_alert "Tier #{tier_name} already exists"
1134
- # return 1
1135
- print cyan,"Tier #{tier_name} already exists.",reset,"\n"
1136
- return 0
1137
- end
1138
- # idempotent
1139
- if !tiers[tier_name]
1140
- tiers[tier_name] = {'instances' => []}
1141
- end
1142
- tier = tiers[tier_name]
1143
-
1144
- # Boot Order
1145
- if !boot_order
1146
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'bootOrder', 'fieldLabel' => 'Boot Order', 'type' => 'text', 'required' => false, 'description' => 'Sequence order for starting app instances by tier. 0-N', 'defaultValue' => tier['bootOrder']}], options[:options])
1147
- boot_order = v_prompt['bootOrder']
1148
- end
1149
- if boot_order.to_s == 'null'
1150
- tier.delete('bootOrder')
1151
- elsif boot_order.to_s != ''
1152
- tier['bootOrder'] = boot_order.to_i
1153
- end
1154
-
1155
- # Connected Tiers
1156
- if !linked_tiers
1157
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'linkedTiers', 'fieldLabel' => 'Connected Tiers', 'type' => 'text', 'required' => false, 'description' => 'Names of connected tiers, comma separated', 'defaultValue' => (linked_tiers ? linked_tiers.join(',') : nil)}], options[:options])
1158
- linked_tiers = v_prompt['linkedTiers'].to_s.split(',').collect {|it| it.strip }.select {|it| it != ''}
1159
- end
1160
- if linked_tiers && !linked_tiers.empty?
1161
- linked_tiers.each do |other_tier_name|
1162
- link_result = link_tiers(tiers, [tier_name, other_tier_name])
1163
- # could just re-prompt unless options[:no_prompt]
1164
- return 1 if !link_result
1165
- end
1166
- end
1167
-
1168
- # ok, make api request
1169
- app_template["config"]["tiers"] = tiers
1170
- request_payload = app_template["config"]
1171
- # request_payload = {appTemplate: app_template}
1172
-
1173
- if options[:dry_run]
1174
- print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
1175
- return
1176
- end
1177
- json_response = @app_templates_interface.update(app_template['id'], request_payload)
1178
-
1179
- if options[:json]
1180
- puts JSON.pretty_generate(json_response)
1181
- elsif !options[:quiet]
1182
- print_green_success "Added tier #{tier_name}"
1183
- # prompt for new instance
1184
- if !options[:no_prompt]
1185
- if ::Morpheus::Cli::OptionTypes::confirm("Would you like to add an instance now?", options.merge({default: true}))
1186
- add_instance([app_template['id'], tier_name])
1187
- while ::Morpheus::Cli::OptionTypes::confirm("Add another instance now?", options.merge({default: false})) do
1188
- add_instance([app_template['id'], tier_name])
1189
- end
1190
- # if !add_instance_result
1191
- # end
1192
- end
1193
- end
1194
- # print details
1195
- get([app_template['name']])
1196
- end
1197
- return 0
1198
- rescue RestClient::Exception => e
1199
- print_rest_exception(e, options)
1200
- exit 1
1201
- end
1202
- end
1203
-
1204
- def update_tier(args)
1205
- options = {}
1206
- new_tier_name = nil
1207
- boot_order = nil
1208
- linked_tiers = nil
1209
- optparse = Morpheus::Cli::OptionParser.new do |opts|
1210
- opts.banner = subcommand_usage("[template] [tier]")
1211
- opts.on('--name VALUE', String, "Tier Name") do |val|
1212
- new_tier_name = val
1213
- end
1214
- opts.on('--bootOrder NUMBER', String, "Boot Order" ) do |val|
1215
- boot_order = val
1216
- end
1217
- opts.on('--linkedTiers x,y,z', Array, "Connected Tiers") do |val|
1218
- linked_tiers = val
1219
- end
1220
- build_common_options(opts, options, [:options, :json, :dry_run, :remote])
1221
- end
1222
- optparse.parse!(args)
1223
-
1224
- if args.count != 2
1225
- print_error Morpheus::Terminal.angry_prompt
1226
- puts_error "#{command_name} update-tier expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
1227
- return 1
1228
- end
1229
- app_template_name = args[0]
1230
- tier_name = args[1]
1231
-
1232
- connect(options)
1233
-
1234
- begin
1235
- app_template = find_app_template_by_name_or_id(app_template_name)
1236
- return 1 if app_template.nil?
1237
-
1238
- app_template["config"] ||= {}
1239
- app_template["config"]["tiers"] ||= {}
1240
- tiers = app_template["config"]["tiers"]
1241
-
1242
- if !tiers[tier_name]
1243
- print_red_alert "Tier not found by name #{tier_name}"
1244
- return 1
1245
- end
1246
- tier = tiers[tier_name]
1247
-
1248
-
1249
- if options[:no_prompt]
1250
- if !(new_tier_name || boot_order || linked_tiers)
1251
- print_error Morpheus::Terminal.angry_prompt
1252
- puts_error "#{command_name} update-tier requires an option to update.\n#{optparse}"
1253
- return 1
1254
- end
1255
- end
1256
-
1257
- # prompt update tier
1258
- # Name
1259
- if !new_tier_name
1260
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Tier Name', 'type' => 'text', 'required' => true, 'description' => 'Rename the tier', 'defaultValue' => tier_name}], options[:options])
1261
- new_tier_name = v_prompt['name']
1262
- end
1263
- if new_tier_name && new_tier_name != tier_name
1264
- old_tier_name = tier_name
1265
- if tiers[new_tier_name]
1266
- print_red_alert "A tier named #{tier_name} already exists."
1267
- return 1
1268
- end
1269
- tier = tiers.delete(tier_name)
1270
- tiers[new_tier_name] = tier
1271
- # Need to fix all the linkedTiers
1272
- tiers.each do |k, v|
1273
- if v['linkedTiers'] && v['linkedTiers'].include?(tier_name)
1274
- v['linkedTiers'] = v['linkedTiers'].map {|it| it == tier_name ? new_tier_name : it }
1275
- end
1276
- end
1277
- # old_tier_name = tier_name
1278
- tier_name = new_tier_name
1279
- end
1280
-
1281
- # Boot Order
1282
- if !boot_order
1283
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'bootOrder', 'fieldLabel' => 'Boot Order', 'type' => 'text', 'required' => false, 'description' => 'Sequence order for starting app instances by tier. 0-N', 'defaultValue' => tier['bootOrder']}], options[:options])
1284
- boot_order = v_prompt['bootOrder']
1285
- end
1286
- if boot_order.to_s == 'null'
1287
- tier.delete('bootOrder')
1288
- elsif boot_order.to_s != ''
1289
- tier['bootOrder'] = boot_order.to_i
1290
- end
1291
-
1292
- # Connected Tiers
1293
- if !linked_tiers
1294
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'linkedTiers', 'fieldLabel' => 'Connected Tiers', 'type' => 'text', 'required' => false, 'description' => 'Names of connected tiers, comma separated', 'defaultValue' => (tier['linkedTiers'] ? tier['linkedTiers'].join(',') : nil)}], options[:options])
1295
- linked_tiers = v_prompt['linkedTiers'].to_s.split(',').collect {|it| it.strip }.select {|it| it != ''}
1296
- end
1297
- current_linked_tiers = tier['linkedTiers'] || []
1298
- if linked_tiers && linked_tiers != current_linked_tiers
1299
- remove_tiers = current_linked_tiers - linked_tiers
1300
- remove_tiers.each do |other_tier_name|
1301
- unlink_result = unlink_tiers(tiers, [tier_name, other_tier_name])
1302
- # could just re-prompt unless options[:no_prompt]
1303
- return 1 if !unlink_result
1304
- end
1305
- add_tiers = linked_tiers - current_linked_tiers
1306
- add_tiers.each do |other_tier_name|
1307
- link_result = link_tiers(tiers, [tier_name, other_tier_name])
1308
- # could just re-prompt unless options[:no_prompt]
1309
- return 1 if !link_result
1310
- end
1311
- end
1312
-
1313
- # ok, make api request
1314
- app_template["config"]["tiers"] = tiers
1315
- request_payload = app_template["config"]
1316
- # request_payload = {appTemplate: app_template}
1317
-
1318
- if options[:dry_run]
1319
- print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
1320
- return
1321
- end
1322
- json_response = @app_templates_interface.update(app_template['id'], request_payload)
1323
-
1324
- if options[:json]
1325
- puts JSON.pretty_generate(json_response)
1326
- elsif !options[:quiet]
1327
- print_green_success "Updated tier #{tier_name}"
1328
- get([app_template['id']])
1329
- end
1330
- return 0
1331
- rescue RestClient::Exception => e
1332
- print_rest_exception(e, options)
1333
- exit 1
1334
- end
1335
- end
1336
-
1337
- def remove_tier(args)
1338
- options = {}
1339
- optparse = Morpheus::Cli::OptionParser.new do |opts|
1340
- opts.banner = subcommand_usage("[template] [tier]")
1341
- build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
1342
- end
1343
- optparse.parse!(args)
1344
-
1345
- if args.count < 2
1346
- print_error Morpheus::Terminal.angry_prompt
1347
- puts_error "#{command_name} remove-tier expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
1348
- return 1
1349
- end
1350
- app_template_name = args[0]
1351
- tier_name = args[1]
1352
-
1353
- connect(options)
1354
-
1355
- begin
1356
- app_template = find_app_template_by_name_or_id(app_template_name)
1357
- return 1 if app_template.nil?
1358
-
1359
- app_template["config"] ||= {}
1360
- app_template["config"]["tiers"] ||= {}
1361
- tiers = app_template["config"]["tiers"]
1362
-
1363
- if !tiers[tier_name]
1364
- # print_red_alert "Tier not found by name #{tier_name}"
1365
- # return 1
1366
- print cyan,"Tier #{tier_name} does not exist.",reset,"\n"
1367
- return 0
1368
- end
1369
-
1370
- unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the tier #{tier_name}?")
1371
- exit
1372
- end
1373
-
1374
- # remove it
1375
- tiers.delete(tier_name)
1376
-
1377
- # ok, make api request
1378
- app_template["config"]["tiers"] = tiers
1379
- request_payload = app_template["config"]
1380
- # request_payload = {appTemplate: app_template}
1381
-
1382
- if options[:dry_run]
1383
- print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
1384
- return
1385
- end
1386
-
1387
- json_response = @app_templates_interface.update(app_template['id'], request_payload)
1388
-
1389
-
1390
- if options[:json]
1391
- print JSON.pretty_generate(json_response)
1392
- print "\n"
1393
- else
1394
- print_green_success "Removed tier #{tier_name}"
1395
- get([app_template['name']])
1396
- end
1397
-
1398
- rescue RestClient::Exception => e
1399
- print_rest_exception(e, options)
1400
- exit 1
1401
- end
1402
- end
1403
-
1404
- def connect_tiers(args)
1405
- options = {}
1406
- optparse = Morpheus::Cli::OptionParser.new do |opts|
1407
- opts.banner = subcommand_usage("[template] [Tier1] [Tier2]")
1408
- build_common_options(opts, options, [:json, :dry_run, :remote])
1409
- end
1410
- optparse.parse!(args)
1411
-
1412
- if args.count < 3
1413
- print_error Morpheus::Terminal.angry_prompt
1414
- puts_error "#{command_name} connect-tiers expects 3 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
1415
- # puts optparse
1416
- return 1
1417
- end
1418
- app_template_name = args[0]
1419
- tier1_name = args[1]
1420
- tier2_name = args[2]
1421
-
1422
- connect(options)
1423
-
1424
- begin
1425
- app_template = find_app_template_by_name_or_id(app_template_name)
1426
- return 1 if app_template.nil?
1427
-
1428
- app_template["config"] ||= {}
1429
- tiers = app_template["config"]["tiers"]
1430
-
1431
- if !tiers || tiers.keys.size == 0
1432
- error_msg = "App Template #{app_template['name']} has no tiers."
1433
- # print_red_alert "App Template #{app_template['name']} has no tiers."
1434
- # raise_command_error "App Template #{app_template['name']} has no tiers."
1435
- print_error Morpheus::Terminal.angry_prompt
1436
- puts_error "App Template #{app_template['name']} has no tiers."
1437
- return 1
1438
- end
1439
-
1440
- connect_tiers = []
1441
- tier1 = tiers[tier1_name]
1442
- tier2 = tiers[tier2_name]
1443
- # uhh support N args
1444
-
1445
- if tier1.nil?
1446
- print_red_alert "Tier not found by name #{tier1_name}!"
1447
- return 1
1448
- end
1449
-
1450
- if tier2.nil?
1451
- print_red_alert "Tier not found by name #{tier2_name}!"
1452
- return 1
1453
- end
1454
-
1455
- tier1["linkedTiers"] = tier1["linkedTiers"] || []
1456
- tier2["linkedTiers"] = tier2["linkedTiers"] || []
1457
-
1458
- found_edge = tier1["linkedTiers"].include?(tier2_name) || tier2["linkedTiers"].include?(tier1_name)
1459
-
1460
- if found_edge
1461
- puts cyan,"Tiers #{tier1_name} and #{tier2_name} are already connected.",reset
1462
- return 0
1463
- end
1464
-
1465
- # ok to be connect the tiers
1466
- # note: the ui doesn't hook up both sides eh?
1467
-
1468
- if !tier1["linkedTiers"].include?(tier2_name)
1469
- tier1["linkedTiers"].push(tier2_name)
1470
- end
1471
-
1472
- if !tier2["linkedTiers"].include?(tier1_name)
1473
- tier2["linkedTiers"].push(tier1_name)
1474
- end
1475
-
1476
- # ok, make api request
1477
- app_template["config"]["tiers"] = tiers
1478
- request_payload = app_template["config"]
1479
- # request_payload = {appTemplate: app_template}
1480
-
1481
- if options[:dry_run]
1482
- print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
1483
- return
1484
- end
1485
- json_response = @app_templates_interface.update(app_template['id'], request_payload)
1486
-
1487
-
1488
- if options[:json]
1489
- print JSON.pretty_generate(json_response)
1490
- print "\n"
1491
- else
1492
- print_green_success "Connected 2 tiers for app template #{app_template['name']}"
1493
- get([app_template['name']])
1494
- end
1495
-
1496
- rescue RestClient::Exception => e
1497
- print_rest_exception(e, options)
1498
- exit 1
1499
- end
1500
- end
1501
-
1502
- def disconnect_tiers(args)
1503
- options = {}
1504
- optparse = Morpheus::Cli::OptionParser.new do |opts|
1505
- opts.banner = subcommand_usage("[template] [Tier1] [Tier2]")
1506
- build_common_options(opts, options, [:json, :dry_run, :remote])
1507
- end
1508
- optparse.parse!(args)
1509
-
1510
- if args.count < 3
1511
- print_error Morpheus::Terminal.angry_prompt
1512
- puts_error "#{command_name} disconnect-tiers expects 3 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
1513
- # puts optparse
1514
- return 1
1515
- end
1516
- app_template_name = args[0]
1517
- tier1_name = args[1]
1518
- tier2_name = args[2]
1519
-
1520
- connect(options)
1521
-
1522
- begin
1523
- app_template = find_app_template_by_name_or_id(app_template_name)
1524
- return 1 if app_template.nil?
1525
-
1526
- app_template["config"] ||= {}
1527
- tiers = app_template["config"]["tiers"]
1528
-
1529
- if !tiers || tiers.keys.size == 0
1530
- # print_red_alert "App Template #{app_template['name']} has no tiers."
1531
- # raise_command_error "App Template #{app_template['name']} has no tiers."
1532
- print_error Morpheus::Terminal.angry_prompt
1533
- puts_error "App Template #{app_template['name']} has no tiers."
1534
- return 1
1535
- end
1536
-
1537
- connect_tiers = []
1538
- tier1 = tiers[tier1_name]
1539
- tier2 = tiers[tier2_name]
1540
- # uhh support N args
1541
-
1542
- if tier1.nil?
1543
- print_red_alert "Tier not found by name #{tier1_name}!"
1544
- return 1
1545
- end
1546
-
1547
- if tier2.nil?
1548
- print_red_alert "Tier not found by name #{tier2_name}!"
1549
- return 1
1550
- end
1551
-
1552
- tier1["linkedTiers"] = tier1["linkedTiers"] || []
1553
- tier2["linkedTiers"] = tier2["linkedTiers"] || []
1554
-
1555
- found_edge = tier1["linkedTiers"].include?(tier2_name) || tier2["linkedTiers"].include?(tier1_name)
1556
-
1557
- if found_edge
1558
- puts cyan,"Tiers #{tier1_name} and #{tier2_name} are not connected.",reset
1559
- return 0
1560
- end
1561
-
1562
- # remove links
1563
- tier1["linkedTiers"] = tier1["linkedTiers"].reject {|it| it == tier2_name }
1564
- tier2["linkedTiers"] = tier2["linkedTiers"].reject {|it| it == tier1_name }
1565
-
1566
- # ok, make api request
1567
- app_template["config"]["tiers"] = tiers
1568
- request_payload = app_template["config"]
1569
- # request_payload = {appTemplate: app_template}
1570
-
1571
- if options[:dry_run]
1572
- print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
1573
- return
1574
- end
1575
- json_response = @app_templates_interface.update(app_template['id'], request_payload)
1576
-
1577
-
1578
- if options[:json]
1579
- print JSON.pretty_generate(json_response)
1580
- print "\n"
1581
- else
1582
- print_green_success "Connected 2 tiers for app template #{app_template['name']}"
1583
- get([app_template['name']])
1584
- end
1585
-
1586
- rescue RestClient::Exception => e
1587
- print_rest_exception(e, options)
1588
- exit 1
1589
- end
1590
- end
1591
-
1592
- def available_tiers(args)
1593
- options = {}
1594
- optparse = Morpheus::Cli::OptionParser.new do |opts|
1595
- opts.banner = subcommand_usage()
1596
- build_common_options(opts, options, [:json, :dry_run, :remote])
1597
- end
1598
- optparse.parse!(args)
1599
- connect(options)
1600
- params = {}
1601
-
1602
- begin
1603
- if options[:dry_run]
1604
- print_dry_run @app_templates_interface.dry.list_tiers(params)
1605
- return
1606
- end
1607
- json_response = @app_templates_interface.list_tiers(params)
1608
- tiers = json_response["tiers"] # just a list of names
1609
- if options[:json]
1610
- puts JSON.pretty_generate(json_response)
1611
- else
1612
- print_h1 "Available Tiers"
1613
- if tiers.empty?
1614
- print yellow,"No tiers found.",reset,"\n"
1615
- else
1616
- # rows = tiers.collect do |tier|
1617
- # {
1618
- # id: tier['id'],
1619
- # name: tier['name'],
1620
- # }
1621
- # end
1622
- # print cyan
1623
- # tp rows, [:name]
1624
- print cyan
1625
- tiers.each do |tier_name|
1626
- puts tier_name
1627
- end
1628
- end
1629
- print reset,"\n"
1630
- end
1631
- return 0
1632
- rescue RestClient::Exception => e
1633
- print_rest_exception(e, options)
1634
- exit 1
1635
- end
1636
-
1637
- end
1638
-
1639
- private
1640
-
1641
-
1642
- def add_app_template_option_types(connected=true)
1643
- [
1644
- {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
1645
- {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
1646
- {'fieldName' => 'category', 'fieldLabel' => 'Category', 'type' => 'text', 'required' => false, 'displayOrder' => 3},
1647
- #{'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'selectOptions' => (connected ? get_available_groups() : []), 'required' => true}
1648
- ]
1649
- end
1650
-
1651
- def update_app_template_option_types(connected=true)
1652
- list = add_app_template_option_types(connected)
1653
- list = list.reject {|it| ["group"].include? it['fieldName'] }
1654
- list.each {|it| it['required'] = false }
1655
- list
1656
- end
1657
-
1658
- def find_app_template_by_name_or_id(val)
1659
- if val.to_s =~ /\A\d{1,}\Z/
1660
- return find_app_template_by_id(val)
1661
- else
1662
- return find_app_template_by_name(val)
1663
- end
1664
- end
1665
-
1666
- def find_app_template_by_id(id)
1667
- begin
1668
- json_response = @app_templates_interface.get(id.to_i)
1669
- return json_response['appTemplate']
1670
- rescue RestClient::Exception => e
1671
- if e.response && e.response.code == 404
1672
- print_red_alert "App Template not found by id #{id}"
1673
- else
1674
- raise e
1675
- end
1676
- end
1677
- end
1678
-
1679
- def find_app_template_by_name(name)
1680
- app_templates = @app_templates_interface.list({name: name.to_s})['appTemplates']
1681
- if app_templates.empty?
1682
- print_red_alert "App Template not found by name #{name}"
1683
- return nil
1684
- elsif app_templates.size > 1
1685
- print_red_alert "#{app_templates.size} app templates by name #{name}"
1686
- print_app_templates_table(app_templates, {color: red})
1687
- print reset,"\n"
1688
- return nil
1689
- else
1690
- return app_templates[0]
1691
- end
1692
- end
1693
-
1694
- def find_group_by_name(name)
1695
- group_results = @groups_interface.get(name)
1696
- if group_results['groups'].empty?
1697
- print_red_alert "Group not found by name #{name}"
1698
- return nil
1699
- end
1700
- return group_results['groups'][0]
1701
- end
1702
-
1703
- def find_cloud_by_name(group_id, name)
1704
- option_results = @options_interface.options_for_source('clouds',{groupId: group_id})
1705
- match = option_results['data'].find { |grp| grp['value'].to_s == name.to_s || grp['name'].downcase == name.downcase}
1706
- if match.nil?
1707
- print_red_alert "Cloud not found by name #{name}"
1708
- return nil
1709
- else
1710
- return match['value']
1711
- end
1712
- end
1713
-
1714
- def print_app_templates_table(app_templates, opts={})
1715
- table_color = opts[:color] || cyan
1716
- rows = app_templates.collect do |app_template|
1717
- #instance_type_names = (app_template['instanceTypes'] || []).collect {|it| it['name'] }.join(', ')
1718
- instance_type_names = []
1719
- # if app_template['config'] && app_template['config']["tiers"]
1720
- # app_template['config']["tiers"]
1721
- # end
1722
- {
1723
- id: app_template['id'],
1724
- name: app_template['name'],
1725
- description: app_template['description'],
1726
- category: app_template['category'],
1727
- tiers_summary: format_app_template_tiers_summary(app_template)
1728
- }
1729
- end
1730
-
1731
- term_width = current_terminal_width()
1732
- tiers_col_width = 60
1733
- if term_width > 190
1734
- tiers_col_width += 130
1735
- end
1736
- columns = [
1737
- :id,
1738
- :name,
1739
- :description,
1740
- :category,
1741
- {:tiers_summary => {:display_name => "TIERS", :max_width => tiers_col_width} }
1742
- ]
1743
- if opts[:include_fields]
1744
- columns = opts[:include_fields]
1745
- end
1746
- print table_color
1747
- print as_pretty_table(rows, columns, opts)
1748
- print reset
1749
- end
1750
-
1751
- def generate_id(len=16)
1752
- id = ""
1753
- len.times { id << (1 + rand(9)).to_s }
1754
- id
1755
- end
1756
-
1757
- def format_app_template_tiers_summary(app_template)
1758
- # don't use colors here, or cell truncation will not work
1759
- str = ""
1760
- if app_template["config"] && app_template["config"]["tiers"]
1761
- tier_descriptions = app_template["config"]["tiers"].collect do |tier_name, tier_config|
1762
- # maybe do Tier Name (instance, instance2)
1763
- instance_blurbs = []
1764
- if tier_config["instances"]
1765
- tier_config["instances"].each do |instance_config|
1766
- if instance_config["instance"] && instance_config["instance"]["type"]
1767
- # only have type: code in the config, rather not name fetch remotely right now..
1768
- # instance_blurbs << instance_config["instance"]["type"]
1769
- instance_name = instance_config["instance"]["name"] || ""
1770
- instance_type_code = instance_config["instance"]["type"]
1771
- instances_str = "#{instance_type_code}"
1772
- if instance_name.to_s != ""
1773
- instances_str << " - #{instance_name}"
1774
- end
1775
- begin
1776
- config_list = parse_scoped_instance_configs(instance_config)
1777
- if config_list.size == 0
1778
- instances_str << " (No configs)"
1779
- elsif config_list.size == 1
1780
- # configs_str = config_list.collect {|it|
1781
- # str = ""
1782
- # it[:scope].to_s.inspect
1783
- # }.join(", ")
1784
- the_config = config_list[0]
1785
- scope_str = the_config[:scope].collect {|k,v| v.to_s }.join("/")
1786
- instances_str << " (#{scope_str})"
1787
- else
1788
- instances_str << " (#{config_list.size} configs)"
1789
- end
1790
- rescue => err
1791
- puts_error "Failed to parse instance scoped instance configs: #{err.class} #{err.message}"
1792
- raise err
1793
- end
1794
- instance_blurbs << instances_str
1795
- end
1796
- end
1797
- end
1798
- if instance_blurbs.size > 0
1799
- tier_name + ": #{instance_blurbs.join(', ')}"
1800
- else
1801
- tier_name + ": (empty)"
1802
- end
1803
- end
1804
- str += tier_descriptions.compact.join(", ")
1805
- end
1806
- str
1807
- end
1808
-
1809
- def print_app_template_details(app_template)
1810
- print cyan
1811
- description_cols = {
1812
- "ID" => 'id',
1813
- "Name" => 'name',
1814
- "Description" => 'description',
1815
- "Category" => 'category',
1816
- #"Image" => lambda {|it| it['config'] ? it['config']['image'] : '' },
1817
- }
1818
- if app_template["config"] && app_template["config"]["image"]
1819
- description_cols["Image"] = lambda {|it| app_template["config"]["image"] }
1820
- # else
1821
- # '/assets/apps/template.png'
1822
- end
1823
- print_description_list(description_cols, app_template)
1824
- # print_h2 "Tiers"
1825
- if app_template["config"] && app_template["config"]["tiers"] && app_template["config"]["tiers"].keys.size != 0
1826
- print cyan
1827
- #puts as_yaml(app_template["config"]["tiers"])
1828
- app_template["config"]["tiers"].each do |tier_name, tier_config|
1829
- # print_h2 "Tier: #{tier_name}"
1830
- print_h2 tier_name
1831
- # puts " Instances:"
1832
- if tier_config['instances'] && tier_config['instances'].size != 0
1833
- # puts as_yaml(tier)
1834
- tier_config['instances'].each_with_index do |instance_config, instance_index|
1835
- instance_name = instance_config["instance"]["name"] || ""
1836
- instance_type_code = ""
1837
- if instance_config["instance"]["type"]
1838
- instance_type_code = instance_config["instance"]["type"]
1839
- end
1840
- instance_bullet = ""
1841
- # instance_bullet += "#{green} - #{bold}#{instance_type_code}#{reset}"
1842
- instance_bullet += "#{green}#{bold}#{instance_type_code}#{reset}"
1843
- if instance_name.to_s != ""
1844
- instance_bullet += "#{green} - #{instance_name}#{reset}"
1845
- end
1846
- puts instance_bullet
1847
- # print "\n"
1848
- begin
1849
- config_list = parse_scoped_instance_configs(instance_config)
1850
- print cyan
1851
- if config_list.size > 0
1852
- print "\n"
1853
- if config_list.size == 1
1854
- puts " Config:"
1855
- else
1856
- puts " Configs (#{config_list.size}):"
1857
- end
1858
- config_list.each do |config_obj|
1859
- # puts " = #{config_obj[:scope].inspect}"
1860
- config_scope = config_obj[:scope]
1861
- scoped_instance_config = config_obj[:config]
1862
- config_description = ""
1863
- config_items = []
1864
- if config_scope[:environment]
1865
- config_items << {label: "Environment", value: config_scope[:environment]}
1866
- end
1867
- if config_scope[:group]
1868
- config_items << {label: "Group", value: config_scope[:group]}
1869
- end
1870
- if config_scope[:cloud]
1871
- config_items << {label: "Cloud", value: config_scope[:cloud]}
1872
- end
1873
- # if scoped_instance_config['plan'] && scoped_instance_config['plan']['code']
1874
- # config_items << {label: "Plan", value: scoped_instance_config['plan']['code']}
1875
- # end
1876
- config_description = config_items.collect {|item| "#{item[:label]}: #{item[:value]}"}.join(", ")
1877
- puts " * #{config_description}"
1878
- end
1879
- else
1880
- print white," Instance has no configs, see `app-templates add-instance-config \"#{app_template['name']}\" \"#{tier_name}\" \"#{instance_type_code}\"`",reset,"\n"
1881
- end
1882
- rescue => err
1883
- #puts_error "Failed to parse instance scoped instance configs for app template #{app_template['id']} #{app_template['name']} Exception: #{err.class} #{err.message}"
1884
- end
1885
- print "\n"
1886
- #puts as_yaml(instance_config)
1887
- # todo: iterate over
1888
- # instance_config["groups"][group_name]["clouds"][cloud_name]
1889
- end
1890
-
1891
- print cyan
1892
- if tier_config['bootOrder']
1893
- puts "Boot Order: #{tier_config['bootOrder']}"
1894
- end
1895
- if tier_config['linkedTiers'] && !tier_config['linkedTiers'].empty?
1896
- puts "Connected Tiers: #{tier_config['linkedTiers'].join(', ')}"
1897
- end
1898
-
1899
- else
1900
- print white," Tier is empty, see `app-templates add-instance \"#{app_template['name']}\" \"#{tier_name}\"`",reset,"\n"
1901
- end
1902
- # print "\n"
1903
-
1904
- end
1905
- # print "\n"
1906
-
1907
- else
1908
- print white,"\nTemplate is empty, see `app-templates add-tier \"#{app_template['name']}\"`",reset,"\n"
1909
- end
1910
- end
1911
-
1912
- # this parses the environments => groups => clouds tree structure
1913
- # and returns a list of objects like {scope: {group:'thegroup'}, config: Map}
1914
- # this would be be better as a recursive function, brute forced for now.
1915
- def parse_scoped_instance_configs(instance_config)
1916
- config_list = []
1917
- if instance_config['environments'] && instance_config['environments'].keys.size > 0
1918
- instance_config['environments'].each do |env_name, env_config|
1919
- if env_config['groups']
1920
- env_config['groups'].each do |group_name, group_config|
1921
- if group_config['clouds'] && !group_config['clouds'].empty?
1922
- group_config['clouds'].each do |cloud_name, cloud_config|
1923
- config_list << {config: cloud_config, scope: {environment: env_name, group: group_name, cloud: cloud_name}}
1924
- end
1925
- end
1926
- if (!group_config['clouds'] || group_config['clouds'].empty?)
1927
- config_list << {config: group_config, scope: {environment: env_name, group: group_name}}
1928
- end
1929
- end
1930
- end
1931
- if env_config['clouds'] && !env_config['clouds'].empty?
1932
- env_config['clouds'].each do |cloud_name, cloud_config|
1933
- config_list << {config: cloud_config, scope: {environment: env_name, cloud: cloud_name}}
1934
- end
1935
- end
1936
- if (!env_config['groups'] || env_config['groups'].empty?) && (!env_config['clouds'] || env_config['clouds'].empty?)
1937
- config_list << {config: env_config, scope: {environment: env_name}}
1938
- end
1939
- end
1940
- end
1941
- if instance_config['groups']
1942
- instance_config['groups'].each do |group_name, group_config|
1943
- if group_config['clouds'] && !group_config['clouds'].empty?
1944
- group_config['clouds'].each do |cloud_name, cloud_config|
1945
- config_list << {config: cloud_config, scope: {group: group_name, cloud: cloud_name}}
1946
- end
1947
- end
1948
- if (!group_config['clouds'] || group_config['clouds'].empty?)
1949
- config_list << {config: group_config, scope: {group: group_name}}
1950
- end
1951
- end
1952
- end
1953
- if instance_config['clouds']
1954
- instance_config['clouds'].each do |cloud_name, cloud_config|
1955
- config_list << {config: cloud_config, scope: {cloud: cloud_name}}
1956
- end
1957
- end
1958
- return config_list
1959
- end
1960
-
1961
- def link_tiers(tiers, tier_names)
1962
- # tiers = app_template["config"]["tiers"]
1963
- tier_names = [tier_names].flatten.collect {|it| it }.compact.uniq
1964
- if !tiers
1965
- print_red_alert "No tiers found for template"
1966
- return false
1967
- end
1968
-
1969
- existing_tier_names = tiers.keys
1970
- matching_tier_names = tier_names.map {|tier_name|
1971
- existing_tier_names.find {|k| k.downcase == tier_name.downcase }
1972
- }.compact
1973
- if matching_tier_names.size != tier_names.size
1974
- print_red_alert "Template does not contain tiers: '#{tier_names}'"
1975
- return false
1976
- end
1977
- matching_tier_names.each do |tier_name|
1978
- tier = tiers[tier_name]
1979
- tier['linkedTiers'] ||= []
1980
- other_tier_names = matching_tier_names.select {|it| tier_name != it}
1981
- other_tier_names.each do |other_tier_name|
1982
- if !tier['linkedTiers'].include?(other_tier_name)
1983
- tier['linkedTiers'].push(other_tier_name)
1984
- end
1985
- end
1986
- end
1987
- return true
1988
- end
1989
-
1990
- def unlink_tiers(tiers, tier_names)
1991
- # tiers = app_template["config"]["tiers"]
1992
- tier_names = [tier_names].flatten.collect {|it| it }.compact.uniq
1993
- if !tiers
1994
- print_red_alert "No tiers found for template"
1995
- return false
1996
- end
1997
-
1998
- existing_tier_names = tiers.keys
1999
- matching_tier_names = tier_names.map {|tier_name|
2000
- existing_tier_names.find {|k| k.downcase == tier_name.downcase }
2001
- }.compact
2002
- if matching_tier_names.size != tier_names.size
2003
- print_red_alert "Template does not contain tiers: '#{tier_names}'"
2004
- return false
2005
- end
2006
- matching_tier_names.each do |tier_name|
2007
- tier = tiers[tier_name]
2008
- tier['linkedTiers'] ||= []
2009
- other_tier_names = matching_tier_names.select {|it| tier_name != it}
2010
- other_tier_names.each do |other_tier_name|
2011
- if tier['linkedTiers'].include?(other_tier_name)
2012
- tier['linkedTiers'] = tier['linkedTiers'].reject {|it| it == other_tier_name }
2013
- end
2014
- end
2015
- end
2016
- return true
2017
- end
2018
-
2019
- end