morpheus-cli 2.9.4 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7efa35bc257cb762e29ce35995cc2291e5d21e54
4
- data.tar.gz: ca41c50847ed8ee3a5e50d7e75c773332b1b64da
3
+ metadata.gz: 575e23105fe6df10494bb38dcdc54e02b582d3ac
4
+ data.tar.gz: fb5e3ea23fc52347ecab160d92442029f6ea3ecb
5
5
  SHA512:
6
- metadata.gz: ff52228f42aca0f2caa7727e9a14f9e1004c3878d62fa5a121efff7f7b240141b9914c64ebdc060828079df1fb2fcbfcfbbbfc5fdb89568b2b4263f4302e372a
7
- data.tar.gz: 37c3835e75478fc8b595e86486b237941798d66806aea5c8d607803cc5f49aad1715b364aa607f46a9e568ec9b8e9e1ac65d7bd70cfb8ab4407b4c78f664039b
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 get_envs(id, options=nil)
30
- url = "#{@base_url}/api/apps/#{id}/envs"
31
- headers = { params: {}, authorization: "Bearer #{@access_token}" }
32
- response = Morpheus::RestClient.execute(method: :get, url: url,
33
- timeout: 30, headers: headers)
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 create_env(id, options)
38
- url = "#{@base_url}/api/apps/#{id}/envs"
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 = {envs: options}
42
- response = Morpheus::RestClient.execute(method: :post, url: url,
43
- timeout: 30, headers: headers, payload: payload.to_json)
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 del_env(id, name)
48
- url = "#{@base_url}/api/apps/#{id}/envs/#{name}"
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
- response = Morpheus::RestClient.execute(method: :delete, url: url,
52
- timeout: 30, headers: headers)
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
- def create(options)
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' }
@@ -13,7 +13,6 @@ class Morpheus::Cli::Accounts
13
13
 
14
14
  def initialize()
15
15
  @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
16
- #@active_groups = ::Morpheus::Cli::Groups.load_group_file
17
16
  end
18
17
 
19
18
  def connect(opts)
@@ -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
- @apps_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).apps
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,remove,stop,start,restart,resize,upgrade,clone,envs,setenv,delenv,firewall_disable,firewall_enable,security_groups,apply_security_groups] [name]"
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
- # JD: this has never worked, it is time to implement it!
35
- # when 'add'
36
- # add(args[1..-1])
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 'stats'
46
- stats(args[1..-1])
47
- when 'details'
48
- details(args[1..-1])
49
- when 'envs'
50
- envs(args[1..-1])
51
- when 'setenv'
52
- setenv(args[1..-1])
53
- when 'delenv'
54
- delenv(args[1..-1])
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 add(args)
81
+ def list(args)
70
82
  options = {}
71
83
  optparse = OptionParser.new do|opts|
72
- opts.banner = "Usage: morpheus apps add NAME TYPE"
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
- app_name = args[0]
83
- instance_type_code = args[1]
84
- instance_type = find_instance_type_by_code(instance_type_code)
85
- if instance_type.nil?
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
- groupId = @active_groups[@appliance_name.to_sym]
91
-
92
- params = {
93
- :servicePlan => nil,
94
- :app => {
95
- :name => app_name,
96
- :site => {
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
- end
121
- layout = instance_type['appTypeLayouts'][layout_selection-1]['id']
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
- print "Selection: "
131
- plan_selection = nil
132
- while true do
133
- plan_selection = $stdin.gets.chomp!
134
- if plan_selection.to_i.to_s == plan_selection
135
- plan_selection = plan_selection.to_i
136
- break
137
- end
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
- params[:servicePlan] = plans['servicePlans'][plan_selection-1]['id']
140
- print "\n"
141
- end
142
-
143
- if !instance_type['config'].nil?
144
- instance_type_config = JSON.parse(instance_type['config'])
145
- if instance_type_config['options'].nil? == false
146
- instance_type_config['options'].each do |opt|
147
- print "#{opt['label']}: "
148
- if(opt['name'].downcase.include?("password"))
149
- params[opt['name']] = STDIN.noecho(&:gets).chomp!
150
- print "\n"
151
- else
152
- params[opt['name']] = $stdin.gets.chomp!
153
- end
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
- end
158
- begin
159
- @apps_interface.create(params)
160
- rescue RestClient::Exception => e
161
- print_rest_exception(e, options)
162
- exit 1
163
- end
164
- list([])
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 logs(args)
177
+ def details(args)
168
178
  options = {}
169
179
  optparse = OptionParser.new do|opts|
170
- opts.banner = "Usage: morpheus apps logs [name] [options]"
171
- build_common_options(opts, options, [:list, :json])
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 = find_app_by_name(args[0])
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
- puts logs
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
- logs['data'].reverse.each do |log_entry|
196
- log_level = ''
197
- case log_entry['level']
198
- when 'INFO'
199
- log_level = "#{blue}#{bold}INFO#{reset}"
200
- when 'DEBUG'
201
- log_level = "#{white}#{bold}DEBUG#{reset}"
202
- when 'WARN'
203
- log_level = "#{yellow}#{bold}WARN#{reset}"
204
- when 'ERROR'
205
- log_level = "#{red}#{bold}ERROR#{reset}"
206
- when 'FATAL'
207
- log_level = "#{red}#{bold}FATAL#{reset}"
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 stats(args)
220
- options = {}
221
- optparse = OptionParser.new do|opts|
222
- opts.banner = "Usage: morpheus apps stats [name]"
223
- build_common_options(opts, options, [:json])
224
- end
225
- optparse.parse(args)
226
- if args.count < 1
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
- connect(options)
231
- begin
232
- app_results = @apps_interface.get({name: args[0]})
233
- if app_results['apps'].empty?
234
- print_red_alert "App not found by name #{args[0]}"
235
- exit 1
236
- end
237
- app = app_results['apps'][0]
238
- app_id = app['id']
239
- stats = app_results['stats'][app_id.to_s]
240
- print "\n" ,cyan, bold, "#{app['name']} (#{app['appType']['name']})\n","==================", reset, "\n\n"
241
- print cyan, "Memory: \t#{Filesize.from("#{stats['usedMemory']} B").pretty} / #{Filesize.from("#{stats['maxMemory']} B").pretty}\n"
242
- print cyan, "Storage: \t#{Filesize.from("#{stats['usedStorage']} B").pretty} / #{Filesize.from("#{stats['maxStorage']} B").pretty}\n\n",reset
243
- puts
244
- rescue RestClient::Exception => e
245
- print_rest_exception(e, options)
246
- exit 1
247
- end
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
- def details(args)
251
- options = {}
252
- optparse = OptionParser.new do|opts|
253
- opts.banner = "Usage: morpheus apps details [name]"
254
- build_common_options(opts, options, [:json])
255
- end
256
- optparse.parse(args)
257
- if args.count < 1
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
- connect(options)
262
- begin
263
- app_results = @apps_interface.get({name: args[0]})
264
- if app_results['apps'].empty?
265
- print_red_alert "App not found by name #{args[0]}"
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 envs(args)
379
+ def remove(args)
282
380
  options = {}
283
381
  optparse = OptionParser.new do|opts|
284
- opts.banner = "Usage: morpheus apps envs [name]"
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
- app_results = @apps_interface.get({name: args[0]})
295
- if app_results['apps'].empty?
296
- print_red_alert "App not found by name #{args[0]}"
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
- tp envs, :name, :value, :export
308
- print "\n" ,cyan, bold, "Importad Envs\n","==================", "\n\n", reset, cyan
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 setenv(args)
320
- options = {}
321
- evar = {name: args[1], value: args[2], export: false}
322
- optparse = OptionParser.new do|opts|
323
- opts.banner = "Usage: morpheus apps setenv INSTANCE NAME VALUE [-e]"
324
- opts.on( '-e', "Exportable" ) do |exportable|
325
- evar[:export] = exportable
326
- end
327
- build_common_options(opts, options, [:json])
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
- connect(options)
335
- begin
336
- app_results = @apps_interface.get({name: args[0]})
337
- if app_results['apps'].empty?
338
- print_red_alert "App not found by name #{args[0]}"
339
- exit 1
340
- end
341
- app = app_results['apps'][0]
342
- app_id = app['id']
343
- params = {}
344
-
345
- @apps_interface.create_env(app_id, [evar])
346
- envs([args[0]])
347
- rescue RestClient::Exception => e
348
- print_rest_exception(e, options)
349
- exit 1
350
- end
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 delenv(args)
459
+ def logs(args)
354
460
  options = {}
355
461
  optparse = OptionParser.new do|opts|
356
- opts.banner = "Usage: morpheus apps setenv INSTANCE NAME"
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 < 2
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
- app_results = @apps_interface.get({name: args[0]})
368
- if app_results['apps'].empty?
369
- print_red_alert "App not found by name #{args[0]}"
370
- exit 1
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] [options]
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
- def find_group_by_name(name)
682
- group_results = @groups_interface.get(name)
683
- if group_results['groups'].empty?
684
- puts "Group not found by name #{name}"
685
- return nil
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 find_instance_type_by_code(code)
691
- instance_type_results = @instance_types_interface.get({code: code})
692
- if instance_type_results['appTypes'].empty?
693
- puts "Instance Type not found by code #{code}"
694
- return nil
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 instance_type_results['appTypes'][0]
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