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