morpheus-cli 2.9.4 → 2.10.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/lib/morpheus/api/apps_interface.rb +21 -17
- data/lib/morpheus/api/instances_interface.rb +10 -0
- data/lib/morpheus/cli/accounts.rb +0 -1
- data/lib/morpheus/cli/apps.rb +462 -320
- data/lib/morpheus/cli/dashboard_command.rb +0 -1
- data/lib/morpheus/cli/deploys.rb +0 -2
- data/lib/morpheus/cli/hosts.rb +124 -135
- data/lib/morpheus/cli/instance_types.rb +0 -2
- data/lib/morpheus/cli/instances.rb +204 -197
- data/lib/morpheus/cli/mixins/print_helper.rb +14 -0
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +246 -0
- data/lib/morpheus/cli/recent_activity_command.rb +0 -1
- data/lib/morpheus/cli/users.rb +0 -1
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/whoami.rb +0 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 575e23105fe6df10494bb38dcdc54e02b582d3ac
|
4
|
+
data.tar.gz: fb5e3ea23fc52347ecab160d92442029f6ea3ecb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 873500f5e460eaf1c1e56e5e223b3e1c52d83955786d7d4b5ed7642025e3001758a0dbdd5c6ca59cdf3b8385ffca0b473cf401d779a8166675f0c996d0ca6a1a
|
7
|
+
data.tar.gz: 667d412138a4cb25766b8a55cea03cf20749caad2ad10d97ebd39ccf4da99d5830dd5b5d4ba629f6ceb66a8e4e771a4e9ff8b06abbb8ee90731ba60d4ddd9514
|
@@ -26,36 +26,38 @@ class Morpheus::AppsInterface < Morpheus::APIClient
|
|
26
26
|
JSON.parse(response.to_s)
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
30
|
-
url = "#{@base_url}/api/apps
|
31
|
-
headers = {
|
32
|
-
|
33
|
-
|
29
|
+
def create(options)
|
30
|
+
url = "#{@base_url}/api/apps"
|
31
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
32
|
+
|
33
|
+
payload = options
|
34
|
+
response = Morpheus::RestClient.execute(method: :post, url: url,
|
35
|
+
timeout: 30, headers: headers, payload: payload.to_json)
|
34
36
|
JSON.parse(response.to_s)
|
35
37
|
end
|
36
38
|
|
37
|
-
def
|
38
|
-
url = "#{@base_url}/api/apps/#{
|
39
|
+
def update(app_id, options)
|
40
|
+
url = "#{@base_url}/api/apps/#{app_id}"
|
39
41
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
40
42
|
|
41
|
-
payload =
|
42
|
-
response = Morpheus::RestClient.execute(method: :
|
43
|
-
|
43
|
+
payload = options
|
44
|
+
response = Morpheus::RestClient.execute(method: :put, url: url,
|
45
|
+
timeout: 30, headers: headers, payload: payload.to_json)
|
44
46
|
JSON.parse(response.to_s)
|
45
47
|
end
|
46
48
|
|
47
|
-
def
|
48
|
-
url = "#{@base_url}/api/apps/#{
|
49
|
+
def add_instance(app_id, options)
|
50
|
+
url = "#{@base_url}/api/apps/#{app_id}/add-instance"
|
49
51
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
+
payload = options
|
54
|
+
response = Morpheus::RestClient.execute(method: :post, url: url,
|
55
|
+
timeout: 30, headers: headers, payload: payload.to_json)
|
53
56
|
JSON.parse(response.to_s)
|
54
57
|
end
|
55
58
|
|
56
|
-
|
57
|
-
|
58
|
-
url = "#{@base_url}/api/apps"
|
59
|
+
def remove_instance(app_id, options)
|
60
|
+
url = "#{@base_url}/api/apps/#{app_id}/remove-instance"
|
59
61
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
60
62
|
|
61
63
|
payload = options
|
@@ -72,6 +74,7 @@ class Morpheus::AppsInterface < Morpheus::APIClient
|
|
72
74
|
JSON.parse(response.to_s)
|
73
75
|
end
|
74
76
|
|
77
|
+
=begin
|
75
78
|
def stop(id)
|
76
79
|
url = "#{@base_url}/api/apps/#{id}/stop"
|
77
80
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
@@ -95,6 +98,7 @@ class Morpheus::AppsInterface < Morpheus::APIClient
|
|
95
98
|
timeout: 30, headers: headers)
|
96
99
|
JSON.parse(response.to_s)
|
97
100
|
end
|
101
|
+
=end
|
98
102
|
|
99
103
|
def firewall_disable(id)
|
100
104
|
url = "#{@base_url}/api/apps/#{id}/security-groups/disable"
|
@@ -63,6 +63,16 @@ class Morpheus::InstancesInterface < Morpheus::APIClient
|
|
63
63
|
JSON.parse(response.to_s)
|
64
64
|
end
|
65
65
|
|
66
|
+
def update(id, options)
|
67
|
+
url = "#{@base_url}/api/instances/#{id}"
|
68
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
69
|
+
|
70
|
+
payload = options
|
71
|
+
response = Morpheus::RestClient.execute(method: :put, url: url,
|
72
|
+
timeout: 30, headers: headers,verify_ssl: false, payload: payload.to_json)
|
73
|
+
JSON.parse(response.to_s)
|
74
|
+
end
|
75
|
+
|
66
76
|
def destroy(id, params = {})
|
67
77
|
url = "#{@base_url}/api/instances/#{id}"
|
68
78
|
headers = {:params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
data/lib/morpheus/cli/apps.rb
CHANGED
@@ -5,9 +5,11 @@ require 'optparse'
|
|
5
5
|
require 'filesize'
|
6
6
|
require 'table_print'
|
7
7
|
require 'morpheus/cli/cli_command'
|
8
|
+
require 'morpheus/cli/mixins/provisioning_helper'
|
8
9
|
|
9
10
|
class Morpheus::Cli::Apps
|
10
11
|
include Morpheus::Cli::CliCommand
|
12
|
+
include Morpheus::Cli::ProvisioningHelper
|
11
13
|
|
12
14
|
def initialize()
|
13
15
|
@appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
@@ -19,39 +21,49 @@ class Morpheus::Cli::Apps
|
|
19
21
|
print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
|
20
22
|
exit 1
|
21
23
|
end
|
24
|
+
@api_client = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url)
|
22
25
|
@apps_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).apps
|
23
26
|
@instance_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
|
24
|
-
@
|
27
|
+
@instances_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instances
|
28
|
+
@options_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).options
|
25
29
|
@groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).groups
|
30
|
+
@logs_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).logs
|
26
31
|
@active_groups = ::Morpheus::Cli::Groups.load_group_file
|
27
32
|
end
|
28
33
|
|
29
34
|
def handle(args)
|
30
|
-
usage = "Usage: morpheus apps [list,
|
35
|
+
usage = "Usage: morpheus apps [list,details,add,update,remove,add-instance,remove-instance,logs,firewall_disable,firewall_enable,security_groups,apply_security_groups] [name]"
|
31
36
|
case args[0]
|
32
37
|
when 'list'
|
33
38
|
list(args[1..-1])
|
34
|
-
|
35
|
-
|
36
|
-
|
39
|
+
when 'details'
|
40
|
+
details(args[1..-1])
|
41
|
+
when 'add'
|
42
|
+
add(args[1..-1])
|
43
|
+
when 'update'
|
44
|
+
update(args[1..-1])
|
37
45
|
when 'remove'
|
38
46
|
remove(args[1..-1])
|
47
|
+
when 'add-instance'
|
48
|
+
add_instance(args[1..-1])
|
49
|
+
when 'remove-instance'
|
50
|
+
remove_instance(args[1..-1])
|
39
51
|
when 'stop'
|
40
52
|
stop(args[1..-1])
|
41
53
|
when 'start'
|
42
54
|
start(args[1..-1])
|
43
55
|
when 'restart'
|
44
56
|
restart(args[1..-1])
|
45
|
-
when '
|
46
|
-
|
47
|
-
when '
|
48
|
-
|
49
|
-
when 'envs'
|
50
|
-
|
51
|
-
when 'setenv'
|
52
|
-
|
53
|
-
when 'delenv'
|
54
|
-
|
57
|
+
when 'logs'
|
58
|
+
logs(args[1..-1])
|
59
|
+
# when 'stats'
|
60
|
+
# stats(args[1..-1])
|
61
|
+
# when 'envs'
|
62
|
+
# envs(args[1..-1])
|
63
|
+
# when 'setenv'
|
64
|
+
# setenv(args[1..-1])
|
65
|
+
# when 'delenv'
|
66
|
+
# delenv(args[1..-1])
|
55
67
|
when 'firewall_disable'
|
56
68
|
firewall_disable(args[1..-1])
|
57
69
|
when 'firewall_enable'
|
@@ -66,109 +78,107 @@ class Morpheus::Cli::Apps
|
|
66
78
|
end
|
67
79
|
end
|
68
80
|
|
69
|
-
def
|
81
|
+
def list(args)
|
70
82
|
options = {}
|
71
83
|
optparse = OptionParser.new do|opts|
|
72
|
-
opts.banner = "Usage: morpheus apps
|
73
|
-
build_common_options(opts, options, [])
|
84
|
+
opts.banner = "Usage: morpheus apps list"
|
85
|
+
build_common_options(opts, options, [:list, :json])
|
74
86
|
end
|
75
87
|
optparse.parse(args)
|
76
|
-
if args.count < 2
|
77
|
-
puts "\n#{optparse.banner}\n\n"
|
78
|
-
exit 1
|
79
|
-
end
|
80
88
|
connect(options)
|
89
|
+
begin
|
90
|
+
params = {}
|
91
|
+
[:phrase, :offset, :max, :sort, :direction].each do |k|
|
92
|
+
params[k] = options[k] unless options[k].nil?
|
93
|
+
end
|
81
94
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
95
|
+
json_response = @apps_interface.get(params)
|
96
|
+
|
97
|
+
if options[:json]
|
98
|
+
print JSON.pretty_generate(json_response)
|
99
|
+
print "\n"
|
100
|
+
return
|
101
|
+
end
|
102
|
+
apps = json_response['apps']
|
103
|
+
print "\n" ,cyan, bold, "Morpheus Apps\n","==================", reset, "\n\n"
|
104
|
+
if apps.empty?
|
105
|
+
puts yellow,"No apps currently configured.",reset
|
106
|
+
else
|
107
|
+
print_apps_table(apps)
|
108
|
+
end
|
86
109
|
print reset,"\n\n"
|
110
|
+
rescue RestClient::Exception => e
|
111
|
+
print_rest_exception(e, options)
|
87
112
|
exit 1
|
88
113
|
end
|
114
|
+
end
|
89
115
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
:
|
97
|
-
:id => groupId
|
98
|
-
},
|
99
|
-
:appType => {
|
100
|
-
:code => instance_type_code
|
101
|
-
}
|
102
|
-
}
|
103
|
-
}
|
104
|
-
|
105
|
-
# JD: this is old and busted.. appTypeLayouts is not returned..
|
106
|
-
instance_type['appTypeLayouts'].sort! { |x,y| y['sortOrder'] <=> x['sortOrder'] }
|
107
|
-
puts "Configurations: "
|
108
|
-
instance_type['appTypeLayouts'].each_with_index do |layout, index|
|
109
|
-
puts " #{index+1}) #{layout['name']} (#{layout['code']})"
|
110
|
-
end
|
111
|
-
print "Selection: "
|
112
|
-
layout_selection = nil
|
113
|
-
layout = nil
|
114
|
-
while true do
|
115
|
-
layout_selection = $stdin.gets.chomp!
|
116
|
-
if layout_selection.to_i.to_s == layout_selection
|
117
|
-
layout_selection = layout_selection.to_i
|
118
|
-
break
|
116
|
+
def add(args)
|
117
|
+
usage = "Usage: morpheus apps add"
|
118
|
+
options = {}
|
119
|
+
optparse = OptionParser.new do|opts|
|
120
|
+
opts.banner = usage
|
121
|
+
opts.on( '-g', '--group GROUP', "Group Name" ) do |val|
|
122
|
+
options[:group_name] = val
|
119
123
|
end
|
120
|
-
|
121
|
-
|
122
|
-
params[:app][:layout] = {id: layout}
|
123
|
-
print "\n"
|
124
|
-
if params[:servicePlan].nil?
|
125
|
-
plans = @instance_types_interface.service_plans(layout)
|
126
|
-
puts "Select a Plan: "
|
127
|
-
plans['servicePlans'].each_with_index do |plan, index|
|
128
|
-
puts " #{index+1}) #{plan['name']}"
|
124
|
+
opts.on( '-G', '--group-id ID', "Group Id" ) do |val|
|
125
|
+
options[:group_id] = val
|
129
126
|
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
127
|
+
build_common_options(opts, options, [:options, :json, :dry_run])
|
128
|
+
end
|
129
|
+
optparse.parse(args)
|
130
|
+
connect(options)
|
131
|
+
begin
|
132
|
+
|
133
|
+
# use active group by default
|
134
|
+
if !options[:group_name] && !options[:group_id]
|
135
|
+
options[:group_id] = @active_groups[@appliance_name.to_sym]
|
138
136
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
end
|
137
|
+
group = find_group_from_options(options)
|
138
|
+
|
139
|
+
payload = {
|
140
|
+
'app' => {}
|
141
|
+
}
|
142
|
+
|
143
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Enter a name for this app'}], options[:options])
|
144
|
+
payload['app']['name'] = v_prompt['name']
|
145
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false}], options[:options])
|
146
|
+
payload['app']['description'] = v_prompt['description']
|
147
|
+
if group
|
148
|
+
payload['app']['site'] = {id: group["id"]}
|
149
|
+
else
|
150
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'selectOptions' => get_available_groups(), 'required' => true}], options[:options])
|
151
|
+
payload['app']['site'] = {id: v_prompt["group"]}
|
156
152
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
153
|
+
|
154
|
+
# todo: allow adding instances with creation..
|
155
|
+
|
156
|
+
if options[:dry_run]
|
157
|
+
print_dry_run("POST #{@appliance_url}/api/apps", payload)
|
158
|
+
return
|
159
|
+
end
|
160
|
+
json_response = @apps_interface.create(payload)
|
161
|
+
if options[:json]
|
162
|
+
print JSON.pretty_generate(json_response)
|
163
|
+
print "\n"
|
164
|
+
else
|
165
|
+
print_green_success "Added app #{payload['app']['name']}"
|
166
|
+
list([])
|
167
|
+
# details_options = [payload['app']['name']]
|
168
|
+
# details(details_options)
|
169
|
+
end
|
170
|
+
|
171
|
+
rescue RestClient::Exception => e
|
172
|
+
print_rest_exception(e, options)
|
173
|
+
exit 1
|
174
|
+
end
|
165
175
|
end
|
166
176
|
|
167
|
-
def
|
177
|
+
def details(args)
|
168
178
|
options = {}
|
169
179
|
optparse = OptionParser.new do|opts|
|
170
|
-
opts.banner = "Usage: morpheus apps
|
171
|
-
build_common_options(opts, options, [:
|
180
|
+
opts.banner = "Usage: morpheus apps details [name]"
|
181
|
+
build_common_options(opts, options, [:json])
|
172
182
|
end
|
173
183
|
optparse.parse(args)
|
174
184
|
if args.count < 1
|
@@ -177,111 +187,199 @@ class Morpheus::Cli::Apps
|
|
177
187
|
end
|
178
188
|
connect(options)
|
179
189
|
begin
|
180
|
-
app =
|
181
|
-
containers = []
|
182
|
-
app['appTiers'].each do |app_tier|
|
183
|
-
app_tier['appInstances'].each do |app_instance|
|
184
|
-
containers += app_instance['instance']['containers']
|
185
|
-
end
|
186
|
-
end
|
187
|
-
params = {}
|
188
|
-
[:phrase, :offset, :max, :sort, :direction].each do |k|
|
189
|
-
params[k] = options[k] unless options[k].nil?
|
190
|
-
end
|
191
|
-
logs = @logs_interface.container_logs(containers, params)
|
190
|
+
app = find_app_by_name_or_id(args[0])
|
192
191
|
if options[:json]
|
193
|
-
|
192
|
+
print JSON.pretty_generate({app: app})
|
193
|
+
return
|
194
|
+
end
|
195
|
+
|
196
|
+
print "\n" ,cyan, bold, "App Details\n","==================", reset, "\n\n"
|
197
|
+
print cyan
|
198
|
+
puts "ID: #{app['id']}"
|
199
|
+
puts "Name: #{app['name']}"
|
200
|
+
puts "Description: #{app['description']}"
|
201
|
+
puts "Account: #{app['account'] ? app['account']['name'] : ''}"
|
202
|
+
# puts "Group: #{app['siteId']}"
|
203
|
+
|
204
|
+
stats = app['stats']
|
205
|
+
print cyan, "Memory: \t#{Filesize.from("#{stats['usedMemory']} B").pretty} / #{Filesize.from("#{stats['maxMemory']} B").pretty}\n"
|
206
|
+
print cyan, "Storage: \t#{Filesize.from("#{stats['usedStorage']} B").pretty} / #{Filesize.from("#{stats['maxStorage']} B").pretty}\n\n",reset
|
207
|
+
|
208
|
+
app_tiers = app['appTiers']
|
209
|
+
if app_tiers.empty?
|
210
|
+
puts yellow, "This app is empty", reset
|
194
211
|
else
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
212
|
+
app_tiers.each do |app_tier|
|
213
|
+
print "\n" ,cyan, bold, "Tier: #{app_tier['tier']['name']}\n","==================", reset, "\n\n"
|
214
|
+
print cyan
|
215
|
+
instances = (app_tier['appInstances'] || []).collect {|it| it['instance']}
|
216
|
+
if instances.empty?
|
217
|
+
puts yellow, "This tier is empty", reset
|
218
|
+
else
|
219
|
+
instance_table = instances.collect do |instance|
|
220
|
+
status_string = instance['status']
|
221
|
+
if status_string == 'running'
|
222
|
+
status_string = "#{green}#{status_string.upcase}#{cyan}"
|
223
|
+
elsif status_string == 'stopped' or status_string == 'failed'
|
224
|
+
status_string = "#{red}#{status_string.upcase}#{cyan}"
|
225
|
+
elsif status_string == 'unknown'
|
226
|
+
status_string = "#{white}#{status_string.upcase}#{cyan}"
|
227
|
+
else
|
228
|
+
status_string = "#{yellow}#{status_string.upcase}#{cyan}"
|
229
|
+
end
|
230
|
+
connection_string = ''
|
231
|
+
if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
|
232
|
+
connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
|
233
|
+
end
|
234
|
+
{id: instance['id'], name: instance['name'], connection: connection_string, environment: instance['instanceContext'], nodes: instance['containers'].count, status: status_string, type: instance['instanceType']['name'], group: !instance['group'].nil? ? instance['group']['name'] : nil, cloud: !instance['cloud'].nil? ? instance['cloud']['name'] : nil}
|
235
|
+
end
|
236
|
+
tp instance_table, :id, :name, :cloud, :type, :environment, :nodes, :connection, :status
|
208
237
|
end
|
209
|
-
puts "[#{log_entry['ts']}] #{log_level} - #{log_entry['message']}"
|
210
238
|
end
|
211
|
-
print reset,"\n"
|
212
239
|
end
|
240
|
+
print cyan
|
241
|
+
|
242
|
+
print reset,"\n"
|
243
|
+
|
213
244
|
rescue RestClient::Exception => e
|
214
245
|
print_rest_exception(e, options)
|
215
246
|
exit 1
|
216
247
|
end
|
217
248
|
end
|
218
249
|
|
219
|
-
def
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
250
|
+
def update(args)
|
251
|
+
usage = "Usage: morpheus apps update [name] [options]"
|
252
|
+
options = {}
|
253
|
+
optparse = OptionParser.new do|opts|
|
254
|
+
opts.banner = usage
|
255
|
+
build_common_options(opts, options, [:options, :json, :dry_run])
|
256
|
+
end
|
257
|
+
optparse.parse(args)
|
258
|
+
if args.count < 1
|
227
259
|
puts "\n#{optparse.banner}\n\n"
|
228
260
|
exit 1
|
229
261
|
end
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
262
|
+
connect(options)
|
263
|
+
|
264
|
+
begin
|
265
|
+
|
266
|
+
app = find_app_by_name_or_id(args[0])
|
267
|
+
|
268
|
+
# group = find_group_from_options(options)
|
269
|
+
|
270
|
+
payload = {
|
271
|
+
'app' => {id: app["id"]}
|
272
|
+
}
|
273
|
+
|
274
|
+
update_app_option_types = [
|
275
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Enter a name for this app'},
|
276
|
+
{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false}
|
277
|
+
]
|
278
|
+
|
279
|
+
params = options[:options] || {}
|
280
|
+
|
281
|
+
if params.empty?
|
282
|
+
puts "\n#{usage}\n"
|
283
|
+
option_lines = update_app_option_types.collect {|it| "\t-O #{it['fieldName']}=\"value\"" }.join("\n")
|
284
|
+
puts "\nAvailable Options:\n#{option_lines}\n\n"
|
285
|
+
exit 1
|
286
|
+
end
|
287
|
+
|
288
|
+
#puts "parsed params is : #{params.inspect}"
|
289
|
+
app_keys = ['name', 'description']
|
290
|
+
params = params.select {|k,v| app_keys.include?(k) }
|
291
|
+
payload['app'].merge!(params)
|
292
|
+
|
293
|
+
if options[:dry_run]
|
294
|
+
print_dry_run("PUT #{@appliance_url}/api/apps/#{app['id']}", payload)
|
295
|
+
return
|
296
|
+
end
|
297
|
+
json_response = @apps_interface.update(app["id"], payload)
|
298
|
+
if options[:json]
|
299
|
+
print JSON.pretty_generate(json_response)
|
300
|
+
print "\n"
|
301
|
+
else
|
302
|
+
print_green_success "Updated app #{app['name']}"
|
303
|
+
list([])
|
304
|
+
# details_options = [payload['app']['name']]
|
305
|
+
# details(details_options)
|
306
|
+
end
|
307
|
+
|
308
|
+
rescue RestClient::Exception => e
|
309
|
+
print_rest_exception(e, options)
|
310
|
+
exit 1
|
311
|
+
end
|
248
312
|
end
|
249
313
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
314
|
+
|
315
|
+
def add_instance(args)
|
316
|
+
usage = "Usage: morpheus apps add-instance [name] [instance] [tier]"
|
317
|
+
options = {}
|
318
|
+
optparse = OptionParser.new do|opts|
|
319
|
+
opts.banner = usage
|
320
|
+
build_common_options(opts, options, [:options, :json, :dry_run])
|
321
|
+
end
|
322
|
+
optparse.parse(args)
|
323
|
+
if args.count < 1
|
258
324
|
puts "\n#{optparse.banner}\n\n"
|
259
325
|
exit 1
|
260
326
|
end
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
if
|
265
|
-
|
266
|
-
exit 1
|
327
|
+
# optional [tier] and [instance] arguments
|
328
|
+
if args[1] && args[1] !~ /\A\-/
|
329
|
+
options[:instance_name] = args[1]
|
330
|
+
if args[2] && args[2] !~ /\A\-/
|
331
|
+
options[:tier_name] = args[2]
|
267
332
|
end
|
268
|
-
app = app_results['apps'][0]
|
269
|
-
app_id = app['id']
|
270
|
-
stats = app_results['stats'][app_id.to_s]
|
271
|
-
print "\n" ,cyan, bold, "#{app['name']} (#{app['appType']['name']})\n","==================", reset, "\n\n"
|
272
|
-
print cyan, "Memory: \t#{Filesize.from("#{stats['usedMemory']} B").pretty} / #{Filesize.from("#{stats['maxMemory']} B").pretty}\n"
|
273
|
-
print cyan, "Storage: \t#{Filesize.from("#{stats['usedStorage']} B").pretty} / #{Filesize.from("#{stats['maxStorage']} B").pretty}\n\n",reset
|
274
|
-
puts app
|
275
|
-
rescue RestClient::Exception => e
|
276
|
-
print_rest_exception(e, options)
|
277
|
-
exit 1
|
278
333
|
end
|
334
|
+
connect(options)
|
335
|
+
begin
|
336
|
+
|
337
|
+
app = find_app_by_name_or_id(args[0])
|
338
|
+
|
339
|
+
# Only supports adding an existing instance right now..
|
340
|
+
|
341
|
+
payload = {}
|
342
|
+
|
343
|
+
if options[:instance_name]
|
344
|
+
instance = find_instance_by_name_or_id(options[:instance_name])
|
345
|
+
else
|
346
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'instance', 'fieldLabel' => 'Instance', 'type' => 'text', 'required' => true, 'description' => 'Enter the instance name or id'}], options[:options])
|
347
|
+
instance = find_instance_by_name_or_id(v_prompt['instance'])
|
348
|
+
end
|
349
|
+
payload[:instanceId] = instance['id']
|
350
|
+
|
351
|
+
if options[:tier_name]
|
352
|
+
payload[:tierName] = options[:tier_name]
|
353
|
+
else
|
354
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tier', 'fieldLabel' => 'Tier', 'type' => 'text', 'required' => true, 'description' => 'Enter the name of the tier'}], options[:options])
|
355
|
+
payload[:tierName] = v_prompt['tier']
|
356
|
+
end
|
357
|
+
|
358
|
+
if options[:dry_run]
|
359
|
+
print_dry_run("POST #{@appliance_url}/api/apps/#{app['id']}/add-instance", payload)
|
360
|
+
return
|
361
|
+
end
|
362
|
+
json_response = @apps_interface.add_instance(app['id'], payload)
|
363
|
+
if options[:json]
|
364
|
+
print JSON.pretty_generate(json_response)
|
365
|
+
print "\n"
|
366
|
+
else
|
367
|
+
print_green_success "Added instance #{instance['name']} to app #{app['name']}"
|
368
|
+
list([])
|
369
|
+
# details_options = [app['name']]
|
370
|
+
# details(details_options)
|
371
|
+
end
|
372
|
+
|
373
|
+
rescue RestClient::Exception => e
|
374
|
+
print_rest_exception(e, options)
|
375
|
+
exit 1
|
376
|
+
end
|
279
377
|
end
|
280
378
|
|
281
|
-
def
|
379
|
+
def remove(args)
|
282
380
|
options = {}
|
283
381
|
optparse = OptionParser.new do|opts|
|
284
|
-
opts.banner = "Usage: morpheus apps
|
382
|
+
opts.banner = "Usage: morpheus apps remove [name]"
|
285
383
|
build_common_options(opts, options, [:json])
|
286
384
|
end
|
287
385
|
optparse.parse(args)
|
@@ -290,97 +388,128 @@ class Morpheus::Cli::Apps
|
|
290
388
|
exit 1
|
291
389
|
end
|
292
390
|
connect(options)
|
391
|
+
|
293
392
|
begin
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
return
|
298
|
-
end
|
299
|
-
app = app_results['apps'][0]
|
300
|
-
app_id = app['id']
|
301
|
-
env_results = @apps_interface.get_envs(app_id)
|
302
|
-
print "\n" ,cyan, bold, "#{app['name']} (#{app['appType']['name']})\n","==================", "\n\n", reset, cyan
|
303
|
-
envs = env_results['envs'] || {}
|
304
|
-
if env_results['readOnlyEnvs']
|
305
|
-
envs += env_results['readOnlyEnvs'].map { |k,v| {:name => k, :value => k.downcase.include?("password") ? "********" : v, :export => true}}
|
393
|
+
app = find_app_by_name_or_id(args[0])
|
394
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the app '#{app['name']}'?", options)
|
395
|
+
exit 1
|
306
396
|
end
|
307
|
-
|
308
|
-
|
309
|
-
imported_envs = env_results['importedEnvs'].map { |k,v| {:name => k, :value => k.downcase.include?("password") ? "********" : v}}
|
310
|
-
tp imported_envs
|
311
|
-
print reset, "\n"
|
312
|
-
|
397
|
+
@apps_interface.destroy(app_results['app']['id'])
|
398
|
+
list([])
|
313
399
|
rescue RestClient::Exception => e
|
314
400
|
print_rest_exception(e, options)
|
315
401
|
exit 1
|
316
402
|
end
|
317
403
|
end
|
318
404
|
|
319
|
-
def
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
end
|
329
|
-
optparse.parse(args)
|
330
|
-
if args.count < 3
|
405
|
+
def remove_instance(args)
|
406
|
+
usage = "Usage: morpheus apps remove-instance [name] [instance]"
|
407
|
+
options = {}
|
408
|
+
optparse = OptionParser.new do|opts|
|
409
|
+
opts.banner = usage
|
410
|
+
build_common_options(opts, options, [:options, :json, :dry_run])
|
411
|
+
end
|
412
|
+
optparse.parse(args)
|
413
|
+
if args.count < 1
|
331
414
|
puts "\n#{optparse.banner}\n\n"
|
332
415
|
exit 1
|
333
416
|
end
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
417
|
+
# optional [tier] and [instance] arguments
|
418
|
+
if args[1] && args[1] !~ /\A\-/
|
419
|
+
options[:instance_name] = args[1]
|
420
|
+
end
|
421
|
+
connect(options)
|
422
|
+
begin
|
423
|
+
|
424
|
+
app = find_app_by_name_or_id(args[0])
|
425
|
+
|
426
|
+
payload = {}
|
427
|
+
|
428
|
+
if options[:instance_name]
|
429
|
+
instance = find_instance_by_name_or_id(options[:instance_name])
|
430
|
+
else
|
431
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'instance', 'fieldLabel' => 'Instance', 'type' => 'text', 'required' => true, 'description' => 'Enter the instance name or id'}], options[:options])
|
432
|
+
instance = find_instance_by_name_or_id(v_prompt['instance'])
|
433
|
+
end
|
434
|
+
payload[:instanceId] = instance['id']
|
435
|
+
|
436
|
+
if options[:dry_run]
|
437
|
+
print_dry_run("POST #{@appliance_url}/api/apps/#{app['id']}/remove-instance", payload)
|
438
|
+
return
|
439
|
+
end
|
440
|
+
|
441
|
+
json_response = @apps_interface.remove_instance(app['id'], payload)
|
442
|
+
|
443
|
+
if options[:json]
|
444
|
+
print JSON.pretty_generate(json_response)
|
445
|
+
print "\n"
|
446
|
+
else
|
447
|
+
print_green_success "Removed instance #{instance['name']} from app #{app['name']}"
|
448
|
+
list([])
|
449
|
+
# details_options = [app['name']]
|
450
|
+
# details(details_options)
|
451
|
+
end
|
452
|
+
|
453
|
+
rescue RestClient::Exception => e
|
454
|
+
print_rest_exception(e, options)
|
455
|
+
exit 1
|
456
|
+
end
|
351
457
|
end
|
352
458
|
|
353
|
-
def
|
459
|
+
def logs(args)
|
354
460
|
options = {}
|
355
461
|
optparse = OptionParser.new do|opts|
|
356
|
-
opts.banner = "Usage: morpheus apps
|
357
|
-
build_common_options(opts, options, [:json])
|
462
|
+
opts.banner = "Usage: morpheus apps logs [name]"
|
463
|
+
build_common_options(opts, options, [:list, :json])
|
358
464
|
end
|
359
465
|
optparse.parse(args)
|
360
|
-
if args.count <
|
466
|
+
if args.count < 1
|
361
467
|
puts "\n#{optparse.banner}\n\n"
|
362
468
|
exit 1
|
363
469
|
end
|
364
470
|
connect(options)
|
365
|
-
|
366
471
|
begin
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
472
|
+
app = find_app_by_name_or_id(args[0])
|
473
|
+
containers = []
|
474
|
+
app['appTiers'].each do |app_tier|
|
475
|
+
app_tier['appInstances'].each do |app_instance|
|
476
|
+
containers += app_instance['instance']['containers']
|
477
|
+
end
|
478
|
+
end
|
479
|
+
params = {}
|
480
|
+
[:phrase, :offset, :max, :sort, :direction].each do |k|
|
481
|
+
params[k] = options[k] unless options[k].nil?
|
482
|
+
end
|
483
|
+
logs = @logs_interface.container_logs(containers, params)
|
484
|
+
if options[:json]
|
485
|
+
print JSON.pretty_generate(logs)
|
486
|
+
print "\n"
|
487
|
+
else
|
488
|
+
logs['data'].reverse.each do |log_entry|
|
489
|
+
log_level = ''
|
490
|
+
case log_entry['level']
|
491
|
+
when 'INFO'
|
492
|
+
log_level = "#{blue}#{bold}INFO#{reset}"
|
493
|
+
when 'DEBUG'
|
494
|
+
log_level = "#{white}#{bold}DEBUG#{reset}"
|
495
|
+
when 'WARN'
|
496
|
+
log_level = "#{yellow}#{bold}WARN#{reset}"
|
497
|
+
when 'ERROR'
|
498
|
+
log_level = "#{red}#{bold}ERROR#{reset}"
|
499
|
+
when 'FATAL'
|
500
|
+
log_level = "#{red}#{bold}FATAL#{reset}"
|
501
|
+
end
|
502
|
+
puts "[#{log_entry['ts']}] #{log_level} - #{log_entry['message']}"
|
503
|
+
end
|
504
|
+
print reset,"\n"
|
371
505
|
end
|
372
|
-
app = app_results['apps'][0]
|
373
|
-
app_id = app['id']
|
374
|
-
name = args[1]
|
375
|
-
|
376
|
-
@apps_interface.del_env(app_id, name)
|
377
|
-
envs([args[0]])
|
378
506
|
rescue RestClient::Exception => e
|
379
507
|
print_rest_exception(e, options)
|
380
508
|
exit 1
|
381
509
|
end
|
382
510
|
end
|
383
511
|
|
512
|
+
=begin
|
384
513
|
def stop(args)
|
385
514
|
options = {}
|
386
515
|
optparse = OptionParser.new do|opts|
|
@@ -458,70 +587,7 @@ class Morpheus::Cli::Apps
|
|
458
587
|
exit 1
|
459
588
|
end
|
460
589
|
end
|
461
|
-
|
462
|
-
def list(args)
|
463
|
-
options = {}
|
464
|
-
optparse = OptionParser.new do|opts|
|
465
|
-
opts.banner = "Usage: morpheus apps list"
|
466
|
-
opts.on( '-g', '--group GROUP', "Group Name" ) do |group|
|
467
|
-
options[:group] = group
|
468
|
-
end
|
469
|
-
build_common_options(opts, options, [:json])
|
470
|
-
end
|
471
|
-
optparse.parse(args)
|
472
|
-
|
473
|
-
connect(options)
|
474
|
-
|
475
|
-
begin
|
476
|
-
params = {}
|
477
|
-
[:phrase, :offset, :max, :sort, :direction].each do |k|
|
478
|
-
params[k] = options[k] unless options[k].nil?
|
479
|
-
end
|
480
|
-
# todo: include options[:group] in params?
|
481
|
-
json_response = @apps_interface.get(params)
|
482
|
-
apps = json_response['apps']
|
483
|
-
print "\n" ,cyan, bold, "Morpheus Apps\n","==================", reset, "\n\n"
|
484
|
-
if apps.empty?
|
485
|
-
puts yellow,"No apps currently configured.",reset
|
486
|
-
else
|
487
|
-
apps.each do |app|
|
488
|
-
print cyan, "= #{app['name']}\n"
|
489
|
-
end
|
490
|
-
end
|
491
|
-
print reset,"\n\n"
|
492
|
-
|
493
|
-
rescue => e
|
494
|
-
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
495
|
-
return nil
|
496
|
-
end
|
497
|
-
end
|
498
|
-
|
499
|
-
def remove(args)
|
500
|
-
options = {}
|
501
|
-
optparse = OptionParser.new do|opts|
|
502
|
-
opts.banner = "Usage: morpheus apps remove [name]"
|
503
|
-
build_common_options(opts, options, [:json])
|
504
|
-
end
|
505
|
-
optparse.parse(args)
|
506
|
-
if args.count < 1
|
507
|
-
puts "\n#{optparse.banner}\n\n"
|
508
|
-
exit 1
|
509
|
-
end
|
510
|
-
connect(options)
|
511
|
-
|
512
|
-
begin
|
513
|
-
app_results = @apps_interface.get({name: args[0]})
|
514
|
-
if app_results['apps'].empty?
|
515
|
-
print_red_alert "App not found by name #{args[0]}"
|
516
|
-
exit 1
|
517
|
-
end
|
518
|
-
@apps_interface.destroy(app_results['apps'][0]['id'])
|
519
|
-
list([])
|
520
|
-
rescue RestClient::Exception => e
|
521
|
-
print_rest_exception(e, options)
|
522
|
-
exit 1
|
523
|
-
end
|
524
|
-
end
|
590
|
+
=end
|
525
591
|
|
526
592
|
def firewall_disable(args)
|
527
593
|
options = {}
|
@@ -620,7 +686,7 @@ class Morpheus::Cli::Apps
|
|
620
686
|
|
621
687
|
def apply_security_groups(args)
|
622
688
|
usage = <<-EOF
|
623
|
-
Usage: morpheus apps apply_security_groups [name]
|
689
|
+
Usage: morpheus apps apply_security_groups [name]
|
624
690
|
EOF
|
625
691
|
|
626
692
|
options = {}
|
@@ -668,7 +734,16 @@ EOF
|
|
668
734
|
end
|
669
735
|
end
|
670
736
|
|
671
|
-
private
|
737
|
+
private
|
738
|
+
|
739
|
+
def find_app_by_id(id)
|
740
|
+
app_results = @apps_interface.get(id.to_i)
|
741
|
+
if app_results['app'].empty?
|
742
|
+
print_red_alert "App not found by id #{id}"
|
743
|
+
exit 1
|
744
|
+
end
|
745
|
+
return app_results['app']
|
746
|
+
end
|
672
747
|
|
673
748
|
def find_app_by_name(name)
|
674
749
|
app_results = @apps_interface.get({name: name})
|
@@ -678,21 +753,88 @@ private
|
|
678
753
|
end
|
679
754
|
return app_results['apps'][0]
|
680
755
|
end
|
681
|
-
|
682
|
-
|
683
|
-
if
|
684
|
-
|
685
|
-
|
756
|
+
|
757
|
+
def find_app_by_name_or_id(val)
|
758
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
759
|
+
return find_app_by_id(val)
|
760
|
+
else
|
761
|
+
return find_app_by_name(val)
|
686
762
|
end
|
687
|
-
return group_results['groups'][0]
|
688
763
|
end
|
689
764
|
|
690
|
-
def
|
691
|
-
|
692
|
-
if
|
693
|
-
|
694
|
-
|
765
|
+
def find_instance_by_id(id)
|
766
|
+
instance_results = @instances_interface.get(id.to_i)
|
767
|
+
if instance_results['instance'].empty?
|
768
|
+
print_red_alert "Instance not found by id #{id}"
|
769
|
+
exit 1
|
770
|
+
end
|
771
|
+
return instance_results['instance']
|
772
|
+
end
|
773
|
+
|
774
|
+
def find_instance_by_name(name)
|
775
|
+
instance_results = @instances_interface.get({name: name})
|
776
|
+
if instance_results['instances'].empty?
|
777
|
+
print_red_alert "Instance not found by name #{name}"
|
778
|
+
exit 1
|
695
779
|
end
|
696
|
-
return
|
780
|
+
return instance_results['instances'][0]
|
697
781
|
end
|
782
|
+
|
783
|
+
def find_instance_by_name_or_id(val)
|
784
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
785
|
+
return find_instance_by_id(val)
|
786
|
+
else
|
787
|
+
return find_instance_by_name(val)
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
def print_apps_table(apps, opts={})
|
792
|
+
table_color = opts[:color] || cyan
|
793
|
+
rows = apps.collect do |app|
|
794
|
+
instances_str = (app['instanceCount'].to_i == 1) ? "1 Instance" : "#{app['instanceCount']} Instances"
|
795
|
+
containers_str = (app['containerCount'].to_i == 1) ? "1 Container" : "#{app['containerCount']} Containers"
|
796
|
+
status_string = app['status']
|
797
|
+
if app['instanceCount'].to_i == 0
|
798
|
+
# show this instead of WARNING
|
799
|
+
status_string = "#{white}EMPTY#{table_color}"
|
800
|
+
elsif status_string == 'running'
|
801
|
+
status_string = "#{green}#{status_string.upcase}#{table_color}"
|
802
|
+
elsif status_string == 'stopped' or status_string == 'failed'
|
803
|
+
status_string = "#{red}#{status_string.upcase}#{table_color}"
|
804
|
+
elsif status_string == 'unknown'
|
805
|
+
status_string = "#{white}#{status_string.upcase}#{table_color}"
|
806
|
+
else
|
807
|
+
status_string = "#{yellow}#{status_string.upcase}#{table_color}"
|
808
|
+
end
|
809
|
+
|
810
|
+
{
|
811
|
+
id: app['id'],
|
812
|
+
name: app['name'],
|
813
|
+
instances: instances_str,
|
814
|
+
containers: containers_str,
|
815
|
+
account: app['account'] ? app['account']['name'] : nil,
|
816
|
+
status: status_string,
|
817
|
+
#dateCreated: format_local_dt(app['dateCreated'])
|
818
|
+
}
|
819
|
+
end
|
820
|
+
|
821
|
+
print table_color
|
822
|
+
tp rows, [
|
823
|
+
:id,
|
824
|
+
:name,
|
825
|
+
:instances,
|
826
|
+
:containers,
|
827
|
+
#:account,
|
828
|
+
:status,
|
829
|
+
#{:dateCreated => {:display_name => "Date Created"} }
|
830
|
+
]
|
831
|
+
print reset
|
832
|
+
end
|
833
|
+
|
834
|
+
def generate_id(len=16)
|
835
|
+
id = ""
|
836
|
+
len.times { id << (1 + rand(9)).to_s }
|
837
|
+
id
|
838
|
+
end
|
839
|
+
|
698
840
|
end
|