vmc 0.3.23 → 0.4.0.beta.1
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.
- data/bin/vmc +11 -2
- data/vmc-ng/LICENSE +746 -0
- data/vmc-ng/Rakefile +11 -0
- data/vmc-ng/bin/vmc +14 -0
- data/vmc-ng/lib/vmc.rb +2 -0
- data/vmc-ng/lib/vmc/cli.rb +327 -0
- data/vmc-ng/lib/vmc/cli/app.rb +622 -0
- data/vmc-ng/lib/vmc/cli/better_help.rb +193 -0
- data/vmc-ng/lib/vmc/cli/command.rb +523 -0
- data/vmc-ng/lib/vmc/cli/dots.rb +133 -0
- data/vmc-ng/lib/vmc/cli/service.rb +122 -0
- data/vmc-ng/lib/vmc/cli/user.rb +72 -0
- data/vmc-ng/lib/vmc/constants.rb +10 -0
- data/vmc-ng/lib/vmc/detect.rb +64 -0
- data/vmc-ng/lib/vmc/errors.rb +17 -0
- data/vmc-ng/lib/vmc/plugin.rb +40 -0
- data/vmc-ng/lib/vmc/version.rb +3 -0
- data/{LICENSE → vmc/LICENSE} +0 -0
- data/{README.md → vmc/README.md} +1 -5
- data/{Rakefile → vmc/Rakefile} +0 -0
- data/vmc/bin/vmc +6 -0
- data/{caldecott_helper → vmc/caldecott_helper}/Gemfile +0 -0
- data/{caldecott_helper → vmc/caldecott_helper}/Gemfile.lock +0 -0
- data/{caldecott_helper → vmc/caldecott_helper}/server.rb +0 -0
- data/{config → vmc/config}/clients.yml +0 -0
- data/{config → vmc/config}/micro/offline.conf +0 -0
- data/{config → vmc/config}/micro/paths.yml +0 -0
- data/{config → vmc/config}/micro/refresh_ip.rb +0 -0
- data/{lib → vmc/lib}/cli.rb +0 -0
- data/{lib → vmc/lib}/cli/commands/admin.rb +0 -0
- data/{lib → vmc/lib}/cli/commands/apps.rb +0 -0
- data/{lib → vmc/lib}/cli/commands/base.rb +0 -0
- data/{lib → vmc/lib}/cli/commands/manifest.rb +0 -0
- data/{lib → vmc/lib}/cli/commands/micro.rb +0 -0
- data/{lib → vmc/lib}/cli/commands/misc.rb +0 -0
- data/{lib → vmc/lib}/cli/commands/services.rb +0 -0
- data/{lib → vmc/lib}/cli/commands/user.rb +2 -6
- data/{lib → vmc/lib}/cli/config.rb +0 -0
- data/{lib → vmc/lib}/cli/console_helper.rb +4 -14
- data/{lib → vmc/lib}/cli/core_ext.rb +0 -0
- data/{lib → vmc/lib}/cli/errors.rb +0 -0
- data/{lib → vmc/lib}/cli/frameworks.rb +1 -1
- data/{lib → vmc/lib}/cli/manifest_helper.rb +0 -0
- data/{lib → vmc/lib}/cli/runner.rb +0 -0
- data/{lib → vmc/lib}/cli/services_helper.rb +0 -0
- data/{lib → vmc/lib}/cli/tunnel_helper.rb +0 -0
- data/{lib → vmc/lib}/cli/usage.rb +0 -0
- data/{lib → vmc/lib}/cli/version.rb +1 -1
- data/{lib → vmc/lib}/cli/zip_util.rb +0 -0
- data/{lib → vmc/lib}/vmc.rb +0 -0
- data/{lib → vmc/lib}/vmc/client.rb +0 -0
- data/{lib → vmc/lib}/vmc/const.rb +0 -0
- data/{lib → vmc/lib}/vmc/micro.rb +0 -0
- data/{lib → vmc/lib}/vmc/micro/switcher/base.rb +0 -0
- data/{lib → vmc/lib}/vmc/micro/switcher/darwin.rb +0 -0
- data/{lib → vmc/lib}/vmc/micro/switcher/dummy.rb +0 -0
- data/{lib → vmc/lib}/vmc/micro/switcher/linux.rb +0 -0
- data/{lib → vmc/lib}/vmc/micro/switcher/windows.rb +0 -0
- data/{lib → vmc/lib}/vmc/micro/vmrun.rb +1 -11
- metadata +177 -93
data/vmc-ng/Rakefile
ADDED
data/vmc-ng/bin/vmc
ADDED
data/vmc-ng/lib/vmc.rb
ADDED
@@ -0,0 +1,327 @@
|
|
1
|
+
require "vmc/cli/command"
|
2
|
+
|
3
|
+
VMC::Command.groups(
|
4
|
+
[:start, "Getting Started"],
|
5
|
+
[:apps, "Applications",
|
6
|
+
[:manage, "Management"],
|
7
|
+
[:info, "Information"]],
|
8
|
+
[:services, "Services",
|
9
|
+
[:manage, "Management"]],
|
10
|
+
[:admin, "Administration",
|
11
|
+
[:user, "User Management"]])
|
12
|
+
|
13
|
+
require "vmc/cli/app"
|
14
|
+
require "vmc/cli/service"
|
15
|
+
require "vmc/cli/user"
|
16
|
+
|
17
|
+
module VMC
|
18
|
+
class CLI < App # subclass App since we operate on Apps by default
|
19
|
+
desc "service SUBCOMMAND ...ARGS", "Service management"
|
20
|
+
subcommand "service", Service
|
21
|
+
|
22
|
+
desc "user SUBCOMMAND ...ARGS", "User management"
|
23
|
+
subcommand "user", User
|
24
|
+
|
25
|
+
desc "info", "Display information on the current target, user, etc."
|
26
|
+
group :start
|
27
|
+
flag :runtimes, :default => false
|
28
|
+
flag :services, :default => false
|
29
|
+
flag :frameworks, :default => false
|
30
|
+
def info
|
31
|
+
info =
|
32
|
+
with_progress("Getting target information") do
|
33
|
+
client.info
|
34
|
+
end
|
35
|
+
|
36
|
+
authorized = !!info["frameworks"]
|
37
|
+
|
38
|
+
if input(:runtimes)
|
39
|
+
raise NotAuthorized unless authorized
|
40
|
+
|
41
|
+
runtimes = {}
|
42
|
+
info["frameworks"].each do |_, f|
|
43
|
+
f["runtimes"].each do |r|
|
44
|
+
runtimes[r["name"]] = r
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
runtimes = runtimes.values.sort_by { |x| x["name"] }
|
49
|
+
|
50
|
+
if simple_output?
|
51
|
+
runtimes.each do |r|
|
52
|
+
puts r["name"]
|
53
|
+
end
|
54
|
+
return
|
55
|
+
end
|
56
|
+
|
57
|
+
runtimes.each do |r|
|
58
|
+
puts ""
|
59
|
+
puts "#{c(r["name"], :blue)}:"
|
60
|
+
puts " version: #{b(r["version"])}"
|
61
|
+
puts " description: #{b(r["description"])}"
|
62
|
+
end
|
63
|
+
|
64
|
+
return
|
65
|
+
end
|
66
|
+
|
67
|
+
if input(:services)
|
68
|
+
raise NotAuthorized unless authorized
|
69
|
+
|
70
|
+
services = {}
|
71
|
+
client.system_services.each do |_, svcs|
|
72
|
+
svcs.each do |name, versions|
|
73
|
+
services[name] = versions.values
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
if simple_output?
|
78
|
+
services.each do |name, _|
|
79
|
+
puts name
|
80
|
+
end
|
81
|
+
|
82
|
+
return
|
83
|
+
end
|
84
|
+
|
85
|
+
services.each do |name, versions|
|
86
|
+
puts ""
|
87
|
+
puts "#{c(name, :blue)}:"
|
88
|
+
puts " versions: #{versions.collect { |v| v["version"] }.join ", "}"
|
89
|
+
puts " description: #{versions[0]["description"]}"
|
90
|
+
puts " type: #{versions[0]["type"]}"
|
91
|
+
end
|
92
|
+
|
93
|
+
return
|
94
|
+
end
|
95
|
+
|
96
|
+
if input(:frameworks)
|
97
|
+
raise NotAuthorized unless authorized
|
98
|
+
|
99
|
+
puts "" unless simple_output?
|
100
|
+
|
101
|
+
info["frameworks"].each do |name, _|
|
102
|
+
puts name
|
103
|
+
end
|
104
|
+
|
105
|
+
return
|
106
|
+
end
|
107
|
+
|
108
|
+
puts ""
|
109
|
+
|
110
|
+
puts info["description"]
|
111
|
+
puts ""
|
112
|
+
puts "target: #{b(client.target)}"
|
113
|
+
puts " version: #{info["version"]}"
|
114
|
+
puts " support: #{info["support"]}"
|
115
|
+
|
116
|
+
if info["user"]
|
117
|
+
puts ""
|
118
|
+
puts "user: #{b(info["user"])}"
|
119
|
+
puts " usage:"
|
120
|
+
|
121
|
+
limits = info["limits"]
|
122
|
+
info["usage"].each do |k, v|
|
123
|
+
m = limits[k]
|
124
|
+
if k == "memory"
|
125
|
+
puts " #{k}: #{usage(v * 1024 * 1024, m * 1024 * 1024)}"
|
126
|
+
else
|
127
|
+
puts " #{k}: #{b(v)} of #{b(m)} limit"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
desc "target [URL]", "Set or display the current target cloud"
|
134
|
+
group :start
|
135
|
+
def target(url = nil)
|
136
|
+
if url.nil?
|
137
|
+
display_target
|
138
|
+
return
|
139
|
+
end
|
140
|
+
|
141
|
+
target = sane_target_url(url)
|
142
|
+
display = c(target.sub(/https?:\/\//, ""), :blue)
|
143
|
+
with_progress("Setting target to #{display}") do
|
144
|
+
unless force?
|
145
|
+
# check that the target is valid
|
146
|
+
CFoundry::Client.new(target).info
|
147
|
+
end
|
148
|
+
|
149
|
+
set_target(target)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
desc "login [EMAIL]", "Authenticate with the target"
|
154
|
+
group :start
|
155
|
+
flag(:email) {
|
156
|
+
ask("Email")
|
157
|
+
}
|
158
|
+
flag(:password)
|
159
|
+
# TODO: implement new authentication scheme
|
160
|
+
def login(email = nil)
|
161
|
+
unless simple_output?
|
162
|
+
display_target
|
163
|
+
puts ""
|
164
|
+
end
|
165
|
+
|
166
|
+
email ||= input(:email)
|
167
|
+
password = input(:password)
|
168
|
+
|
169
|
+
authenticated = false
|
170
|
+
failed = false
|
171
|
+
until authenticated
|
172
|
+
unless force?
|
173
|
+
if failed || !password
|
174
|
+
password = ask("Password", :echo => "*", :forget => true)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
with_progress("Authenticating") do |s|
|
179
|
+
begin
|
180
|
+
save_token(client.login(email, password))
|
181
|
+
authenticated = true
|
182
|
+
rescue CFoundry::Denied
|
183
|
+
return if force?
|
184
|
+
|
185
|
+
s.fail do
|
186
|
+
failed = true
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
ensure
|
192
|
+
$exit_status = 1 if not authenticated
|
193
|
+
end
|
194
|
+
|
195
|
+
desc "logout", "Log out from the target"
|
196
|
+
group :start
|
197
|
+
def logout
|
198
|
+
with_progress("Logging out") do
|
199
|
+
remove_token
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
desc "register [EMAIL]", "Create a user and log in"
|
204
|
+
group :start, :hidden => true
|
205
|
+
flag(:email) {
|
206
|
+
ask("Email")
|
207
|
+
}
|
208
|
+
flag(:password) {
|
209
|
+
ask("Password", :echo => "*", :forget => true)
|
210
|
+
}
|
211
|
+
flag(:no_login, :type => :boolean)
|
212
|
+
def register(email = nil)
|
213
|
+
unless simple_output?
|
214
|
+
puts "Target: #{c(client_target, :blue)}"
|
215
|
+
puts ""
|
216
|
+
end
|
217
|
+
|
218
|
+
email ||= input(:email)
|
219
|
+
password = input(:password)
|
220
|
+
|
221
|
+
with_progress("Creating user") do
|
222
|
+
client.register(email, password)
|
223
|
+
end
|
224
|
+
|
225
|
+
unless input(:skip_login)
|
226
|
+
with_progress("Logging in") do
|
227
|
+
save_token(client.login(email, password))
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
desc "apps", "List your applications"
|
233
|
+
group :apps
|
234
|
+
def apps
|
235
|
+
apps =
|
236
|
+
with_progress("Getting applications") do
|
237
|
+
client.apps
|
238
|
+
end
|
239
|
+
|
240
|
+
if apps.empty? and !simple_output?
|
241
|
+
puts ""
|
242
|
+
puts "No applications."
|
243
|
+
return
|
244
|
+
end
|
245
|
+
|
246
|
+
apps.each.with_index do |a, num|
|
247
|
+
display_app(a)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
desc "services", "List your services"
|
252
|
+
group :services
|
253
|
+
def services
|
254
|
+
services =
|
255
|
+
with_progress("Getting services") do
|
256
|
+
client.services
|
257
|
+
end
|
258
|
+
|
259
|
+
puts "" unless simple_output?
|
260
|
+
|
261
|
+
if services.empty? and !simple_output?
|
262
|
+
puts "No services."
|
263
|
+
end
|
264
|
+
|
265
|
+
services.each do |s|
|
266
|
+
display_service(s)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
desc "users", "List all users"
|
271
|
+
group :admin, :hidden => true
|
272
|
+
def users
|
273
|
+
users =
|
274
|
+
with_progress("Getting users") do
|
275
|
+
client.users
|
276
|
+
end
|
277
|
+
|
278
|
+
users.each do |u|
|
279
|
+
display_user(u)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
desc "help [COMMAND]", "usage instructions"
|
284
|
+
flag :all, :default => false
|
285
|
+
group :start
|
286
|
+
def help(task = nil)
|
287
|
+
if task
|
288
|
+
self.class.task_help(@shell, task)
|
289
|
+
else
|
290
|
+
unless input(:all)
|
291
|
+
puts "Showing basic command set. Pass --all to list all commands."
|
292
|
+
puts ""
|
293
|
+
end
|
294
|
+
|
295
|
+
self.class.print_help_groups(input(:all))
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
private
|
300
|
+
|
301
|
+
def display_service(s)
|
302
|
+
if simple_output?
|
303
|
+
puts s.name
|
304
|
+
else
|
305
|
+
puts "#{c(s.name, :blue)}: #{s.vendor} v#{s.version}"
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def display_user(u)
|
310
|
+
if simple_output?
|
311
|
+
puts u.email
|
312
|
+
else
|
313
|
+
puts ""
|
314
|
+
puts "#{c(u.email, :blue)}:"
|
315
|
+
puts " admin?: #{c(u.admin?, u.admin? ? :green : :red)}"
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def display_target
|
320
|
+
if simple_output?
|
321
|
+
puts client.target
|
322
|
+
else
|
323
|
+
puts "Target: #{c(client.target, :blue)}"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
@@ -0,0 +1,622 @@
|
|
1
|
+
require "vmc/cli/command"
|
2
|
+
require "vmc/detect"
|
3
|
+
|
4
|
+
module VMC
|
5
|
+
class App < Command
|
6
|
+
MEM_CHOICES = ["64M", "128M", "256M", "512M"]
|
7
|
+
|
8
|
+
desc "push [NAME]", "Push an application, syncing changes if it exists"
|
9
|
+
group :apps, :manage
|
10
|
+
flag(:name) { ask("Name") }
|
11
|
+
flag(:path) {
|
12
|
+
ask("Push from...", :default => ".")
|
13
|
+
}
|
14
|
+
flag(:url) { |name, target|
|
15
|
+
ask("URL", :default => "#{name}.#{target}")
|
16
|
+
}
|
17
|
+
flag(:memory) {
|
18
|
+
ask("Memory Limit",
|
19
|
+
:choices => MEM_CHOICES,
|
20
|
+
|
21
|
+
# TODO: base this on framework choice
|
22
|
+
:default => "64M")
|
23
|
+
}
|
24
|
+
flag(:instances) {
|
25
|
+
ask("Instances", :default => 1)
|
26
|
+
}
|
27
|
+
flag(:framework) { |choices, default|
|
28
|
+
ask("Framework", :choices => choices, :default => default)
|
29
|
+
}
|
30
|
+
flag(:runtime) { |choices|
|
31
|
+
ask("Runtime", :choices => choices)
|
32
|
+
}
|
33
|
+
flag(:start, :default => true)
|
34
|
+
flag(:restart, :default => true)
|
35
|
+
def push(name = nil)
|
36
|
+
path = File.expand_path(input(:path))
|
37
|
+
|
38
|
+
name ||= input(:name)
|
39
|
+
|
40
|
+
detector = Detector.new(client, path)
|
41
|
+
frameworks = detector.all_frameworks
|
42
|
+
detected, default = detector.frameworks
|
43
|
+
|
44
|
+
app = client.app(name)
|
45
|
+
|
46
|
+
if app.exists?
|
47
|
+
upload_app(app, path)
|
48
|
+
restart(app.name) if input(:restart)
|
49
|
+
return
|
50
|
+
end
|
51
|
+
|
52
|
+
app.total_instances = input(:instances)
|
53
|
+
|
54
|
+
domain = client.target.sub(/^https?:\/\/api\.(.+)\/?/, '\1')
|
55
|
+
app.urls = [input(:url, name, domain)]
|
56
|
+
|
57
|
+
framework = input(:framework, ["other"] + detected.keys, default)
|
58
|
+
if framework == "other"
|
59
|
+
forget(:framework)
|
60
|
+
framework = input(:framework, frameworks.keys)
|
61
|
+
end
|
62
|
+
|
63
|
+
framework_runtimes =
|
64
|
+
frameworks[framework]["runtimes"].collect do |k|
|
65
|
+
"#{k["name"]} (#{k["description"]})"
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO: include descriptions
|
69
|
+
runtime = input(:runtime, framework_runtimes).split.first
|
70
|
+
|
71
|
+
app.framework = framework
|
72
|
+
app.runtime = runtime
|
73
|
+
|
74
|
+
app.memory = megabytes(input(:memory))
|
75
|
+
|
76
|
+
with_progress("Creating #{c(name, :blue)}") do
|
77
|
+
app.create!
|
78
|
+
end
|
79
|
+
|
80
|
+
begin
|
81
|
+
upload_app(app, path)
|
82
|
+
rescue
|
83
|
+
err "Upload failed. Try again with 'vmc push'."
|
84
|
+
raise
|
85
|
+
end
|
86
|
+
|
87
|
+
start(name) if input(:start)
|
88
|
+
end
|
89
|
+
|
90
|
+
desc "start APPS...", "Start an application"
|
91
|
+
group :apps, :manage
|
92
|
+
flag :name
|
93
|
+
flag :debug_mode
|
94
|
+
def start(*names)
|
95
|
+
if name = input(:name)
|
96
|
+
names = [name]
|
97
|
+
end
|
98
|
+
|
99
|
+
fail "No applications given." if names.empty?
|
100
|
+
|
101
|
+
names.each do |name|
|
102
|
+
app = client.app(name)
|
103
|
+
|
104
|
+
fail "Unknown application." unless app.exists?
|
105
|
+
|
106
|
+
switch_mode(app, input(:debug_mode))
|
107
|
+
|
108
|
+
with_progress("Starting #{c(name, :blue)}") do |s|
|
109
|
+
if app.running?
|
110
|
+
s.skip do
|
111
|
+
err "Already started."
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
app.start!
|
116
|
+
end
|
117
|
+
|
118
|
+
check_application(app)
|
119
|
+
|
120
|
+
if app.debug_mode && !simple_output?
|
121
|
+
puts ""
|
122
|
+
instances(name)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
desc "stop APPS...", "Stop an application"
|
128
|
+
group :apps, :manage
|
129
|
+
flag :name
|
130
|
+
def stop(*names)
|
131
|
+
if name = input(:name)
|
132
|
+
names = [name]
|
133
|
+
end
|
134
|
+
|
135
|
+
fail "No applications given." if names.empty?
|
136
|
+
|
137
|
+
names.each do |name|
|
138
|
+
with_progress("Stopping #{c(name, :blue)}") do |s|
|
139
|
+
app = client.app(name)
|
140
|
+
|
141
|
+
unless app.exists?
|
142
|
+
s.fail do
|
143
|
+
err "Unknown application."
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
if app.stopped?
|
148
|
+
s.skip do
|
149
|
+
err "Application is not running."
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
app.stop!
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
desc "restart APPS...", "Stop and start an application"
|
159
|
+
group :apps, :manage
|
160
|
+
flag :name
|
161
|
+
flag :debug_mode
|
162
|
+
def restart(*names)
|
163
|
+
stop(*names)
|
164
|
+
start(*names)
|
165
|
+
end
|
166
|
+
|
167
|
+
desc "delete APPS...", "Delete an application"
|
168
|
+
group :apps, :manage
|
169
|
+
flag :name
|
170
|
+
flag(:really) { |name, color|
|
171
|
+
force? || ask("Really delete #{c(name, color)}?", :default => false)
|
172
|
+
}
|
173
|
+
flag(:name) { |names|
|
174
|
+
ask("Delete which application?", :choices => names)
|
175
|
+
}
|
176
|
+
flag(:all, :default => false)
|
177
|
+
def delete(*names)
|
178
|
+
if input(:all)
|
179
|
+
return unless input(:really, "ALL APPS", :red)
|
180
|
+
|
181
|
+
with_progress("Deleting all applications") do
|
182
|
+
client.apps.collect(&:delete!)
|
183
|
+
end
|
184
|
+
|
185
|
+
return
|
186
|
+
end
|
187
|
+
|
188
|
+
if names.empty?
|
189
|
+
apps = client.apps
|
190
|
+
fail "No applications." if apps.empty?
|
191
|
+
|
192
|
+
names = [input(:name, apps.collect(&:name))]
|
193
|
+
end
|
194
|
+
|
195
|
+
names.each do |name|
|
196
|
+
really = input(:really, name, :blue)
|
197
|
+
|
198
|
+
forget(:really)
|
199
|
+
|
200
|
+
next unless really
|
201
|
+
|
202
|
+
with_progress("Deleting #{c(name, :blue)}") do
|
203
|
+
client.app(name).delete!
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
desc "instances APPS...", "List an app's instances"
|
209
|
+
group :apps, :info, :hidden => true
|
210
|
+
flag :name
|
211
|
+
def instances(*names)
|
212
|
+
if name = input(:name)
|
213
|
+
names = [name]
|
214
|
+
end
|
215
|
+
|
216
|
+
fail "No applications given." if names.empty?
|
217
|
+
|
218
|
+
names.each do |name|
|
219
|
+
instances =
|
220
|
+
with_progress("Getting instances for #{c(name, :blue)}") do
|
221
|
+
client.app(name).instances
|
222
|
+
end
|
223
|
+
|
224
|
+
instances.each do |i|
|
225
|
+
if simple_output?
|
226
|
+
puts i.index
|
227
|
+
else
|
228
|
+
puts ""
|
229
|
+
display_instance(i)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
desc "scale APP", "Update the instances/memory limit for an application"
|
236
|
+
group :apps, :info, :hidden => true
|
237
|
+
flag(:instances, :type => :numeric) { |default|
|
238
|
+
ask("Instances", :default => default)
|
239
|
+
}
|
240
|
+
flag(:memory) { |default|
|
241
|
+
ask("Memory Limit",
|
242
|
+
:default => human_size(default * 1024 * 1024, 0),
|
243
|
+
:choices => MEM_CHOICES)
|
244
|
+
}
|
245
|
+
def scale(name)
|
246
|
+
app = client.app(name)
|
247
|
+
|
248
|
+
instances = passed_value(:instances)
|
249
|
+
memory = passed_value(:memory)
|
250
|
+
|
251
|
+
unless instances || memory
|
252
|
+
instances = input(:instances, app.total_instances)
|
253
|
+
memory = input(:memory, app.memory)
|
254
|
+
end
|
255
|
+
|
256
|
+
with_progress("Scaling #{c(name, :blue)}") do
|
257
|
+
app.total_instances = instances.to_i if instances
|
258
|
+
app.memory = megabytes(memory) if memory
|
259
|
+
app.update!
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
desc "logs APP", "Print out an app's logs"
|
264
|
+
group :apps, :info, :hidden => true
|
265
|
+
flag(:instance, :type => :numeric, :default => 0)
|
266
|
+
flag(:all, :default => false)
|
267
|
+
def logs(name)
|
268
|
+
app = client.app(name)
|
269
|
+
fail "Unknown application." unless app.exists?
|
270
|
+
|
271
|
+
instances =
|
272
|
+
if input(:all)
|
273
|
+
app.instances
|
274
|
+
else
|
275
|
+
app.instances.select { |i| i.index == input(:instance) }
|
276
|
+
end
|
277
|
+
|
278
|
+
if instances.empty?
|
279
|
+
if input(:all)
|
280
|
+
fail "No instances found."
|
281
|
+
else
|
282
|
+
fail "Instance #{name} \##{input(:instance)} not found."
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
instances.each do |i|
|
287
|
+
logs =
|
288
|
+
with_progress(
|
289
|
+
"Getting logs for " +
|
290
|
+
c(name, :blue) + " " +
|
291
|
+
c("\##{i.index}", :yellow)) do
|
292
|
+
i.files("logs")
|
293
|
+
end
|
294
|
+
|
295
|
+
puts "" unless simple_output?
|
296
|
+
|
297
|
+
logs.each do |log|
|
298
|
+
body =
|
299
|
+
with_progress("Reading " + b(log.join("/"))) do
|
300
|
+
i.file(*log)
|
301
|
+
end
|
302
|
+
|
303
|
+
puts body
|
304
|
+
puts "" unless body.empty?
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
desc "file APP [PATH]", "Print out an app's file contents"
|
310
|
+
group :apps, :info, :hidden => true
|
311
|
+
def file(name, path = "/")
|
312
|
+
file =
|
313
|
+
with_progress("Getting file contents") do
|
314
|
+
client.app(name).file(*path.split("/"))
|
315
|
+
end
|
316
|
+
|
317
|
+
puts "" unless simple_output?
|
318
|
+
|
319
|
+
print file
|
320
|
+
end
|
321
|
+
|
322
|
+
desc "files APP [PATH]", "Examine an app's files"
|
323
|
+
group :apps, :info, :hidden => true
|
324
|
+
def files(name, path = "/")
|
325
|
+
files =
|
326
|
+
with_progress("Getting file listing") do
|
327
|
+
client.app(name).files(*path.split("/"))
|
328
|
+
end
|
329
|
+
|
330
|
+
puts "" unless simple_output?
|
331
|
+
|
332
|
+
files.each do |file|
|
333
|
+
puts file
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
desc "health ...APPS", "Get application health"
|
338
|
+
group :apps, :info, :hidden => true
|
339
|
+
flag :name
|
340
|
+
def health(*names)
|
341
|
+
if name = input(:name)
|
342
|
+
names = [name]
|
343
|
+
end
|
344
|
+
|
345
|
+
apps =
|
346
|
+
with_progress("Getting application health") do
|
347
|
+
names.collect do |n|
|
348
|
+
[n, app_status(client.app(n))]
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
apps.each do |name, status|
|
353
|
+
unless simple_output?
|
354
|
+
puts ""
|
355
|
+
print "#{c(name, :blue)}: "
|
356
|
+
end
|
357
|
+
|
358
|
+
puts status
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
desc "stats APP", "Display application instance status"
|
363
|
+
group :apps, :info, :hidden => true
|
364
|
+
def stats(name)
|
365
|
+
stats =
|
366
|
+
with_progress("Getting stats") do
|
367
|
+
client.app(name).stats
|
368
|
+
end
|
369
|
+
|
370
|
+
stats.sort_by { |k, _| k }.each do |idx, info|
|
371
|
+
stats = info["stats"]
|
372
|
+
usage = stats["usage"]
|
373
|
+
puts ""
|
374
|
+
puts "instance #{c("#" + idx, :blue)}:"
|
375
|
+
print " cpu: #{percentage(usage["cpu"])} of"
|
376
|
+
puts " #{b(stats["cores"])} cores"
|
377
|
+
puts " memory: #{usage(usage["mem"] * 1024, stats["mem_quota"])}"
|
378
|
+
puts " disk: #{usage(usage["disk"], stats["disk_quota"])}"
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
desc "update", "DEPRECATED", :hide => true
|
383
|
+
def update(*args)
|
384
|
+
fail "The 'update' command is no longer used; use 'push' instead."
|
385
|
+
end
|
386
|
+
|
387
|
+
class URL < Command
|
388
|
+
desc "map APP URL", "Add a URL mapping for an app"
|
389
|
+
group :apps, :info, :hidden => true
|
390
|
+
def map(name, url)
|
391
|
+
simple = url.sub(/^https?:\/\/(.*)\/?/i, '\1')
|
392
|
+
|
393
|
+
with_progress("Updating #{c(name, :blue)}") do
|
394
|
+
app = client.app(name)
|
395
|
+
app.urls << simple
|
396
|
+
app.update!
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
desc "unmap APP URL", "Remove a URL mapping from an app"
|
401
|
+
group :apps, :info, :hidden => true
|
402
|
+
def unmap(name, url)
|
403
|
+
simple = url.sub(/^https?:\/\/(.*)\/?/i, '\1')
|
404
|
+
|
405
|
+
app = client.app(name)
|
406
|
+
fail "Unknown application." unless app.exists?
|
407
|
+
|
408
|
+
with_progress("Updating #{c(name, :blue)}") do |s|
|
409
|
+
unless app.urls.delete(simple)
|
410
|
+
s.fail do
|
411
|
+
err "URL #{url} is not mapped to this application."
|
412
|
+
return
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
app.update!
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
desc "url SUBCOMMAND ...ARGS", "Manage application URL bindings"
|
422
|
+
subcommand "url", URL
|
423
|
+
|
424
|
+
class Env < Command
|
425
|
+
VALID_NAME = /^[a-zA-Za-z_][[:alnum:]_]*$/
|
426
|
+
|
427
|
+
desc "set APP [NAME] [VALUE]", "Set an environment variable"
|
428
|
+
group :apps, :info, :hidden => true
|
429
|
+
def set(appname, name, value)
|
430
|
+
unless name =~ VALID_NAME
|
431
|
+
fail "Invalid variable name; must match #{VALID_NAME.inspect}"
|
432
|
+
end
|
433
|
+
|
434
|
+
app = client.app(appname)
|
435
|
+
fail "Unknown application." unless app.exists?
|
436
|
+
|
437
|
+
with_progress("Updating #{c(app.name, :blue)}") do
|
438
|
+
app.update!("env" =>
|
439
|
+
app.env.reject { |v|
|
440
|
+
v.start_with?("#{name}=")
|
441
|
+
}.push("#{name}=#{value}"))
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
desc "unset APP [NAME]", "Remove an environment variable"
|
446
|
+
group :apps, :info, :hidden => true
|
447
|
+
def unset(appname, name)
|
448
|
+
app = client.app(appname)
|
449
|
+
fail "Unknown application." unless app.exists?
|
450
|
+
|
451
|
+
with_progress("Updating #{c(app.name, :blue)}") do
|
452
|
+
app.update!("env" =>
|
453
|
+
app.env.reject { |v|
|
454
|
+
v.start_with?("#{name}=")
|
455
|
+
})
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
desc "list APP", "Show all environment variables set for an app"
|
460
|
+
group :apps, :info, :hidden => true
|
461
|
+
def list(appname)
|
462
|
+
vars =
|
463
|
+
with_progress("Getting variables") do |s|
|
464
|
+
app = client.app(appname)
|
465
|
+
|
466
|
+
unless app.exists?
|
467
|
+
s.fail do
|
468
|
+
err "Unknown application."
|
469
|
+
return
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
app.env
|
474
|
+
end
|
475
|
+
|
476
|
+
puts "" unless simple_output?
|
477
|
+
|
478
|
+
vars.each do |pair|
|
479
|
+
name, val = pair.split("=", 2)
|
480
|
+
puts "#{c(name, :blue)}: #{val}"
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
desc "env SUBCOMMAND ...ARGS", "Manage application environment variables"
|
486
|
+
subcommand "env", Env
|
487
|
+
|
488
|
+
private
|
489
|
+
|
490
|
+
def upload_app(app, path)
|
491
|
+
with_progress("Uploading #{c(app.name, :blue)}") do
|
492
|
+
app.upload(path)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
# set app debug mode, ensuring it's valid, and shutting it down
|
497
|
+
def switch_mode(app, mode)
|
498
|
+
mode = nil if mode == "none"
|
499
|
+
|
500
|
+
return false if app.debug_mode == mode
|
501
|
+
|
502
|
+
if mode.nil?
|
503
|
+
with_progress("Removing debug mode") do
|
504
|
+
app.debug_mode = nil
|
505
|
+
app.stop! if app.running?
|
506
|
+
end
|
507
|
+
|
508
|
+
return true
|
509
|
+
end
|
510
|
+
|
511
|
+
with_progress("Switching mode to #{c(mode, :blue)}") do |s|
|
512
|
+
runtimes = client.system_runtimes
|
513
|
+
modes = runtimes[app.runtime]["debug_modes"] || []
|
514
|
+
if modes.include?(mode)
|
515
|
+
app.debug_mode = mode
|
516
|
+
app.stop! if app.running?
|
517
|
+
true
|
518
|
+
else
|
519
|
+
s.fail do
|
520
|
+
err "Unknown mode '#{mode}'; available: #{modes.inspect}"
|
521
|
+
false
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
APP_CHECK_LIMIT = 60
|
528
|
+
|
529
|
+
def check_application(app)
|
530
|
+
with_progress("Checking #{c(app.name, :blue)}") do |s|
|
531
|
+
if app.debug_mode == "suspend"
|
532
|
+
s.skip do
|
533
|
+
puts "Application is in suspended debugging mode."
|
534
|
+
puts "It will wait for you to attach to it before starting."
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
seconds = 0
|
539
|
+
until app.healthy?
|
540
|
+
sleep 1
|
541
|
+
seconds += 1
|
542
|
+
if seconds == APP_CHECK_LIMIT
|
543
|
+
s.give_up do
|
544
|
+
err "Application failed to start."
|
545
|
+
# TODO: print logs
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
# choose the right color for app/instance state
|
553
|
+
def state_color(s)
|
554
|
+
case s
|
555
|
+
when "STARTING"
|
556
|
+
:blue
|
557
|
+
when "STARTED", "RUNNING"
|
558
|
+
:green
|
559
|
+
when "DOWN"
|
560
|
+
:red
|
561
|
+
when "FLAPPING"
|
562
|
+
:magenta
|
563
|
+
when "N/A"
|
564
|
+
:cyan
|
565
|
+
else
|
566
|
+
:yellow
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
def app_status(a)
|
571
|
+
health = a.health
|
572
|
+
|
573
|
+
if a.debug_mode == "suspend" && health == "0%"
|
574
|
+
c("suspended", :yellow)
|
575
|
+
else
|
576
|
+
c(health.downcase, state_color(health))
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
def display_app(a)
|
581
|
+
if simple_output?
|
582
|
+
puts a.name
|
583
|
+
return
|
584
|
+
end
|
585
|
+
|
586
|
+
puts ""
|
587
|
+
|
588
|
+
status = app_status(a)
|
589
|
+
|
590
|
+
print "#{c(a.name, :blue)}: #{status}"
|
591
|
+
|
592
|
+
unless a.total_instances == 1
|
593
|
+
print ", #{b(a.total_instances)} instances"
|
594
|
+
end
|
595
|
+
|
596
|
+
puts ""
|
597
|
+
|
598
|
+
unless a.urls.empty?
|
599
|
+
puts " urls: #{a.urls.collect { |u| b(u) }.join(", ")}"
|
600
|
+
end
|
601
|
+
|
602
|
+
unless a.services.empty?
|
603
|
+
puts " services: #{a.services.collect { |s| b(s) }.join(", ")}"
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
def display_instance(i)
|
608
|
+
print "instance #{c("\##{i.index}", :blue)}: "
|
609
|
+
puts "#{b(c(i.state.downcase, state_color(i.state)))} "
|
610
|
+
|
611
|
+
puts " started: #{c(i.since.strftime("%F %r"), :cyan)}"
|
612
|
+
|
613
|
+
if d = i.debugger
|
614
|
+
puts " debugger: port #{c(d["port"], :blue)} at #{c(d["ip"], :blue)}"
|
615
|
+
end
|
616
|
+
|
617
|
+
if c = i.console
|
618
|
+
puts " console: port #{b(c["port"])} at #{b(c["ip"])}"
|
619
|
+
end
|
620
|
+
end
|
621
|
+
end
|
622
|
+
end
|