morpheus-cli 0.9.10 → 2.9.0

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/bin/morpheus +48 -33
  3. data/lib/morpheus/api/api_client.rb +12 -0
  4. data/lib/morpheus/api/custom_instance_types.rb +55 -0
  5. data/lib/morpheus/api/custom_instance_types_interface.rb +133 -0
  6. data/lib/morpheus/api/dashboard_interface.rb +37 -0
  7. data/lib/morpheus/api/users_interface.rb +17 -0
  8. data/lib/morpheus/api/whoami_interface.rb +20 -0
  9. data/lib/morpheus/cli.rb +13 -1
  10. data/lib/morpheus/cli/app_templates.rb +1 -1
  11. data/lib/morpheus/cli/cli_command.rb +22 -1
  12. data/lib/morpheus/cli/cli_registry.rb +1 -1
  13. data/lib/morpheus/cli/credentials.rb +33 -11
  14. data/lib/morpheus/cli/dashboard_command.rb +74 -0
  15. data/lib/morpheus/cli/instance_types.rb +4 -2
  16. data/lib/morpheus/cli/key_pairs.rb +1 -1
  17. data/lib/morpheus/cli/library.rb +539 -0
  18. data/lib/morpheus/cli/login.rb +57 -0
  19. data/lib/morpheus/cli/logout.rb +61 -0
  20. data/lib/morpheus/cli/mixins/accounts_helper.rb +52 -10
  21. data/lib/morpheus/cli/mixins/print_helper.rb +23 -16
  22. data/lib/morpheus/cli/mixins/provisioning_helper.rb +1 -1
  23. data/lib/morpheus/cli/mixins/whoami_helper.rb +34 -0
  24. data/lib/morpheus/cli/option_types.rb +15 -1
  25. data/lib/morpheus/cli/recent_activity_command.rb +83 -0
  26. data/lib/morpheus/cli/remote.rb +71 -28
  27. data/lib/morpheus/cli/roles.rb +89 -24
  28. data/lib/morpheus/cli/shell.rb +24 -11
  29. data/lib/morpheus/cli/users.rb +166 -24
  30. data/lib/morpheus/cli/version.rb +1 -1
  31. data/lib/morpheus/cli/version_command.rb +45 -0
  32. data/lib/morpheus/cli/whoami.rb +139 -0
  33. data/lib/morpheus/logging.rb +78 -0
  34. metadata +15 -2
@@ -8,7 +8,7 @@ module Morpheus
8
8
 
9
9
  def self.included(klass)
10
10
  Morpheus::Cli::CliRegistry.add(klass)
11
- klass.include Morpheus::Cli::PrintHelper
11
+ klass.send :include, Morpheus::Cli::PrintHelper
12
12
  klass.extend ClassMethods
13
13
  end
14
14
 
@@ -114,6 +114,20 @@ module Morpheus
114
114
  opts.on('-C','--nocolor', "ANSI") do
115
115
  Term::ANSIColor::coloring = false
116
116
  end
117
+
118
+ opts.on('-V','--debug', "Print extra output for debugging. ") do |json|
119
+ options[:debug] = true
120
+ # this is handled upstream for now...
121
+ # Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::DEBUG)
122
+ # perhaps...
123
+ # create a new logger instance just for this command instance
124
+ # this way we don't elevate the global level for subsequent commands in a shell
125
+ # @logger = Morpheus::Logging::Logger.new(STDOUT)
126
+ # if !@logger.debug?
127
+ # @logger.log_level = Morpheus::Logging::Logger::DEBUG
128
+ # end
129
+ end
130
+
117
131
  opts.on('-h', '--help', "Prints this help" ) do
118
132
  puts opts
119
133
  exit
@@ -127,7 +141,14 @@ module Morpheus
127
141
  Morpheus::Cli::CliRegistry.add(self, cmd_name)
128
142
  end
129
143
 
144
+ def cli_command_hidden(val=true)
145
+ @hidden_command = val
146
+ end
130
147
 
148
+ def hidden_command
149
+ !!@hidden_command
150
+ end
151
+
131
152
  end
132
153
  end
133
154
  end
@@ -61,7 +61,7 @@ module Morpheus
61
61
  end
62
62
 
63
63
  def all
64
- @commands
64
+ @commands.reject {|cmd, klass| klass.hidden_command }
65
65
  end
66
66
 
67
67
  def get(cmd_name)
@@ -1,10 +1,15 @@
1
1
  require 'yaml'
2
2
  require 'io/console'
3
- require 'optparse'
3
+ require 'rest_client'
4
+ #require 'optparse'
5
+ require 'morpheus/cli/mixins/print_helper'
6
+ require 'json'
4
7
 
5
8
  module Morpheus
6
9
  module Cli
7
10
  class Credentials
11
+ include Morpheus::Cli::PrintHelper
12
+
8
13
  def initialize(appliance_name, appliance_url)
9
14
  @appliance_url = appliance_url
10
15
  @appliance_name = appliance_name
@@ -24,39 +29,56 @@ module Morpheus
24
29
  creds = load_saved_credentials
25
30
  end
26
31
  if !creds
32
+ print "\nEnter Morpheus Credentials for #{@appliance_name} - #{@appliance_url}\n\n",reset
27
33
  if username.nil? || username.empty?
28
- puts "Enter Username: "
34
+ # print "Username: "
35
+ print "Username: #{required_blue_prompt} "
29
36
  username = $stdin.gets.chomp!
30
37
  end
31
38
  if password.nil? || password.empty?
32
- puts "Enter Password: "
39
+ # print "Password: "
40
+ print "Password: #{required_blue_prompt} "
33
41
  password = STDIN.noecho(&:gets).chomp!
42
+ print "\n"
34
43
  end
44
+
35
45
  oauth_url = File.join(@appliance_url, "/oauth/token")
36
46
  begin
37
- authorize_response = Morpheus::RestClient.execute(method: :post, url: oauth_url, headers:{ params: {grant_type: 'password', scope:'write', client_id: 'morph-cli', username: username, password: password}},verify_ssl: false)
47
+ authorize_response = Morpheus::RestClient.execute(method: :post, url: oauth_url, headers:{ params: {grant_type: 'password', scope:'write', client_id: 'morph-cli', username: username}}, payload: {password: password},verify_ssl: false, timeout: 10)
48
+
38
49
  json_response = JSON.parse(authorize_response.to_s)
39
50
  access_token = json_response['access_token']
40
- if access_token
41
-
51
+ if !access_token.empty?
42
52
  save_credentials(access_token) unless skip_save
43
53
  return access_token
44
54
  else
45
- puts "Credentials not verified."
55
+ print_red_alert "Credentials not verified."
46
56
  return nil
47
57
  end
58
+ rescue ::RestClient::Exception => e
59
+ if (e.response && e.response.code == 400)
60
+ print_red_alert "Credentials not verified."
61
+ if opts[:json]
62
+ json_response = JSON.parse(e.response.to_s)
63
+ print JSON.pretty_generate(json_response)
64
+ print reset, "\n\n"
65
+ end
66
+ else
67
+ print_rest_exception(e, opts)
68
+ end
69
+ exit 1
48
70
  rescue => e
49
- puts "Error Communicating with the Appliance. Please try again later. #{e}"
50
- return nil
71
+ print_red_alert "Error Communicating with the Appliance. #{e}"
72
+ exit 1
51
73
  end
52
74
  else
53
75
  return creds
54
76
  end
55
77
  end
56
78
 
57
- def login()
79
+ def login(opts = {})
58
80
  clear_saved_credentials
59
- request_credentials
81
+ request_credentials(opts)
60
82
  end
61
83
 
62
84
  def logout()
@@ -0,0 +1,74 @@
1
+ require 'optparse'
2
+ require 'morpheus/cli/cli_command'
3
+ require 'json'
4
+
5
+ class Morpheus::Cli::DashboardCommand
6
+ include Morpheus::Cli::CliCommand
7
+
8
+ cli_command_name :dashboard
9
+ cli_command_hidden # remove once this is done
10
+
11
+ def initialize()
12
+ @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
13
+ @active_groups = ::Morpheus::Cli::Groups.load_group_file
14
+ end
15
+
16
+ def connect(opts)
17
+ @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).load_saved_credentials()
18
+ if @access_token.empty?
19
+ print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
20
+ exit 1
21
+ end
22
+ @api_client = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url)
23
+ @dashboard_interface = @api_client.dashboard
24
+ end
25
+
26
+ def usage
27
+ "Usage: morpheus dashboard"
28
+ end
29
+
30
+ def handle(args)
31
+ show(args)
32
+ end
33
+
34
+ def show(args)
35
+ options = {}
36
+ optparse = OptionParser.new do|opts|
37
+ opts.banner = usage
38
+ build_common_options(opts, options, [:json]) # todo: support :account
39
+ end
40
+ optparse.parse(args)
41
+
42
+ connect(options)
43
+ begin
44
+
45
+ params = {}
46
+
47
+ json_response = @dashboard_interface.get(params)
48
+
49
+ if options[:json]
50
+ print JSON.pretty_generate(json_response)
51
+ print "\n"
52
+ else
53
+
54
+
55
+ # todo: impersonate command and show that info here
56
+
57
+ print "\n" ,cyan, bold, "Dashboard\n","==================", reset, "\n\n"
58
+ print cyan
59
+
60
+ print "\n"
61
+ puts "Coming soon.... see --json"
62
+ print "\n"
63
+
64
+ print reset,"\n"
65
+
66
+ end
67
+ rescue RestClient::Exception => e
68
+ print_rest_exception(e, options)
69
+ exit 1
70
+ end
71
+ end
72
+
73
+
74
+ end
@@ -52,7 +52,8 @@ class Morpheus::Cli::InstanceTypes
52
52
  json_response = @instance_types_interface.get({name: name})
53
53
 
54
54
  if options[:json]
55
- print JSON.pretty_generate(json_response), "\n" and return
55
+ print JSON.pretty_generate(json_response), "\n"
56
+ return
56
57
  end
57
58
 
58
59
  instance_type = json_response['instanceTypes'][0]
@@ -90,7 +91,8 @@ class Morpheus::Cli::InstanceTypes
90
91
  json_response = @instance_types_interface.get(params)
91
92
 
92
93
  if options[:json]
93
- print JSON.pretty_generate(json_response), "\n" and return
94
+ print JSON.pretty_generate(json_response), "\n"
95
+ return
94
96
  end
95
97
 
96
98
  instance_types = json_response['instanceTypes']
@@ -319,7 +319,7 @@ private
319
319
  json_response = @key_pairs_interface.get(account_id, id.to_i)
320
320
  return json_response['keyPair']
321
321
  rescue RestClient::Exception => e
322
- if e.response.code == 404
322
+ if e.response && e.response.code == 404
323
323
  print_red_alert "Key Pair not found by id #{id}"
324
324
  else
325
325
  raise e
@@ -0,0 +1,539 @@
1
+ require 'io/console'
2
+ require 'optparse'
3
+ require 'filesize'
4
+ require 'morpheus/cli/cli_command'
5
+
6
+ class Morpheus::Cli::Library
7
+ include Morpheus::Cli::CliCommand
8
+
9
+ def initialize()
10
+ @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
11
+ end
12
+
13
+ def connect(opts)
14
+ @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials()
15
+ if @access_token.empty?
16
+ print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
17
+ exit 1
18
+ end
19
+ @api_client = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url)
20
+ @custom_instance_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).custom_instance_types
21
+ @provision_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).provision_types
22
+ end
23
+
24
+ def handle(args)
25
+ usage = "Usage: morpheus library [list,details,add,update,remove,add-version] [name]"
26
+ case args[0]
27
+ when 'list'
28
+ list(args[1..-1])
29
+ when 'details'
30
+ details(args[1..-1])
31
+ when 'add'
32
+ add(args[1..-1])
33
+ when 'update'
34
+ update(args[1..-1])
35
+ when 'remove'
36
+ remove(args[1..-1])
37
+ when 'add-version'
38
+ add_version(args[1..-1])
39
+ else
40
+ puts "\n#{usage}\n\n"
41
+ exit 127
42
+ end
43
+ end
44
+
45
+ def list(args)
46
+ options = {}
47
+ optparse = OptionParser.new do|opts|
48
+ opts.banner = "Usage: morpheus library list"
49
+ build_common_options(opts, options, [:list, :json])
50
+ end
51
+ optparse.parse(args)
52
+ connect(options)
53
+ begin
54
+ params = {}
55
+ [:phrase, :offset, :max, :sort, :direction].each do |k|
56
+ params[k] = options[k] unless options[k].nil?
57
+ end
58
+
59
+ json_response = @custom_instance_types_interface.list(params)
60
+
61
+ if options[:json]
62
+ print JSON.pretty_generate(json_response), "\n"
63
+ return
64
+ end
65
+
66
+ instance_types = json_response['instanceTypes']
67
+ print "\n" ,cyan, bold, "Morpheus Custom Instance Types\n","==================", reset, "\n\n"
68
+ if instance_types.empty?
69
+ puts yellow,"No instance types currently configured.",reset
70
+ else
71
+ instance_types.each do |instance_type|
72
+ versions = instance_type['versions'].join(', ')
73
+ print cyan, "= #{instance_type['name']} (#{instance_type['code']}) - #{versions}\n"
74
+ instance_type['instanceTypeLayouts'].each do |layout|
75
+ print green, " - #{layout['name']}\n",reset
76
+ end
77
+ end
78
+ end
79
+ print reset,"\n\n"
80
+
81
+ rescue RestClient::Exception => e
82
+ print_rest_exception(e, options)
83
+ exit 1
84
+ end
85
+ end
86
+
87
+ def details(args)
88
+ options = {}
89
+ optparse = OptionParser.new do|opts|
90
+ opts.banner = "Usage: morpheus library details [name]"
91
+ build_common_options(opts, options, [:json])
92
+ end
93
+ optparse.parse(args)
94
+ if args.count < 1
95
+ puts "\n#{optparse.banner}\n\n"
96
+ exit 1
97
+ end
98
+
99
+ connect(options)
100
+ begin
101
+ instance_type = find_custom_instance_type_by_name_or_code(args[0])
102
+ exit 1 if instance_type.nil?
103
+
104
+ if options[:json]
105
+ print JSON.pretty_generate({instanceType: instance_type}), "\n"
106
+ return
107
+ end
108
+
109
+ if instance_type.nil?
110
+ puts yellow,"No custom instance type found by name #{name}.",reset
111
+ else
112
+ print "\n" ,cyan, bold, "Custom Instance Type Details\n","==================", reset, "\n\n"
113
+ versions = instance_type['versions'].join(', ')
114
+ print cyan, "= #{instance_type['name']} (#{instance_type['code']}) - #{versions}\n"
115
+ instance_type['instanceTypeLayouts'].each do |layout|
116
+ print green, " - #{layout['name']}\n",reset
117
+ end
118
+ print reset,"\n\n"
119
+ end
120
+
121
+ rescue RestClient::Exception => e
122
+ print_rest_exception(e, options)
123
+ exit 1
124
+ end
125
+ end
126
+
127
+ def add(args)
128
+ options = {}
129
+ optparse = OptionParser.new do|opts|
130
+ opts.banner = "Usage: morpheus library add"
131
+ build_common_options(opts, options, [:options, :json])
132
+ end
133
+ optparse.parse(args)
134
+ connect(options)
135
+ begin
136
+ params = Morpheus::Cli::OptionTypes.prompt(add_instance_type_option_types, options[:options], @api_client, options[:params])
137
+ instance_type_keys = ['name', 'description', 'category', 'visibility', 'environmentPrefix']
138
+ instance_type_payload = params.select {|k,v| instance_type_keys.include?(k) }
139
+ logo_file = nil
140
+ if params['logo']
141
+ filename = File.expand_path(params['logo'])
142
+ if !File.exists?(filename)
143
+ print_red_alert "File not found: #{filename}"
144
+ exit 1
145
+ end
146
+ #instance_type_payload['logo'] = File.new(filename, 'rb')
147
+ logo_file = File.new(filename, 'rb')
148
+ end
149
+ if params['hasAutoScale'] == 'on'
150
+ instance_type_payload['hasAutoScale'] = true
151
+ end
152
+ if params['hasDeployment'] == 'on'
153
+ instance_type_payload['hasDeployment'] = true
154
+ end
155
+ request_payload = {instanceType: instance_type_payload}
156
+ json_response = @custom_instance_types_interface.create(request_payload)
157
+
158
+ if json_response['success']
159
+ if logo_file
160
+ begin
161
+ @custom_instance_types_interface.update_logo(json_response['instanceType']['id'], logo_file)
162
+ rescue RestClient::Exception => e
163
+ print_red_alert "Failed to save logo!"
164
+ print_rest_exception(e, options)
165
+ end
166
+ end
167
+ end
168
+
169
+ if options[:json]
170
+ print JSON.pretty_generate(json_response), "\n"
171
+ return
172
+ end
173
+
174
+ print_green_success "Added Instance Type #{instance_type_payload['name']}"
175
+
176
+ unless options[:no_prompt]
177
+ if ::Morpheus::Cli::OptionTypes::confirm("Add first version?", options)
178
+ puts "\n"
179
+ add_version(["code:#{json_response['code']}"])
180
+ end
181
+ end
182
+
183
+ #list([])
184
+
185
+ rescue RestClient::Exception => e
186
+ print_rest_exception(e, options)
187
+ exit 1
188
+ end
189
+ end
190
+
191
+ def update(args)
192
+ options = {}
193
+ optparse = OptionParser.new do|opts|
194
+ opts.banner = "Usage: morpheus library update [name] [options]"
195
+ build_common_options(opts, options, [:options, :json])
196
+ end
197
+ optparse.parse(args)
198
+ if args.count < 1
199
+ puts "\n#{optparse.banner}\n\n"
200
+ exit 1
201
+ end
202
+ connect(options)
203
+ begin
204
+ instance_type = find_custom_instance_type_by_name_or_code(args[0])
205
+ exit 1 if instance_type.nil?
206
+ # option_types = update_instance_type_option_types(instance_type)
207
+ # params = Morpheus::Cli::OptionTypes.prompt(option_types, options[:options], @api_client, options[:params])
208
+ params = options[:options] || {}
209
+
210
+ instance_type_keys = ['name', 'description', 'category', 'visibility', 'environmentPrefix']
211
+ instance_type_payload = params.select {|k,v| instance_type_keys.include?(k) }
212
+ logo_file = nil
213
+ if params['logo']
214
+ filename = File.expand_path(params['logo'])
215
+ if !File.exists?(filename)
216
+ print_red_alert "File not found: #{filename}"
217
+ exit 1
218
+ end
219
+ #instance_type_payload['logo'] = File.new(filename, 'rb')
220
+ logo_file = File.new(filename, 'rb')
221
+ end
222
+ if params['hasAutoScale'] == 'on'
223
+ instance_type_payload['hasAutoScale'] = true
224
+ elsif params['hasAutoScale'] == 'off'
225
+ instance_type_payload['hasAutoScale'] = false
226
+ end
227
+ if params['hasDeployment'] == 'on'
228
+ instance_type_payload['hasDeployment'] = true
229
+ elsif params['hasDeployment'] == 'off'
230
+ instance_type_payload['hasDeployment'] = false
231
+ end
232
+ if instance_type_payload.empty? && logo_file.nil?
233
+ puts "\n#{optparse.banner}\n\n"
234
+ option_lines = update_instance_type_option_types.collect {|it| "\t-O #{it['fieldName']}=\"value\"" }.join("\n")
235
+ puts "\nAvailable Options:\n#{option_lines}\n\n"
236
+ exit 1
237
+ end
238
+ if instance_type_payload.empty?
239
+ # just updating logo (separate request)
240
+ instance_type_payload['name'] = instance_type['name']
241
+ end
242
+ request_payload = {instanceType: instance_type_payload}
243
+ json_response = @custom_instance_types_interface.update(instance_type['id'], request_payload)
244
+
245
+ if json_response['success']
246
+ if logo_file
247
+ begin
248
+ @custom_instance_types_interface.update_logo(json_response['instanceType']['id'], logo_file)
249
+ rescue RestClient::Exception => e
250
+ print_red_alert "Failed to save logo!"
251
+ print_rest_exception(e, options)
252
+ end
253
+ end
254
+ end
255
+
256
+ if options[:json]
257
+ print JSON.pretty_generate(json_response), "\n"
258
+ return
259
+ end
260
+
261
+ print_green_success "Updated Instance Type #{instance_type_payload['name']}"
262
+ #list([])
263
+ rescue RestClient::Exception => e
264
+ print_rest_exception(e, options)
265
+ exit 1
266
+ end
267
+ end
268
+
269
+ def remove(args)
270
+ options = {}
271
+ optparse = OptionParser.new do|opts|
272
+ opts.banner = "Usage: morpheus library remove [name]"
273
+ build_common_options(opts, options, [:json, :auto_confirm])
274
+ end
275
+ optparse.parse(args)
276
+ if args.count < 1
277
+ puts "\n#{optparse.banner}\n\n"
278
+ exit 1
279
+ end
280
+ code = args[0]
281
+ connect(options)
282
+
283
+ begin
284
+
285
+ instance_type = find_custom_instance_type_by_name_or_code(args[0])
286
+ exit 1 if instance_type.nil?
287
+
288
+ unless Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the instance type #{instance_type['name']}?", options)
289
+ exit
290
+ end
291
+
292
+ json_response = @custom_instance_types_interface.destroy(instance_type['id'])
293
+
294
+ if options[:json]
295
+ print JSON.pretty_generate(json_response), "\n"
296
+ return
297
+ end
298
+
299
+ print_green_success "Removed Instance Type #{instance_type['name']}"
300
+ #list([])
301
+ rescue RestClient::Exception => e
302
+ print_rest_exception(e, options)
303
+ exit 1
304
+ end
305
+ end
306
+
307
+ def add_version(args)
308
+ options = {}
309
+ optparse = OptionParser.new do|opts|
310
+ opts.banner = "Usage: morpheus library add-version [name]"
311
+ build_common_options(opts, options, [:options, :json])
312
+ end
313
+ optparse.parse(args)
314
+ if args.count < 1
315
+ puts "\n#{optparse.banner}\n\n"
316
+ exit 1
317
+ end
318
+ connect(options)
319
+ begin
320
+ instance_type = find_custom_instance_type_by_name_or_code(args[0])
321
+ exit 1 if instance_type.nil?
322
+
323
+ #params = Morpheus::Cli::OptionTypes.prompt(add_version_option_types, options[:options], @api_client, options[:params])
324
+
325
+ provision_types = @provision_types_interface.get({customSupported: true})['provisionTypes']
326
+ if provision_types.empty?
327
+ print_red_alert "No available provision types found!"
328
+ exit 1
329
+ end
330
+ provision_type_options = provision_types.collect {|it| { 'name' => it['name'], 'value' => it['code']} }
331
+
332
+ payload = {'containerType' => {}, 'instanceTypeLayout' => {}, 'instanceType' => {}}
333
+
334
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'instanceTypeLayout', 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Name', 'required' => true, 'description' => 'A name for this layout.'}], options[:options])
335
+ payload['instanceTypeLayout']['name'] = v_prompt['instanceTypeLayout']['name']
336
+
337
+ # shortName is only available for the first new version
338
+ if !instance_type['versions'] || instance_type['versions'].size == 0
339
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'containerType', 'fieldName' => 'shortName', 'type' => 'text', 'fieldLabel' => 'Short Name', 'required' => true, 'description' => 'The short name is a lowercase name with no spaces used for display in your container list.'}], options[:options])
340
+ payload['containerType']['shortName'] = v_prompt['containerType']['shortName']
341
+ end
342
+
343
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'containerType', 'fieldName' => 'provisionTypeCode', 'type' => 'select', 'selectOptions' => provision_type_options, 'fieldLabel' => 'Technology', 'required' => true, 'description' => 'The type of container technology.'}], options[:options])
344
+ payload['containerType']['provisionTypeCode'] = v_prompt['containerType']['provisionTypeCode']
345
+ provision_type = provision_types.find {|it| it['code'] == payload['containerType']['provisionTypeCode'] }
346
+
347
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'instanceTypeLayout', 'fieldName' => 'instanceVersion', 'type' => 'text', 'fieldLabel' => 'Version', 'required' => true, 'description' => 'A Version Number eg. 0.0.1'}], options[:options])
348
+ payload['instanceTypeLayout']['instanceVersion'] = v_prompt['instanceTypeLayout']['instanceVersion']
349
+
350
+ custom_option_types = provision_type['customOptionTypes']
351
+
352
+ if (!custom_option_types || custom_option_types.empty?)
353
+ puts yellow,"Sorry, no options were found for #{provision_type['name']}.",reset
354
+ exit 1
355
+ end
356
+
357
+ # prompt custom options for the selected provision type
358
+ field_group_name = custom_option_types.first['fieldGroup'] || "#{provision_type['name']} Options"
359
+ puts field_group_name
360
+ puts "==============="
361
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(custom_option_types,options[:options],@api_client, {provisionTypCode: payload['provisionTypeCode']})
362
+
363
+ if v_prompt['containerType']
364
+ payload['containerType'].merge!(v_prompt['containerType'])
365
+ end
366
+ if v_prompt['containerType.config']
367
+ payload['containerType']['config'] = v_prompt['containerType.config']
368
+ end
369
+ if v_prompt['instanceTypeLayout']
370
+ payload['instanceTypeLayout'].merge!(v_prompt['instanceTypeLayout'])
371
+ end
372
+ # instanceType.backupType, which is not even persisted on the server?
373
+ if v_prompt['instanceType']
374
+ payload['instanceType'].merge!(v_prompt['instanceType'])
375
+ end
376
+
377
+ # puts "PAYLOAD:"
378
+ # puts JSON.pretty_generate(payload)
379
+
380
+ # puts "\nexiting early"
381
+ # exit 0
382
+
383
+ payload['exposedPorts'] = prompt_exposed_ports(options, @api_client)
384
+
385
+ request_payload = payload
386
+ json_response = @custom_instance_types_interface.create_version(instance_type['id'], request_payload)
387
+
388
+ if options[:json]
389
+ print JSON.pretty_generate(json_response), "\n"
390
+ return
391
+ end
392
+
393
+ print_green_success "Added Instance Type Version #{instance_type['name']} - #{payload['instanceTypeLayout']['instanceVersion']}"
394
+ #list([])
395
+ rescue RestClient::Exception => e
396
+ print_rest_exception(e, options)
397
+ exit 1
398
+ end
399
+ end
400
+
401
+
402
+ private
403
+
404
+ def find_custom_instance_type_by_code(code)
405
+ instance_type_results = @custom_instance_types_interface.list({code: code})
406
+ if instance_type_results['instanceTypes'].empty?
407
+ print_red_alert "Custom Instance Type not found by code #{code}"
408
+ return nil
409
+ end
410
+ return instance_type_results['instanceTypes'][0]
411
+ end
412
+
413
+ def find_custom_instance_type_by_name(name)
414
+ instance_type_results = @custom_instance_types_interface.list({name: name})
415
+ instance_types = instance_type_results['instanceTypes']
416
+ if instance_types.empty?
417
+ print_red_alert "Custom Instance Type not found by name #{name}"
418
+ return nil
419
+ elsif instance_types.size > 1
420
+ print_red_alert "Found #{instance_types.size} instance types by name #{name}"
421
+ print red, "\n"
422
+ instance_types.each do |instance_type|
423
+ print "= #{instance_type['name']} (#{instance_type['code']})\n"
424
+ end
425
+ print "\n", "Find by code:<code> instead"
426
+ print reset,"\n\n"
427
+ return nil
428
+ else
429
+ return instance_types[0]
430
+ end
431
+ end
432
+
433
+ def find_custom_instance_type_by_name_or_code(val)
434
+ if val =~ /code:/
435
+ find_custom_instance_type_by_code(val.sub('code:', ''))
436
+ else
437
+ find_custom_instance_type_by_name(val)
438
+ end
439
+ end
440
+
441
+ def instance_type_categories
442
+ [
443
+ {'name' => 'Web', 'value' => 'web'},
444
+ {'name' => 'SQL', 'value' => 'sql'},
445
+ {'name' => 'NoSQL', 'value' => 'nosql'},
446
+ {'name' => 'Apps', 'value' => 'apps'},
447
+ {'name' => 'Network', 'value' => 'network'},
448
+ {'name' => 'Messaging', 'value' => 'messaging'},
449
+ {'name' => 'Cache', 'value' => 'cache'},
450
+ {'name' => 'OS', 'value' => 'os'},
451
+ {'name' => 'Cloud', 'value' => 'cloud'},
452
+ {'name' => 'Utility', 'value' => 'utility'}
453
+ ]
454
+ end
455
+
456
+ def add_instance_type_option_types
457
+ [
458
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
459
+ {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 2},
460
+ {'fieldName' => 'category', 'fieldLabel' => 'Category', 'type' => 'select', 'selectOptions' => instance_type_categories, 'required' => true, 'displayOrder' => 3},
461
+ {'fieldName' => 'logo', 'fieldLabel' => 'Icon File', 'type' => 'text', 'displayOrder' => 4},
462
+ {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => 'private', 'displayOrder' => 5},
463
+ {'fieldName' => 'environmentPrefix', 'fieldLabel' => 'Environment Prefix', 'type' => 'text', 'displayOrder' => 6, 'description' => 'Used for exportable environment variables when tying instance types together in app contexts. If not specified a name will be generated.'},
464
+ {'fieldName' => 'hasAutoScale', 'fieldLabel' => 'Enable Scaling (Horizontal)', 'type' => 'checkbox', 'displayOrder' => 7},
465
+ {'fieldName' => 'hasDeployment', 'fieldLabel' => 'Supports Deployments', 'type' => 'checkbox', 'displayOrder' => 8, 'description' => 'Requires a data volume be configured on each version. Files will be copied into this location.'}
466
+ ]
467
+ end
468
+
469
+ def update_instance_type_option_types(instance_type=nil)
470
+ if instance_type
471
+ opts = add_instance_type_option_types
472
+ opts.find {|opt| opt['fieldName'] == 'name'}['defaultValue'] = instance_type['name']
473
+ opts
474
+ else
475
+ add_instance_type_option_types
476
+ end
477
+ end
478
+
479
+ def add_version_option_types
480
+ [
481
+ {'fieldName' => 'versionNumber', 'fieldLabel' => 'Version Number', 'type' => 'text', 'required' => true, 'displayOrder' => 1}
482
+ ]
483
+ end
484
+
485
+ def update_version_option_types
486
+ add_version_option_types
487
+ end
488
+
489
+ def load_balance_protocols
490
+ [
491
+ {'name' => 'None', 'value' => ''},
492
+ {'name' => 'HTTP', 'value' => 'HTTP'},
493
+ {'name' => 'HTTPS', 'value' => 'HTTPS'},
494
+ {'name' => 'TCP', 'value' => 'TCP'}
495
+ ]
496
+ end
497
+
498
+ # Prompts user for exposed ports array
499
+ # returns array of port objects
500
+ def prompt_exposed_ports(options={}, api_client=nil, api_params={})
501
+ #puts "Configure ports:"
502
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
503
+
504
+ ports = []
505
+ port_index = 0
506
+
507
+ has_another_port = options[:options] && options[:options]["exposedPort#{port_index}"]
508
+ add_another_port = has_another_port || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add an exposed port?"))
509
+
510
+ while add_another_port do
511
+
512
+ field_context = "exposedPort#{port_index}"
513
+
514
+ port = {}
515
+ #port['name'] ||= "Port #{port_index}"
516
+
517
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "Port #{port_index} Name", 'required' => false, 'description' => 'Choose a name for this port.', 'defaultValue' => port['name']}], options[:options])
518
+ port['name'] = v_prompt[field_context]['name']
519
+
520
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'port', 'type' => 'number', 'fieldLabel' => "Port #{port_index} Number", 'required' => true, 'description' => 'Choose port number.', 'defaultValue' => (port['port'] ? port['port'].to_i : nil)}], options[:options])
521
+ port['port'] = v_prompt[field_context]['port']
522
+
523
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'loadBalanceProtocol', 'type' => 'select', 'fieldLabel' => "Port #{port_index} LB", 'selectOptions' => load_balance_protocols, 'required' => false, 'skipSingleOption' => true, 'description' => 'Choose a load balance protocol.', 'defaultValue' => port['loadBalanceProtocol']}], options[:options])
524
+ port['loadBalanceProtocol'] = v_prompt[field_context]['loadBalanceProtocol']
525
+
526
+ ports << port
527
+
528
+ port_index += 1
529
+ has_another_port = options[:options] && options[:options]["exposedPort#{port_index}"]
530
+ add_another_port = has_another_port || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add another exposed port?"))
531
+
532
+ end
533
+
534
+
535
+ return ports
536
+ end
537
+
538
+
539
+ end