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.
- 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
|