morpheus-cli 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/morpheus +12 -0
- data/lib/morpheus/api/api_client.rb +8 -0
- data/lib/morpheus/api/apps_interface.rb +98 -0
- data/lib/morpheus/api/deploy_interface.rb +89 -0
- data/lib/morpheus/cli.rb +4 -0
- data/lib/morpheus/cli/apps.rb +454 -0
- data/lib/morpheus/cli/credentials.rb +3 -0
- data/lib/morpheus/cli/deploys.rb +145 -0
- data/lib/morpheus/cli/groups.rb +1 -0
- data/lib/morpheus/cli/instance_types.rb +1 -0
- data/lib/morpheus/cli/instances.rb +1 -0
- data/lib/morpheus/cli/servers.rb +1 -0
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/zones.rb +1 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec2a0c7f7c355d73f29610ee9ce39ba75a8aa2ef
|
4
|
+
data.tar.gz: 5459548b4682698ffdd9def0761c1f9c0afb0d3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37f0bf316ee216eb9b0a1709f9eef19ee9f08029cc7675dc088112ee05a369f554ef9ffd80341ad95589eae987de06868f235176a19de95cccc1478adcd5f8a8
|
7
|
+
data.tar.gz: 6e7c76c946b1cd791b24fe036c63ca0dfb271bda465a166ede5e9c7a4889653eeb59d655ff9c04d4f3638548475b703be71a845815b0b9d617332bb76c93e52f
|
data/bin/morpheus
CHANGED
@@ -19,6 +19,18 @@ case ARGV[0]
|
|
19
19
|
Morpheus::Cli::Instances.new().handle(ARGV[1..-1])
|
20
20
|
when 'instance-types'
|
21
21
|
Morpheus::Cli::InstanceTypes.new().handle(ARGV[1..-1])
|
22
|
+
when 'apps'
|
23
|
+
Morpheus::Cli::Apps.new().handle(ARGV[1..-1])
|
24
|
+
when 'deploy'
|
25
|
+
Morpheus::Cli::Deploys.new().handle(ARGV[1..-1])
|
26
|
+
when 'logout'
|
27
|
+
appliance_name, appliance_url = Morpheus::Cli::Remote.active_appliance
|
28
|
+
creds = Morpheus::Cli::Credentials.new(appliance_name,appliance_url).load_saved_credentials()
|
29
|
+
if !creds
|
30
|
+
puts "\nYou are not logged in.\n"
|
31
|
+
else
|
32
|
+
Morpheus::Cli::Credentials.new(appliance_name,appliance_url).logout()
|
33
|
+
end
|
22
34
|
else
|
23
35
|
puts "\nUsage: morpheus [command] [options]\n"
|
24
36
|
end
|
@@ -30,4 +30,12 @@ class Morpheus::APIClient
|
|
30
30
|
def instance_types
|
31
31
|
Morpheus::InstanceTypesInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
|
32
32
|
end
|
33
|
+
|
34
|
+
def apps
|
35
|
+
Morpheus::AppsInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
|
36
|
+
end
|
37
|
+
|
38
|
+
def deploy
|
39
|
+
Morpheus::DeployInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
|
40
|
+
end
|
33
41
|
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest-client'
|
3
|
+
|
4
|
+
class Morpheus::AppsInterface < Morpheus::APIClient
|
5
|
+
def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
|
6
|
+
@access_token = access_token
|
7
|
+
@refresh_token = refresh_token
|
8
|
+
@base_url = base_url
|
9
|
+
@expires_at = expires_at
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def get(options=nil)
|
14
|
+
url = "#{@base_url}/api/apps"
|
15
|
+
headers = { params: {}, authorization: "Bearer #{@access_token}" }
|
16
|
+
|
17
|
+
if options.is_a?(Hash)
|
18
|
+
headers[:params].merge!(options)
|
19
|
+
elsif options.is_a?(Numeric)
|
20
|
+
url = "#{@base_url}/api/apps/#{options}"
|
21
|
+
elsif options.is_a?(String)
|
22
|
+
headers[:params]['name'] = options
|
23
|
+
end
|
24
|
+
response = RestClient::Request.execute(method: :get, url: url,
|
25
|
+
timeout: 10, headers: headers)
|
26
|
+
JSON.parse(response.to_s)
|
27
|
+
end
|
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 = RestClient::Request.execute(method: :get, url: url,
|
33
|
+
timeout: 10, headers: headers)
|
34
|
+
JSON.parse(response.to_s)
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_env(id, options)
|
38
|
+
url = "#{@base_url}/api/apps/#{id}/envs"
|
39
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
40
|
+
|
41
|
+
payload = {envs: options}
|
42
|
+
response = RestClient::Request.execute(method: :post, url: url,
|
43
|
+
timeout: 10, headers: headers, payload: payload.to_json)
|
44
|
+
JSON.parse(response.to_s)
|
45
|
+
end
|
46
|
+
|
47
|
+
def del_env(id, name)
|
48
|
+
url = "#{@base_url}/api/apps/#{id}/envs/#{name}"
|
49
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
50
|
+
|
51
|
+
response = RestClient::Request.execute(method: :delete, url: url,
|
52
|
+
timeout: 10, headers: headers)
|
53
|
+
JSON.parse(response.to_s)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def create(options)
|
58
|
+
url = "#{@base_url}/api/apps"
|
59
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
60
|
+
|
61
|
+
payload = options
|
62
|
+
response = RestClient::Request.execute(method: :post, url: url,
|
63
|
+
timeout: 10, headers: headers, payload: payload.to_json)
|
64
|
+
JSON.parse(response.to_s)
|
65
|
+
end
|
66
|
+
|
67
|
+
def destroy(id)
|
68
|
+
url = "#{@base_url}/api/apps/#{id}"
|
69
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
70
|
+
response = RestClient::Request.execute(method: :delete, url: url,
|
71
|
+
timeout: 10, headers: headers)
|
72
|
+
JSON.parse(response.to_s)
|
73
|
+
end
|
74
|
+
|
75
|
+
def stop(id)
|
76
|
+
url = "#{@base_url}/api/apps/#{id}/stop"
|
77
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
78
|
+
response = RestClient::Request.execute(method: :put, url: url,
|
79
|
+
timeout: 10, headers: headers)
|
80
|
+
JSON.parse(response.to_s)
|
81
|
+
end
|
82
|
+
|
83
|
+
def start(id)
|
84
|
+
url = "#{@base_url}/api/apps/#{id}/start"
|
85
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
86
|
+
response = RestClient::Request.execute(method: :put, url: url,
|
87
|
+
timeout: 10, headers: headers)
|
88
|
+
JSON.parse(response.to_s)
|
89
|
+
end
|
90
|
+
|
91
|
+
def restart(id)
|
92
|
+
url = "#{@base_url}/api/apps/#{id}/restart"
|
93
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
94
|
+
response = RestClient::Request.execute(method: :put, url: url,
|
95
|
+
timeout: 10, headers: headers)
|
96
|
+
JSON.parse(response.to_s)
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest-client'
|
3
|
+
|
4
|
+
class Morpheus::DeployInterface < Morpheus::APIClient
|
5
|
+
def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
|
6
|
+
@access_token = access_token
|
7
|
+
@refresh_token = refresh_token
|
8
|
+
@base_url = base_url
|
9
|
+
@expires_at = expires_at
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def get(instanceId, options=nil)
|
14
|
+
url = "#{@base_url}/api/instances/#{instanceId}/deploy"
|
15
|
+
headers = { params: {}, authorization: "Bearer #{@access_token}" }
|
16
|
+
|
17
|
+
if options.is_a?(Hash)
|
18
|
+
headers[:params].merge!(options)
|
19
|
+
elsif options.is_a?(String)
|
20
|
+
headers[:params]['name'] = options
|
21
|
+
end
|
22
|
+
response = RestClient::Request.execute(method: :get, url: url,
|
23
|
+
timeout: 10, headers: headers)
|
24
|
+
JSON.parse(response.to_s)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def create(instanceId, options=nil)
|
29
|
+
url = "#{@base_url}/api/instances/#{instanceId}/deploy"
|
30
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
31
|
+
|
32
|
+
payload = options || {}
|
33
|
+
response = RestClient::Request.execute(method: :post, url: url,
|
34
|
+
timeout: 10, headers: headers, payload: payload.to_json)
|
35
|
+
JSON.parse(response.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
def list_files(id)
|
39
|
+
url = "#{@base_url}/api/deploy/#{id}/files"
|
40
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
41
|
+
|
42
|
+
payload = options
|
43
|
+
response = RestClient::Request.execute(method: :get, url: url,
|
44
|
+
timeout: 10, headers: headers)
|
45
|
+
JSON.parse(response.to_s)
|
46
|
+
end
|
47
|
+
|
48
|
+
def upload_file(id,path,destination=nil)
|
49
|
+
url = "#{@base_url}/api/deploy/#{id}/files"
|
50
|
+
if !destination.empty?
|
51
|
+
url += "/#{destination}"
|
52
|
+
end
|
53
|
+
|
54
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
55
|
+
|
56
|
+
payload = nil
|
57
|
+
# TODO: Setup payload to be contents of file appropriately
|
58
|
+
response = RestClient::Request.execute(
|
59
|
+
method: :post,
|
60
|
+
url: url,
|
61
|
+
headers: headers,
|
62
|
+
payload: {
|
63
|
+
multipart: true,
|
64
|
+
file: File.new(path, 'rb')
|
65
|
+
})
|
66
|
+
JSON.parse(response.to_s)
|
67
|
+
end
|
68
|
+
|
69
|
+
def destroy(id)
|
70
|
+
url = "#{@base_url}/api/deploy/#{id}"
|
71
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
72
|
+
response = RestClient::Request.execute(method: :delete, url: url,
|
73
|
+
timeout: 10, headers: headers)
|
74
|
+
JSON.parse(response.to_s)
|
75
|
+
end
|
76
|
+
|
77
|
+
def deploy(id, options)
|
78
|
+
url = "#{@base_url}/api/deploy/#{id}/deploy"
|
79
|
+
payload = options
|
80
|
+
if !options[:appDeploy].nil?
|
81
|
+
if !options[:appDeploy][:config].nil?
|
82
|
+
options[:appDeploy][:config] = options[:appDeploy][:config].to_json
|
83
|
+
end
|
84
|
+
end
|
85
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
86
|
+
response = RestClient::Request.execute(method: :post, url: url, headers: headers, payload: payload.to_json)
|
87
|
+
JSON.parse(response.to_s)
|
88
|
+
end
|
89
|
+
end
|
data/lib/morpheus/cli.rb
CHANGED
@@ -8,6 +8,8 @@ module Morpheus
|
|
8
8
|
require 'morpheus/api/servers_interface'
|
9
9
|
require 'morpheus/api/instances_interface'
|
10
10
|
require 'morpheus/api/instance_types_interface'
|
11
|
+
require 'morpheus/api/apps_interface'
|
12
|
+
require 'morpheus/api/deploy_interface'
|
11
13
|
require 'morpheus/cli/credentials'
|
12
14
|
require 'morpheus/cli/error_handler'
|
13
15
|
require 'morpheus/cli/remote'
|
@@ -15,6 +17,8 @@ module Morpheus
|
|
15
17
|
require 'morpheus/cli/zones'
|
16
18
|
require 'morpheus/cli/servers'
|
17
19
|
require 'morpheus/cli/instances'
|
20
|
+
require 'morpheus/cli/apps'
|
21
|
+
require 'morpheus/cli/deploys'
|
18
22
|
require 'morpheus/cli/instance_types'
|
19
23
|
# Your code goes here...
|
20
24
|
end
|
@@ -0,0 +1,454 @@
|
|
1
|
+
# require 'yaml'
|
2
|
+
require 'io/console'
|
3
|
+
require 'rest_client'
|
4
|
+
require 'term/ansicolor'
|
5
|
+
require 'optparse'
|
6
|
+
require 'filesize'
|
7
|
+
require 'table_print'
|
8
|
+
|
9
|
+
class Morpheus::Cli::Apps
|
10
|
+
include Term::ANSIColor
|
11
|
+
def initialize()
|
12
|
+
@appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
13
|
+
@access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials()
|
14
|
+
@apps_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).apps
|
15
|
+
@instance_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
|
16
|
+
@apps_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).apps
|
17
|
+
@groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).groups
|
18
|
+
@active_groups = ::Morpheus::Cli::Groups.load_group_file
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def handle(args)
|
23
|
+
if @access_token.empty?
|
24
|
+
print red,bold, "\nInvalid Credentials. Unable to acquire access token. Please verify your credentials and try again.\n\n",reset
|
25
|
+
return 1
|
26
|
+
end
|
27
|
+
if args.empty?
|
28
|
+
puts "\nUsage: morpheus apps [list,add,remove,stop,start,restart,resize,upgrade,clone,envs,setenv,delenv] [name]\n\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
case args[0]
|
32
|
+
when 'list'
|
33
|
+
list(args[1..-1])
|
34
|
+
when 'add'
|
35
|
+
add(args[1..-1])
|
36
|
+
when 'remove'
|
37
|
+
remove(args[1..-1])
|
38
|
+
when 'stop'
|
39
|
+
stop(args[1..-1])
|
40
|
+
when 'start'
|
41
|
+
start(args[1..-1])
|
42
|
+
when 'restart'
|
43
|
+
restart(args[1..-1])
|
44
|
+
when 'stats'
|
45
|
+
stats(args[1..-1])
|
46
|
+
when 'details'
|
47
|
+
details(args[1..-1])
|
48
|
+
when 'envs'
|
49
|
+
envs(args[1..-1])
|
50
|
+
when 'setenv'
|
51
|
+
setenv(args[1..-1])
|
52
|
+
when 'delenv'
|
53
|
+
delenv(args[1..-1])
|
54
|
+
else
|
55
|
+
puts "\nUsage: morpheus apps [list,add,remove,stop,start,restart,resize,upgrade,clone,envs,setenv,delenv] [name]\n\n"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def add(args)
|
60
|
+
if args.count < 2
|
61
|
+
puts "\nUsage: morpheus apps add NAME TYPE\n\n"
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
app_name = args[0]
|
66
|
+
instance_type_code = args[1]
|
67
|
+
instance_type = find_instance_type_by_code(instance_type_code)
|
68
|
+
if instance_type.nil?
|
69
|
+
print reset,"\n\n"
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
73
|
+
groupId = @active_groups[@appliance_name.to_sym]
|
74
|
+
|
75
|
+
options = {
|
76
|
+
:servicePlan => nil,
|
77
|
+
:app => {
|
78
|
+
:name => app_name,
|
79
|
+
:site => {
|
80
|
+
:id => groupId
|
81
|
+
},
|
82
|
+
:appType => {
|
83
|
+
:code => instance_type_code
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
instance_type['appTypeLayouts'].sort! { |x,y| y['sortOrder'] <=> x['sortOrder'] }
|
89
|
+
puts "Configurations: "
|
90
|
+
instance_type['appTypeLayouts'].each_with_index do |layout, index|
|
91
|
+
puts " #{index+1}) #{layout['name']} (#{layout['code']})"
|
92
|
+
end
|
93
|
+
print "Selection: "
|
94
|
+
layout_selection = nil
|
95
|
+
layout = nil
|
96
|
+
while true do
|
97
|
+
layout_selection = $stdin.gets.chomp!
|
98
|
+
if layout_selection.to_i.to_s == layout_selection
|
99
|
+
layout_selection = layout_selection.to_i
|
100
|
+
break
|
101
|
+
end
|
102
|
+
end
|
103
|
+
layout = instance_type['appTypeLayouts'][layout_selection-1]['id']
|
104
|
+
options[:app][:layout] = {id: layout}
|
105
|
+
print "\n"
|
106
|
+
if options[:servicePlan].nil?
|
107
|
+
plans = @instance_types_interface.service_plans(layout)
|
108
|
+
puts "Select a Plan: "
|
109
|
+
plans['servicePlans'].each_with_index do |plan, index|
|
110
|
+
puts " #{index+1}) #{plan['name']}"
|
111
|
+
end
|
112
|
+
print "Selection: "
|
113
|
+
plan_selection = nil
|
114
|
+
while true do
|
115
|
+
plan_selection = $stdin.gets.chomp!
|
116
|
+
if plan_selection.to_i.to_s == plan_selection
|
117
|
+
plan_selection = plan_selection.to_i
|
118
|
+
break
|
119
|
+
end
|
120
|
+
end
|
121
|
+
options[:servicePlan] = plans['servicePlans'][plan_selection-1]['id']
|
122
|
+
print "\n"
|
123
|
+
end
|
124
|
+
|
125
|
+
if !instance_type['config'].nil?
|
126
|
+
instance_type_config = JSON.parse(instance_type['config'])
|
127
|
+
if instance_type_config['options'].nil? == false
|
128
|
+
instance_type_config['options'].each do |opt|
|
129
|
+
print "#{opt['label']}: "
|
130
|
+
if(opt['name'].downcase.include?("password"))
|
131
|
+
options[opt['name']] = STDIN.noecho(&:gets).chomp!
|
132
|
+
print "\n"
|
133
|
+
else
|
134
|
+
options[opt['name']] = $stdin.gets.chomp!
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
begin
|
141
|
+
@apps_interface.create(options)
|
142
|
+
rescue => e
|
143
|
+
if e.response.code == 400
|
144
|
+
error = JSON.parse(e.response.to_s)
|
145
|
+
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
146
|
+
else
|
147
|
+
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
148
|
+
end
|
149
|
+
return nil
|
150
|
+
end
|
151
|
+
list([])
|
152
|
+
end
|
153
|
+
|
154
|
+
def stats(args)
|
155
|
+
if args.count < 1
|
156
|
+
puts "\nUsage: morpheus apps stats [name]\n\n"
|
157
|
+
return
|
158
|
+
end
|
159
|
+
begin
|
160
|
+
app_results = @apps_interface.get({name: args[0]})
|
161
|
+
if app_results['apps'].empty?
|
162
|
+
puts "Instance not found by name #{args[0]}"
|
163
|
+
return
|
164
|
+
end
|
165
|
+
app = app_results['apps'][0]
|
166
|
+
app_id = app['id']
|
167
|
+
stats = app_results['stats'][app_id.to_s]
|
168
|
+
print "\n" ,cyan, bold, "#{app['name']} (#{app['appType']['name']})\n","==================", reset, "\n\n"
|
169
|
+
print cyan, "Memory: \t#{Filesize.from("#{stats['usedMemory']} B").pretty} / #{Filesize.from("#{stats['maxMemory']} B").pretty}\n"
|
170
|
+
print cyan, "Storage: \t#{Filesize.from("#{stats['usedStorage']} B").pretty} / #{Filesize.from("#{stats['maxStorage']} B").pretty}\n\n",reset
|
171
|
+
puts
|
172
|
+
rescue RestClient::Exception => e
|
173
|
+
if e.response.code == 400
|
174
|
+
error = JSON.parse(e.response.to_s)
|
175
|
+
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
176
|
+
else
|
177
|
+
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
178
|
+
end
|
179
|
+
return nil
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def details(args)
|
184
|
+
if args.count < 1
|
185
|
+
puts "\nUsage: morpheus apps stats [name]\n\n"
|
186
|
+
return
|
187
|
+
end
|
188
|
+
begin
|
189
|
+
app_results = @apps_interface.get({name: args[0]})
|
190
|
+
if app_results['apps'].empty?
|
191
|
+
puts "Instance not found by name #{args[0]}"
|
192
|
+
return
|
193
|
+
end
|
194
|
+
app = app_results['apps'][0]
|
195
|
+
app_id = app['id']
|
196
|
+
stats = app_results['stats'][app_id.to_s]
|
197
|
+
print "\n" ,cyan, bold, "#{app['name']} (#{app['appType']['name']})\n","==================", reset, "\n\n"
|
198
|
+
print cyan, "Memory: \t#{Filesize.from("#{stats['usedMemory']} B").pretty} / #{Filesize.from("#{stats['maxMemory']} B").pretty}\n"
|
199
|
+
print cyan, "Storage: \t#{Filesize.from("#{stats['usedStorage']} B").pretty} / #{Filesize.from("#{stats['maxStorage']} B").pretty}\n\n",reset
|
200
|
+
puts app
|
201
|
+
rescue RestClient::Exception => e
|
202
|
+
if e.response.code == 400
|
203
|
+
error = JSON.parse(e.response.to_s)
|
204
|
+
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
205
|
+
else
|
206
|
+
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
207
|
+
end
|
208
|
+
return nil
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def envs(args)
|
213
|
+
if args.count < 1
|
214
|
+
puts "\nUsage: morpheus apps envs [name]\n\n"
|
215
|
+
return
|
216
|
+
end
|
217
|
+
begin
|
218
|
+
app_results = @apps_interface.get({name: args[0]})
|
219
|
+
if app_results['apps'].empty?
|
220
|
+
puts "Instance not found by name #{args[0]}"
|
221
|
+
return
|
222
|
+
end
|
223
|
+
app = app_results['apps'][0]
|
224
|
+
app_id = app['id']
|
225
|
+
env_results = @apps_interface.get_envs(app_id)
|
226
|
+
print "\n" ,cyan, bold, "#{app['name']} (#{app['appType']['name']})\n","==================", "\n\n", reset, cyan
|
227
|
+
envs = env_results['envs'] || {}
|
228
|
+
if env_results['readOnlyEnvs']
|
229
|
+
envs += env_results['readOnlyEnvs'].map { |k,v| {:name => k, :value => k.downcase.include?("password") ? "********" : v, :export => true}}
|
230
|
+
end
|
231
|
+
tp envs, :name, :value, :export
|
232
|
+
print "\n" ,cyan, bold, "Importad Envs\n","==================", "\n\n", reset, cyan
|
233
|
+
imported_envs = env_results['importedEnvs'].map { |k,v| {:name => k, :value => k.downcase.include?("password") ? "********" : v}}
|
234
|
+
tp imported_envs
|
235
|
+
print reset, "\n"
|
236
|
+
|
237
|
+
rescue RestClient::Exception => e
|
238
|
+
if e.response.code == 400
|
239
|
+
error = JSON.parse(e.response.to_s)
|
240
|
+
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
241
|
+
else
|
242
|
+
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
243
|
+
end
|
244
|
+
return nil
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def setenv(args)
|
249
|
+
if args.count < 3
|
250
|
+
puts "\nUsage: morpheus apps setenv INSTANCE NAME VALUE [-e]\n\n"
|
251
|
+
return
|
252
|
+
end
|
253
|
+
begin
|
254
|
+
app_results = @apps_interface.get({name: args[0]})
|
255
|
+
if app_results['apps'].empty?
|
256
|
+
puts "Instance not found by name #{args[0]}"
|
257
|
+
return
|
258
|
+
end
|
259
|
+
app = app_results['apps'][0]
|
260
|
+
app_id = app['id']
|
261
|
+
evar = {name: args[1], value: args[2], export: false}
|
262
|
+
params = {}
|
263
|
+
optparse = OptionParser.new do|opts|
|
264
|
+
opts.on( '-e', "Exportable" ) do |exportable|
|
265
|
+
evar[:export] = exportable
|
266
|
+
end
|
267
|
+
end
|
268
|
+
optparse.parse(args)
|
269
|
+
|
270
|
+
@apps_interface.create_env(app_id, [evar])
|
271
|
+
envs([args[0]])
|
272
|
+
rescue RestClient::Exception => e
|
273
|
+
if e.response.code == 400
|
274
|
+
error = JSON.parse(e.response.to_s)
|
275
|
+
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
276
|
+
else
|
277
|
+
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
278
|
+
end
|
279
|
+
return nil
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def delenv(args)
|
284
|
+
if args.count < 2
|
285
|
+
puts "\nUsage: morpheus apps setenv INSTANCE NAME\n\n"
|
286
|
+
return
|
287
|
+
end
|
288
|
+
begin
|
289
|
+
app_results = @apps_interface.get({name: args[0]})
|
290
|
+
if app_results['apps'].empty?
|
291
|
+
puts "Instance not found by name #{args[0]}"
|
292
|
+
return
|
293
|
+
end
|
294
|
+
app = app_results['apps'][0]
|
295
|
+
app_id = app['id']
|
296
|
+
name = args[1]
|
297
|
+
|
298
|
+
@apps_interface.del_env(app_id, name)
|
299
|
+
envs([args[0]])
|
300
|
+
rescue RestClient::Exception => e
|
301
|
+
if e.response.code == 400
|
302
|
+
error = JSON.parse(e.response.to_s)
|
303
|
+
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
304
|
+
else
|
305
|
+
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
306
|
+
end
|
307
|
+
return nil
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def stop(args)
|
312
|
+
if args.count < 1
|
313
|
+
puts "\nUsage: morpheus apps stop [name]\n\n"
|
314
|
+
return
|
315
|
+
end
|
316
|
+
begin
|
317
|
+
app_results = @apps_interface.get({name: args[0]})
|
318
|
+
if app_results['apps'].empty?
|
319
|
+
puts "Instance not found by name #{args[0]}"
|
320
|
+
return
|
321
|
+
end
|
322
|
+
@apps_interface.stop(app_results['apps'][0]['id'])
|
323
|
+
list([])
|
324
|
+
rescue RestClient::Exception => e
|
325
|
+
if e.response.code == 400
|
326
|
+
error = JSON.parse(e.response.to_s)
|
327
|
+
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
328
|
+
else
|
329
|
+
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
330
|
+
end
|
331
|
+
return nil
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def start(args)
|
336
|
+
if args.count < 1
|
337
|
+
puts "\nUsage: morpheus apps start [name]\n\n"
|
338
|
+
return
|
339
|
+
end
|
340
|
+
begin
|
341
|
+
app_results = @apps_interface.get({name: args[0]})
|
342
|
+
if app_results['apps'].empty?
|
343
|
+
puts "Instance not found by name #{args[0]}"
|
344
|
+
return
|
345
|
+
end
|
346
|
+
@apps_interface.start(app_results['apps'][0]['id'])
|
347
|
+
list([])
|
348
|
+
rescue RestClient::Exception => e
|
349
|
+
if e.response.code == 400
|
350
|
+
error = JSON.parse(e.response.to_s)
|
351
|
+
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
352
|
+
else
|
353
|
+
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
354
|
+
end
|
355
|
+
return nil
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def restart(args)
|
360
|
+
if args.count < 1
|
361
|
+
puts "\nUsage: morpheus apps restart [name]\n\n"
|
362
|
+
return
|
363
|
+
end
|
364
|
+
begin
|
365
|
+
app_results = @apps_interface.get({name: args[0]})
|
366
|
+
if app_results['apps'].empty?
|
367
|
+
puts "Instance not found by name #{args[0]}"
|
368
|
+
return
|
369
|
+
end
|
370
|
+
@apps_interface.restart(app_results['apps'][0]['id'])
|
371
|
+
list([])
|
372
|
+
rescue RestClient::Exception => e
|
373
|
+
if e.response.code == 400
|
374
|
+
error = JSON.parse(e.response.to_s)
|
375
|
+
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
376
|
+
else
|
377
|
+
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
378
|
+
end
|
379
|
+
return nil
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
def list(args)
|
384
|
+
options = {}
|
385
|
+
optparse = OptionParser.new do|opts|
|
386
|
+
opts.on( '-g', '--group GROUP', "Group Name" ) do |group|
|
387
|
+
options[:group] = group
|
388
|
+
end
|
389
|
+
end
|
390
|
+
optparse.parse(args)
|
391
|
+
begin
|
392
|
+
params = {}
|
393
|
+
|
394
|
+
json_response = @apps_interface.get(params)
|
395
|
+
apps = json_response['apps']
|
396
|
+
print "\n" ,cyan, bold, "Morpheus Instances\n","==================", reset, "\n\n"
|
397
|
+
if apps.empty?
|
398
|
+
puts yellow,"No apps currently configured.",reset
|
399
|
+
else
|
400
|
+
apps.each do |app|
|
401
|
+
print cyan, "= #{app['name']} (#{app['appType']['name']}) - #{app['status']['name']}\n"
|
402
|
+
end
|
403
|
+
end
|
404
|
+
print reset,"\n\n"
|
405
|
+
|
406
|
+
rescue => e
|
407
|
+
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
408
|
+
return nil
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def remove(args)
|
413
|
+
if args.count < 1
|
414
|
+
puts "\nUsage: morpheus apps remove [name]\n\n"
|
415
|
+
return
|
416
|
+
end
|
417
|
+
begin
|
418
|
+
app_results = @apps_interface.get({name: args[0]})
|
419
|
+
if app_results['apps'].empty?
|
420
|
+
puts "Instance not found by name #{args[0]}"
|
421
|
+
return
|
422
|
+
end
|
423
|
+
@apps_interface.destroy(app_results['apps'][0]['id'])
|
424
|
+
list([])
|
425
|
+
rescue RestClient::Exception => e
|
426
|
+
if e.response.code == 400
|
427
|
+
error = JSON.parse(e.response.to_s)
|
428
|
+
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
429
|
+
else
|
430
|
+
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
431
|
+
end
|
432
|
+
return nil
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
private
|
437
|
+
def find_group_by_name(name)
|
438
|
+
group_results = @groups_interface.get(name)
|
439
|
+
if group_results['groups'].empty?
|
440
|
+
puts "Group not found by name #{name}"
|
441
|
+
return nil
|
442
|
+
end
|
443
|
+
return group_results['groups'][0]
|
444
|
+
end
|
445
|
+
|
446
|
+
def find_instance_type_by_code(code)
|
447
|
+
instance_type_results = @instance_types_interface.get({code: code})
|
448
|
+
if instance_type_results['appTypes'].empty?
|
449
|
+
puts "Instance Type not found by code #{code}"
|
450
|
+
return nil
|
451
|
+
end
|
452
|
+
return instance_type_results['appTypes'][0]
|
453
|
+
end
|
454
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# require 'yaml'
|
2
|
+
require 'io/console'
|
3
|
+
require 'rest_client'
|
4
|
+
require 'term/ansicolor'
|
5
|
+
require 'optparse'
|
6
|
+
require 'filesize'
|
7
|
+
require 'table_print'
|
8
|
+
|
9
|
+
class Morpheus::Cli::Deploys
|
10
|
+
include Term::ANSIColor
|
11
|
+
def initialize()
|
12
|
+
@appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
13
|
+
@access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials()
|
14
|
+
@instances_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instances
|
15
|
+
@deploy_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).deploy
|
16
|
+
@groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).groups
|
17
|
+
@active_groups = ::Morpheus::Cli::Groups.load_group_file
|
18
|
+
end
|
19
|
+
|
20
|
+
def handle(args)
|
21
|
+
if @access_token.empty?
|
22
|
+
print red,bold, "\nInvalid Credentials. Unable to acquire access token. Please verify your credentials and try again.\n\n",reset
|
23
|
+
return 1
|
24
|
+
end
|
25
|
+
|
26
|
+
deploy(args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def deploy(args)
|
30
|
+
environment = 'production'
|
31
|
+
if args.count > 0
|
32
|
+
environment = args[0]
|
33
|
+
end
|
34
|
+
if load_deploy_file().nil?
|
35
|
+
puts "Morpheus Deploy File `morpheus.yml` not detected. Please create one and try again."
|
36
|
+
return
|
37
|
+
end
|
38
|
+
|
39
|
+
deploy_args = merged_deploy_args(environment)
|
40
|
+
if deploy_args['name'].nil?
|
41
|
+
puts "Instance not specified. Please specify the instance name and try again."
|
42
|
+
return
|
43
|
+
end
|
44
|
+
|
45
|
+
instance_results = @instances_interface.get(name: deploy_args[:name])
|
46
|
+
if instance_results['instances'].empty?
|
47
|
+
puts "Instance not found by name #{args[0]}"
|
48
|
+
return
|
49
|
+
end
|
50
|
+
instance = instance_results['instances'][0]
|
51
|
+
instance_id = instance['id']
|
52
|
+
print "\n" ,cyan, bold, "Morpheus Deployment\n","==================", reset, "\n\n"
|
53
|
+
|
54
|
+
if !deploy_args['script'].nil?
|
55
|
+
print cyan, bold, " - Executing Pre Deploy Script...", reset, "\n"
|
56
|
+
|
57
|
+
if !system(deploy_args['script'])
|
58
|
+
puts "Error executing pre script..."
|
59
|
+
return
|
60
|
+
end
|
61
|
+
end
|
62
|
+
# Create a new deployment record
|
63
|
+
deploy_result = @deploy_interface.create(instance_id)
|
64
|
+
app_deploy = deploy_result['appDeploy']
|
65
|
+
deployment_id = app_deploy['id']
|
66
|
+
|
67
|
+
# Upload Files
|
68
|
+
print "\n",cyan, bold, "Uploading Files...", reset, "\n"
|
69
|
+
current_working_dir = Dir.pwd
|
70
|
+
deploy_args['files'].each do |fmap|
|
71
|
+
Dir.chdir(fmap['path'] || current_working_dir)
|
72
|
+
files = Dir.glob(fmap['pattern'] || '**/*')
|
73
|
+
files.each do |file|
|
74
|
+
if File.file?(file)
|
75
|
+
print cyan,bold, " - Uploading #{file} ...", reset, "\n"
|
76
|
+
destination = file.split("/")[0..-2].join("/")
|
77
|
+
@deploy_interface.upload_file(deployment_id,file,destination)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
print cyan, bold, "Upload Complete!", reset, "\n"
|
82
|
+
Dir.chdir(current_working_dir)
|
83
|
+
|
84
|
+
if !deploy_args['post_script'].nil?
|
85
|
+
print cyan, bold, "Executing Post Script...", reset, "\n"
|
86
|
+
if !system(deploy_args['post_script'])
|
87
|
+
puts "Error executing post script..."
|
88
|
+
return
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
deploy_payload = {}
|
93
|
+
|
94
|
+
if deploy_args['options']
|
95
|
+
deploy_payload = {
|
96
|
+
appDeploy: {
|
97
|
+
config: deploy_args['options']
|
98
|
+
}
|
99
|
+
}
|
100
|
+
end
|
101
|
+
print cyan, bold, "Deploying to Servers...", reset, "\n"
|
102
|
+
@deploy_interface.deploy(deployment_id,deploy_payload)
|
103
|
+
print cyan, bold, "Deploy Successful!", reset, "\n"
|
104
|
+
end
|
105
|
+
|
106
|
+
def list(args)
|
107
|
+
end
|
108
|
+
|
109
|
+
def rollback(args)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Loads a morpheus.yml file from within the current working directory.
|
113
|
+
# This file contains information necessary in the project to perform a deployment via the cli
|
114
|
+
#
|
115
|
+
# === Example File Attributes
|
116
|
+
# * +script+ - The initial script to run before uploading files
|
117
|
+
# * +name+ - The instance name we are deploying to (can be overridden in CLI)
|
118
|
+
# * +remote+ - Optional remote appliance name we are connecting to
|
119
|
+
# * +files+ - List of file patterns to use for uploading files and their target destination
|
120
|
+
# * +options+ - Map of deployment options depending on deployment type
|
121
|
+
# * +post_script+ - A post operation script to be run on the local machine
|
122
|
+
# * +stage_deploy+ - If set to true the deploy will only be staged and not actually run
|
123
|
+
#
|
124
|
+
# +NOTE: + It is also possible to nest these properties in an "environments" map to override based on a passed environment deploy name
|
125
|
+
#
|
126
|
+
def load_deploy_file
|
127
|
+
if !File.exist? "morpheus.yml"
|
128
|
+
puts "No morheus.yml file detected in the current directory. Nothing to do."
|
129
|
+
return nil
|
130
|
+
end
|
131
|
+
|
132
|
+
@deploy_file = YAML.load_file("morpheus.yml")
|
133
|
+
return @deploy_file
|
134
|
+
end
|
135
|
+
|
136
|
+
def merged_deploy_args(environment)
|
137
|
+
environment = environment || production
|
138
|
+
|
139
|
+
deploy_args = @deploy_file.reject { |key,value| key == 'environment'}
|
140
|
+
if !@deploy_file['environment'].nil? && !@deploy_file['environment'][environment].nil?
|
141
|
+
deploy_args = deploy_args.merge(@deploy_file['environment'][environment])
|
142
|
+
end
|
143
|
+
return deploy_args
|
144
|
+
end
|
145
|
+
end
|
data/lib/morpheus/cli/groups.rb
CHANGED
data/lib/morpheus/cli/servers.rb
CHANGED
data/lib/morpheus/cli/version.rb
CHANGED
data/lib/morpheus/cli/zones.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: morpheus-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Estes
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -110,13 +110,17 @@ files:
|
|
110
110
|
- Rakefile
|
111
111
|
- bin/morpheus
|
112
112
|
- lib/morpheus/api/api_client.rb
|
113
|
+
- lib/morpheus/api/apps_interface.rb
|
114
|
+
- lib/morpheus/api/deploy_interface.rb
|
113
115
|
- lib/morpheus/api/groups_interface.rb
|
114
116
|
- lib/morpheus/api/instance_types_interface.rb
|
115
117
|
- lib/morpheus/api/instances_interface.rb
|
116
118
|
- lib/morpheus/api/servers_interface.rb
|
117
119
|
- lib/morpheus/api/zones_interface.rb
|
118
120
|
- lib/morpheus/cli.rb
|
121
|
+
- lib/morpheus/cli/apps.rb
|
119
122
|
- lib/morpheus/cli/credentials.rb
|
123
|
+
- lib/morpheus/cli/deploys.rb
|
120
124
|
- lib/morpheus/cli/error_handler.rb
|
121
125
|
- lib/morpheus/cli/groups.rb
|
122
126
|
- lib/morpheus/cli/instance_types.rb
|