morpheus-cli 0.9.10 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
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