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.
- checksums.yaml +4 -4
- data/bin/morpheus +48 -33
- data/lib/morpheus/api/api_client.rb +12 -0
- data/lib/morpheus/api/custom_instance_types.rb +55 -0
- data/lib/morpheus/api/custom_instance_types_interface.rb +133 -0
- data/lib/morpheus/api/dashboard_interface.rb +37 -0
- data/lib/morpheus/api/users_interface.rb +17 -0
- data/lib/morpheus/api/whoami_interface.rb +20 -0
- data/lib/morpheus/cli.rb +13 -1
- data/lib/morpheus/cli/app_templates.rb +1 -1
- data/lib/morpheus/cli/cli_command.rb +22 -1
- data/lib/morpheus/cli/cli_registry.rb +1 -1
- data/lib/morpheus/cli/credentials.rb +33 -11
- data/lib/morpheus/cli/dashboard_command.rb +74 -0
- data/lib/morpheus/cli/instance_types.rb +4 -2
- data/lib/morpheus/cli/key_pairs.rb +1 -1
- data/lib/morpheus/cli/library.rb +539 -0
- data/lib/morpheus/cli/login.rb +57 -0
- data/lib/morpheus/cli/logout.rb +61 -0
- data/lib/morpheus/cli/mixins/accounts_helper.rb +52 -10
- data/lib/morpheus/cli/mixins/print_helper.rb +23 -16
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/whoami_helper.rb +34 -0
- data/lib/morpheus/cli/option_types.rb +15 -1
- data/lib/morpheus/cli/recent_activity_command.rb +83 -0
- data/lib/morpheus/cli/remote.rb +71 -28
- data/lib/morpheus/cli/roles.rb +89 -24
- data/lib/morpheus/cli/shell.rb +24 -11
- data/lib/morpheus/cli/users.rb +166 -24
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/version_command.rb +45 -0
- data/lib/morpheus/cli/whoami.rb +139 -0
- data/lib/morpheus/logging.rb +78 -0
- 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
|
@@ -1,10 +1,15 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'io/console'
|
3
|
-
require '
|
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
|
-
|
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
|
-
|
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}
|
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
|
-
|
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
|
-
|
50
|
-
|
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"
|
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"
|
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
|