af 0.3.12
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/LICENSE +24 -0
- data/README.md +92 -0
- data/Rakefile +17 -0
- data/bin/af +6 -0
- data/lib/cli.rb +30 -0
- data/lib/cli/commands/admin.rb +77 -0
- data/lib/cli/commands/apps.rb +940 -0
- data/lib/cli/commands/base.rb +79 -0
- data/lib/cli/commands/misc.rb +128 -0
- data/lib/cli/commands/services.rb +86 -0
- data/lib/cli/commands/user.rb +60 -0
- data/lib/cli/config.rb +110 -0
- data/lib/cli/core_ext.rb +119 -0
- data/lib/cli/errors.rb +19 -0
- data/lib/cli/frameworks.rb +109 -0
- data/lib/cli/runner.rb +490 -0
- data/lib/cli/services_helper.rb +78 -0
- data/lib/cli/usage.rb +104 -0
- data/lib/cli/version.rb +7 -0
- data/lib/cli/zip_util.rb +77 -0
- data/lib/vmc.rb +3 -0
- data/lib/vmc/client.rb +451 -0
- data/lib/vmc/const.rb +21 -0
- data/spec/assets/app_info.txt +9 -0
- data/spec/assets/app_listings.txt +9 -0
- data/spec/assets/bad_create_app.txt +9 -0
- data/spec/assets/delete_app.txt +9 -0
- data/spec/assets/global_service_listings.txt +9 -0
- data/spec/assets/good_create_app.txt +9 -0
- data/spec/assets/good_create_service.txt +9 -0
- data/spec/assets/info_authenticated.txt +27 -0
- data/spec/assets/info_return.txt +15 -0
- data/spec/assets/info_return_bad.txt +16 -0
- data/spec/assets/list_users.txt +13 -0
- data/spec/assets/login_fail.txt +9 -0
- data/spec/assets/login_success.txt +9 -0
- data/spec/assets/sample_token.txt +1 -0
- data/spec/assets/service_already_exists.txt +9 -0
- data/spec/assets/service_listings.txt +9 -0
- data/spec/assets/service_not_found.txt +9 -0
- data/spec/assets/user_info.txt +9 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/unit/cli_opts_spec.rb +68 -0
- data/spec/unit/client_spec.rb +332 -0
- metadata +221 -0
data/lib/cli/core_ext.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
module VMCExtensions
|
2
|
+
|
3
|
+
def say(message)
|
4
|
+
VMC::Cli::Config.output.puts(message) if VMC::Cli::Config.output
|
5
|
+
end
|
6
|
+
|
7
|
+
def header(message, filler = '-')
|
8
|
+
say "\n"
|
9
|
+
say message
|
10
|
+
say filler.to_s * message.size
|
11
|
+
end
|
12
|
+
|
13
|
+
def banner(message)
|
14
|
+
say "\n"
|
15
|
+
say message
|
16
|
+
end
|
17
|
+
|
18
|
+
def display(message, nl=true)
|
19
|
+
if nl
|
20
|
+
say message
|
21
|
+
else
|
22
|
+
if VMC::Cli::Config.output
|
23
|
+
VMC::Cli::Config.output.print(message)
|
24
|
+
VMC::Cli::Config.output.flush
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def clear(size=80)
|
30
|
+
return unless VMC::Cli::Config.output
|
31
|
+
VMC::Cli::Config.output.print("\r")
|
32
|
+
VMC::Cli::Config.output.print(" " * size)
|
33
|
+
VMC::Cli::Config.output.print("\r")
|
34
|
+
#VMC::Cli::Config.output.flush
|
35
|
+
end
|
36
|
+
|
37
|
+
def err(message, prefix='Error: ')
|
38
|
+
raise VMC::Cli::CliExit, "#{prefix}#{message}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def quit(message = nil)
|
42
|
+
raise VMC::Cli::GracefulExit, message
|
43
|
+
end
|
44
|
+
|
45
|
+
def blank?
|
46
|
+
self.to_s.blank?
|
47
|
+
end
|
48
|
+
|
49
|
+
def uptime_string(delta)
|
50
|
+
num_seconds = delta.to_i
|
51
|
+
days = num_seconds / (60 * 60 * 24);
|
52
|
+
num_seconds -= days * (60 * 60 * 24);
|
53
|
+
hours = num_seconds / (60 * 60);
|
54
|
+
num_seconds -= hours * (60 * 60);
|
55
|
+
minutes = num_seconds / 60;
|
56
|
+
num_seconds -= minutes * 60;
|
57
|
+
"#{days}d:#{hours}h:#{minutes}m:#{num_seconds}s"
|
58
|
+
end
|
59
|
+
|
60
|
+
def pretty_size(size, prec=1)
|
61
|
+
return 'NA' unless size
|
62
|
+
return "#{size}B" if size < 1024
|
63
|
+
return sprintf("%.#{prec}fK", size/1024.0) if size < (1024*1024)
|
64
|
+
return sprintf("%.#{prec}fM", size/(1024.0*1024.0)) if size < (1024*1024*1024)
|
65
|
+
return sprintf("%.#{prec}fG", size/(1024.0*1024.0*1024.0))
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
module VMCStringExtensions
|
71
|
+
|
72
|
+
def red
|
73
|
+
colorize("\e[0m\e[31m")
|
74
|
+
end
|
75
|
+
|
76
|
+
def green
|
77
|
+
colorize("\e[0m\e[32m")
|
78
|
+
end
|
79
|
+
|
80
|
+
def yellow
|
81
|
+
colorize("\e[0m\e[33m")
|
82
|
+
end
|
83
|
+
|
84
|
+
def bold
|
85
|
+
colorize("\e[0m\e[1m")
|
86
|
+
end
|
87
|
+
|
88
|
+
def colorize(color_code)
|
89
|
+
if VMC::Cli::Config.colorize
|
90
|
+
"#{color_code}#{self}\e[0m"
|
91
|
+
else
|
92
|
+
self
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def blank?
|
97
|
+
self =~ /^\s*$/
|
98
|
+
end
|
99
|
+
|
100
|
+
def truncate(limit = 30)
|
101
|
+
return "" if self.blank?
|
102
|
+
etc = "..."
|
103
|
+
stripped = self.strip[0..limit]
|
104
|
+
if stripped.length > limit
|
105
|
+
stripped.gsub(/\s+?(\S+)?$/, "") + etc
|
106
|
+
else
|
107
|
+
stripped
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
class Object
|
114
|
+
include VMCExtensions
|
115
|
+
end
|
116
|
+
|
117
|
+
class String
|
118
|
+
include VMCStringExtensions
|
119
|
+
end
|
data/lib/cli/errors.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module VMC::Cli
|
2
|
+
|
3
|
+
class CliError < StandardError
|
4
|
+
def self.error_code(code = nil)
|
5
|
+
define_method(:error_code) { code }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class UnknownCommand < CliError; error_code(100); end
|
10
|
+
class TargetMissing < CliError; error_code(102); end
|
11
|
+
class TargetInaccessible < CliError; error_code(103); end
|
12
|
+
|
13
|
+
class TargetError < CliError; error_code(201); end
|
14
|
+
class AuthError < TargetError; error_code(202); end
|
15
|
+
|
16
|
+
class CliExit < CliError; error_code(400); end
|
17
|
+
class GracefulExit < CliExit; error_code(401); end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module VMC::Cli
|
2
|
+
|
3
|
+
class Framework
|
4
|
+
|
5
|
+
DEFAULT_FRAMEWORK = "http://b20nine.com/unknown"
|
6
|
+
DEFAULT_MEM = '256M'
|
7
|
+
|
8
|
+
FRAMEWORKS = {
|
9
|
+
'Rails' => ['rails3', { :mem => '256M', :description => 'Rails Application'}],
|
10
|
+
'Spring' => ['spring', { :mem => '512M', :description => 'Java SpringSource Spring Application'}],
|
11
|
+
'Grails' => ['grails', { :mem => '512M', :description => 'Java SpringSource Grails Application'}],
|
12
|
+
'Lift' => ['lift', { :mem => '512M', :description => 'Scala Lift Application'}],
|
13
|
+
'Roo' => ['spring', { :mem => '512M', :description => 'Java SpringSource Roo Application'}],
|
14
|
+
'JavaWeb' => ['spring', { :mem => '512M', :description => 'Java Web Application'}],
|
15
|
+
'Sinatra' => ['sinatra', { :mem => '128M', :description => 'Sinatra Application'}],
|
16
|
+
'Node' => ['node', { :mem => '64M', :description => 'Node.js Application'}],
|
17
|
+
'PHP' => ['php', { :mem => '128M', :description => 'PHP Application'}],
|
18
|
+
'Erlang/OTP Rebar' => ['otp_rebar', { :mem => '64M', :description => 'Erlang/OTP Rebar Application'}]
|
19
|
+
}
|
20
|
+
|
21
|
+
class << self
|
22
|
+
|
23
|
+
def known_frameworks
|
24
|
+
FRAMEWORKS.keys
|
25
|
+
end
|
26
|
+
|
27
|
+
def lookup(name)
|
28
|
+
return Framework.new(*FRAMEWORKS[name])
|
29
|
+
end
|
30
|
+
|
31
|
+
def detect(path)
|
32
|
+
Dir.chdir(path) do
|
33
|
+
|
34
|
+
# Rails
|
35
|
+
if File.exist?('config/environment.rb')
|
36
|
+
return Framework.lookup('Rails')
|
37
|
+
|
38
|
+
# Java
|
39
|
+
elsif Dir.glob('*.war').first
|
40
|
+
war_file = Dir.glob('*.war').first
|
41
|
+
contents = ZipUtil.entry_lines(war_file)
|
42
|
+
|
43
|
+
# Spring/Lift Variations
|
44
|
+
if contents =~ /WEB-INF\/lib\/grails-web.*\.jar/
|
45
|
+
return Framework.lookup('Grails')
|
46
|
+
elsif contents =~ /WEB-INF\/lib\/lift-webkit.*\.jar/
|
47
|
+
return Framework.lookup('Lift')
|
48
|
+
elsif contents =~ /WEB-INF\/classes\/org\/springframework/
|
49
|
+
return Framework.lookup('Spring')
|
50
|
+
elsif contents =~ /WEB-INF\/lib\/spring-core.*\.jar/
|
51
|
+
return Framework.lookup('Spring')
|
52
|
+
else
|
53
|
+
return Framework.lookup('JavaWeb')
|
54
|
+
end
|
55
|
+
|
56
|
+
# Simple Ruby Apps
|
57
|
+
elsif !Dir.glob('*.rb').empty?
|
58
|
+
matched_file = nil
|
59
|
+
Dir.glob('*.rb').each do |fname|
|
60
|
+
next if matched_file
|
61
|
+
File.open(fname, 'r') do |f|
|
62
|
+
str = f.read # This might want to be limited
|
63
|
+
matched_file = fname if (str && str.match(/^\s*require[\s\(]*['"]sinatra['"]/))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
if matched_file
|
67
|
+
f = Framework.lookup('Sinatra')
|
68
|
+
f.exec = "ruby #{matched_file}"
|
69
|
+
return f
|
70
|
+
end
|
71
|
+
|
72
|
+
# Node.js
|
73
|
+
elsif !Dir.glob('*.js').empty?
|
74
|
+
if File.exist?('server.js') || File.exist?('app.js') || File.exist?('index.js') || File.exist?('main.js')
|
75
|
+
return Framework.lookup('Node')
|
76
|
+
end
|
77
|
+
|
78
|
+
# PHP
|
79
|
+
elsif !Dir.glob('*.php').empty?
|
80
|
+
return Framework.lookup('PHP')
|
81
|
+
|
82
|
+
# Erlang/OTP using Rebar
|
83
|
+
elsif !Dir.glob('releases/*/*.rel').empty? && !Dir.glob('releases/*/*.boot').empty?
|
84
|
+
return Framework.lookup('Erlang/OTP Rebar')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
attr_reader :name, :description, :memory
|
93
|
+
attr_accessor :exec
|
94
|
+
|
95
|
+
alias :mem :memory
|
96
|
+
|
97
|
+
def initialize(framework=nil, opts={})
|
98
|
+
@name = framework || DEFAULT_FRAMEWORK
|
99
|
+
@memory = opts[:mem] || DEFAULT_MEM
|
100
|
+
@description = opts[:description] || 'Unknown Application Type'
|
101
|
+
@exec = opts[:exec]
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_s
|
105
|
+
description
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
data/lib/cli/runner.rb
ADDED
@@ -0,0 +1,490 @@
|
|
1
|
+
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + '/usage'
|
5
|
+
|
6
|
+
class VMC::Cli::Runner
|
7
|
+
|
8
|
+
attr_reader :namespace
|
9
|
+
attr_reader :action
|
10
|
+
attr_reader :args
|
11
|
+
attr_reader :options
|
12
|
+
|
13
|
+
def self.run(args)
|
14
|
+
new(args).run
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(args=[])
|
18
|
+
@args = args
|
19
|
+
@options = { :colorize => true }
|
20
|
+
@exit_status = true
|
21
|
+
end
|
22
|
+
|
23
|
+
# Collect all the available options for all commands
|
24
|
+
# Some duplicates exists to capture all scenarios
|
25
|
+
def parse_options!
|
26
|
+
opts_parser = OptionParser.new do |opts|
|
27
|
+
opts.banner = "\nAvailable options:\n\n"
|
28
|
+
|
29
|
+
opts.on('--email EMAIL') { |email| @options[:email] = email }
|
30
|
+
opts.on('--user EMAIL') { |email| @options[:email] = email }
|
31
|
+
opts.on('--passwd PASS') { |pass| @options[:password] = pass }
|
32
|
+
opts.on('--pass PASS') { |pass| @options[:password] = pass }
|
33
|
+
opts.on('--password PASS') { |pass| @options[:password] = pass }
|
34
|
+
opts.on('--app NAME') { |name| @options[:name] = name }
|
35
|
+
opts.on('--name NAME') { |name| @options[:name] = name }
|
36
|
+
opts.on('--bind BIND') { |bind| @options[:bind] = bind }
|
37
|
+
opts.on('--instance INST') { |inst| @options[:instance] = inst }
|
38
|
+
opts.on('--instances INST') { |inst| @options[:instances] = inst }
|
39
|
+
opts.on('--url URL') { |url| @options[:url] = url }
|
40
|
+
opts.on('--mem MEM') { |mem| @options[:mem] = mem }
|
41
|
+
opts.on('--path PATH') { |path| @options[:path] = path }
|
42
|
+
opts.on('--no-start') { @options[:nostart] = true }
|
43
|
+
opts.on('--nostart') { @options[:nostart] = true }
|
44
|
+
opts.on('--force') { @options[:force] = true }
|
45
|
+
opts.on('--all') { @options[:all] = true }
|
46
|
+
|
47
|
+
# generic tracing and debugging
|
48
|
+
opts.on('-t [TKEY]') { |tkey| @options[:trace] = tkey || true }
|
49
|
+
opts.on('--trace [TKEY]') { |tkey| @options[:trace] = tkey || true }
|
50
|
+
|
51
|
+
opts.on('-q', '--quiet') { @options[:quiet] = true }
|
52
|
+
|
53
|
+
# Don't use builtin zip
|
54
|
+
opts.on('--no-zip') { @options[:nozip] = true }
|
55
|
+
opts.on('--nozip') { @options[:nozip] = true }
|
56
|
+
|
57
|
+
opts.on('--no-resources') { @options[:noresources] = true }
|
58
|
+
opts.on('--noresources') { @options[:noresources] = true }
|
59
|
+
|
60
|
+
opts.on('--no-color') { @options[:colorize] = false }
|
61
|
+
opts.on('--verbose') { @options[:verbose] = true }
|
62
|
+
|
63
|
+
opts.on('-n','--no-prompt') { @options[:noprompts] = true }
|
64
|
+
opts.on('--noprompt') { @options[:noprompts] = true }
|
65
|
+
opts.on('--non-interactive') { @options[:noprompts] = true }
|
66
|
+
|
67
|
+
opts.on('--prefix') { @options[:prefixlogs] = true }
|
68
|
+
opts.on('--prefix-logs') { @options[:prefixlogs] = true }
|
69
|
+
opts.on('--prefixlogs') { @options[:prefixlogs] = true }
|
70
|
+
|
71
|
+
opts.on('--json') { @options[:json] = true }
|
72
|
+
|
73
|
+
opts.on('-v', '--version') { set_cmd(:misc, :version) }
|
74
|
+
opts.on('-h', '--help') { puts "#{command_usage}\n"; exit }
|
75
|
+
|
76
|
+
opts.on('--runtime RUNTIME') { |rt| @options[:runtime] = rt }
|
77
|
+
|
78
|
+
# deprecated
|
79
|
+
opts.on('--exec EXEC') { |exec| @options[:exec] = exec }
|
80
|
+
opts.on('--noframework') { @options[:noframework] = true }
|
81
|
+
opts.on('--canary') { @options[:canary] = true }
|
82
|
+
|
83
|
+
# Proxying for another user, requires admin privileges
|
84
|
+
opts.on('-u PROXY') { |proxy| @options[:proxy] = proxy }
|
85
|
+
|
86
|
+
opts.on_tail('--options') { puts "#{opts}\n"; exit }
|
87
|
+
end
|
88
|
+
instances_delta_arg = check_instances_delta!
|
89
|
+
@args = opts_parser.parse!(@args)
|
90
|
+
@args.concat instances_delta_arg
|
91
|
+
convert_options!
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def check_instances_delta!
|
96
|
+
return unless @args
|
97
|
+
instance_args = @args.select { |arg| /^[-]\d+$/ =~ arg } || []
|
98
|
+
@args.delete_if { |arg| instance_args.include? arg}
|
99
|
+
instance_args
|
100
|
+
end
|
101
|
+
|
102
|
+
def display_help
|
103
|
+
puts command_usage
|
104
|
+
exit
|
105
|
+
end
|
106
|
+
|
107
|
+
def convert_options!
|
108
|
+
# make sure certain options are valid and in correct form.
|
109
|
+
@options[:instances] = Integer(@options[:instances]) if @options[:instances]
|
110
|
+
end
|
111
|
+
|
112
|
+
def set_cmd(namespace, action, args_range=0)
|
113
|
+
return if @help_only
|
114
|
+
unless args_range == "*" || args_range.is_a?(Range)
|
115
|
+
args_range = (args_range.to_i..args_range.to_i)
|
116
|
+
end
|
117
|
+
|
118
|
+
if args_range == "*" || args_range.include?(@args.size)
|
119
|
+
@namespace = namespace
|
120
|
+
@action = action
|
121
|
+
else
|
122
|
+
@exit_status = false
|
123
|
+
if @args.size > args_range.last
|
124
|
+
usage_error("Too many arguments for [#{action}]: %s" % [ @args[args_range.last..-1].map{|a| "'#{a}'"}.join(', ') ])
|
125
|
+
else
|
126
|
+
usage_error("Not enough arguments for [#{action}]")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def parse_command!
|
132
|
+
# just return if already set, happends with -v, -h
|
133
|
+
return if @namespace && @action
|
134
|
+
|
135
|
+
verb = @args.shift
|
136
|
+
case verb
|
137
|
+
|
138
|
+
when 'version'
|
139
|
+
usage('vmc version')
|
140
|
+
set_cmd(:misc, :version)
|
141
|
+
|
142
|
+
when 'target'
|
143
|
+
usage('vmc target [url] [--url]')
|
144
|
+
if @args.size == 1
|
145
|
+
set_cmd(:misc, :set_target, 1)
|
146
|
+
else
|
147
|
+
set_cmd(:misc, :target)
|
148
|
+
end
|
149
|
+
|
150
|
+
when 'targets'
|
151
|
+
usage('vmc targets')
|
152
|
+
set_cmd(:misc, :targets)
|
153
|
+
|
154
|
+
when 'tokens'
|
155
|
+
usage('vmc tokens')
|
156
|
+
set_cmd(:misc, :tokens)
|
157
|
+
|
158
|
+
when 'info'
|
159
|
+
usage('vmc info')
|
160
|
+
set_cmd(:misc, :info)
|
161
|
+
|
162
|
+
when 'runtimes'
|
163
|
+
usage('vmc runtimes')
|
164
|
+
set_cmd(:misc, :runtimes)
|
165
|
+
|
166
|
+
when 'frameworks'
|
167
|
+
usage('vmc frameworks')
|
168
|
+
set_cmd(:misc, :frameworks)
|
169
|
+
|
170
|
+
when 'user'
|
171
|
+
usage('vmc user')
|
172
|
+
set_cmd(:user, :info)
|
173
|
+
|
174
|
+
when 'login'
|
175
|
+
usage('vmc login [email] [--email EMAIL] [--passwd PASS]')
|
176
|
+
if @args.size == 1
|
177
|
+
set_cmd(:user, :login, 1)
|
178
|
+
else
|
179
|
+
set_cmd(:user, :login)
|
180
|
+
end
|
181
|
+
|
182
|
+
when 'logout'
|
183
|
+
usage('vmc logout')
|
184
|
+
set_cmd(:user, :logout)
|
185
|
+
|
186
|
+
when 'passwd'
|
187
|
+
usage('vmc passwd')
|
188
|
+
if @args.size == 1
|
189
|
+
set_cmd(:user, :change_password, 1)
|
190
|
+
else
|
191
|
+
set_cmd(:user, :change_password)
|
192
|
+
end
|
193
|
+
|
194
|
+
when 'add-user', 'add_user', 'create_user', 'create-user', 'register'
|
195
|
+
usage('vmc add-user [user] [--email EMAIL] [--passwd PASS]')
|
196
|
+
if @args.size == 1
|
197
|
+
set_cmd(:admin, :add_user, 1)
|
198
|
+
else
|
199
|
+
set_cmd(:admin, :add_user)
|
200
|
+
end
|
201
|
+
|
202
|
+
when 'delete-user', 'delete_user', 'unregister'
|
203
|
+
usage('vmc delete-user <user>')
|
204
|
+
set_cmd(:admin, :delete_user, 1)
|
205
|
+
|
206
|
+
when 'users'
|
207
|
+
usage('vmc users')
|
208
|
+
set_cmd(:admin, :users)
|
209
|
+
|
210
|
+
when 'apps'
|
211
|
+
usage('vmc apps')
|
212
|
+
set_cmd(:apps, :apps)
|
213
|
+
|
214
|
+
when 'list'
|
215
|
+
usage('vmc list')
|
216
|
+
set_cmd(:apps, :list)
|
217
|
+
|
218
|
+
when 'start'
|
219
|
+
usage('vmc start <appname>')
|
220
|
+
set_cmd(:apps, :start, 1)
|
221
|
+
|
222
|
+
when 'stop'
|
223
|
+
usage('vmc stop <appname>')
|
224
|
+
set_cmd(:apps, :stop, 1)
|
225
|
+
|
226
|
+
when 'restart'
|
227
|
+
usage('vmc restart <appname>')
|
228
|
+
set_cmd(:apps, :restart, 1)
|
229
|
+
|
230
|
+
when 'rename'
|
231
|
+
usage('vmc rename <appname> <newname>')
|
232
|
+
set_cmd(:apps, :rename, 2)
|
233
|
+
|
234
|
+
when 'mem'
|
235
|
+
usage('vmc mem <appname> [memsize]')
|
236
|
+
if @args.size == 2
|
237
|
+
set_cmd(:apps, :mem, 2)
|
238
|
+
else
|
239
|
+
set_cmd(:apps, :mem, 1)
|
240
|
+
end
|
241
|
+
|
242
|
+
when 'stats'
|
243
|
+
usage('vmc stats <appname>')
|
244
|
+
set_cmd(:apps, :stats, 1)
|
245
|
+
|
246
|
+
when 'map'
|
247
|
+
usage('vmc map <appname> <url>')
|
248
|
+
set_cmd(:apps, :map, 2)
|
249
|
+
|
250
|
+
when 'unmap'
|
251
|
+
usage('vmc unmap <appname> <url>')
|
252
|
+
set_cmd(:apps, :unmap, 2)
|
253
|
+
|
254
|
+
when 'delete'
|
255
|
+
usage('vmc delete <appname>')
|
256
|
+
if @options[:all] && @args.size == 0
|
257
|
+
set_cmd(:apps, :delete)
|
258
|
+
else
|
259
|
+
set_cmd(:apps, :delete, 1)
|
260
|
+
end
|
261
|
+
|
262
|
+
when 'files'
|
263
|
+
usage('vmc files <appname> [path] [--instance N] [--all] [--prefix]')
|
264
|
+
if @args.size == 1
|
265
|
+
set_cmd(:apps, :files, 1)
|
266
|
+
else
|
267
|
+
set_cmd(:apps, :files, 2)
|
268
|
+
end
|
269
|
+
|
270
|
+
when 'logs'
|
271
|
+
usage('vmc logs <appname> [--instance N] [--all] [--prefix]')
|
272
|
+
set_cmd(:apps, :logs, 1)
|
273
|
+
|
274
|
+
when 'instances', 'scale'
|
275
|
+
if @args.size == 1
|
276
|
+
usage('vmc instances <appname>')
|
277
|
+
set_cmd(:apps, :instances, 1)
|
278
|
+
else
|
279
|
+
usage('vmc instances <appname> <num|delta>')
|
280
|
+
set_cmd(:apps, :instances, 2)
|
281
|
+
end
|
282
|
+
|
283
|
+
when 'crashes'
|
284
|
+
usage('vmc crashes <appname>')
|
285
|
+
set_cmd(:apps, :crashes, 1)
|
286
|
+
|
287
|
+
when 'crashlogs'
|
288
|
+
usage('vmc crashlogs <appname>')
|
289
|
+
set_cmd(:apps, :crashlogs, 1)
|
290
|
+
|
291
|
+
when 'push'
|
292
|
+
usage('vmc push [appname] [--path PATH] [--url URL] [--instances N] [--mem] [--runtime RUNTIME] [--no-start]')
|
293
|
+
if @args.size == 1
|
294
|
+
set_cmd(:apps, :push, 1)
|
295
|
+
else
|
296
|
+
set_cmd(:apps, :push, 0)
|
297
|
+
end
|
298
|
+
|
299
|
+
when 'update'
|
300
|
+
usage('vmc update <appname> [--path PATH]')
|
301
|
+
set_cmd(:apps, :update, 1)
|
302
|
+
|
303
|
+
when 'services'
|
304
|
+
usage('vmc services')
|
305
|
+
set_cmd(:services, :services)
|
306
|
+
|
307
|
+
when 'env'
|
308
|
+
usage('vmc env <appname>')
|
309
|
+
set_cmd(:apps, :environment, 1)
|
310
|
+
|
311
|
+
when 'env-add'
|
312
|
+
usage('vmc env-add <appname> <variable[=]value>')
|
313
|
+
if @args.size == 2
|
314
|
+
set_cmd(:apps, :environment_add, 2)
|
315
|
+
elsif @args.size == 3
|
316
|
+
set_cmd(:apps, :environment_add, 3)
|
317
|
+
end
|
318
|
+
|
319
|
+
when 'env-del'
|
320
|
+
usage('vmc env-del <appname> <variable>')
|
321
|
+
set_cmd(:apps, :environment_del, 2)
|
322
|
+
|
323
|
+
when 'create-service', 'create_service'
|
324
|
+
usage('vmc create-service [service] [servicename] [appname] [--name servicename] [--bind appname]')
|
325
|
+
set_cmd(:services, :create_service) if @args.size == 0
|
326
|
+
set_cmd(:services, :create_service, 1) if @args.size == 1
|
327
|
+
set_cmd(:services, :create_service, 2) if @args.size == 2
|
328
|
+
set_cmd(:services, :create_service, 3) if @args.size == 3
|
329
|
+
|
330
|
+
when 'delete-service', 'delete_service'
|
331
|
+
usage('vmc delete-service <service>')
|
332
|
+
if @args.size == 1
|
333
|
+
set_cmd(:services, :delete_service, 1)
|
334
|
+
else
|
335
|
+
set_cmd(:services, :delete_service)
|
336
|
+
end
|
337
|
+
|
338
|
+
when 'bind-service', 'bind_service'
|
339
|
+
usage('vmc bind-service <servicename> <appname>')
|
340
|
+
set_cmd(:services, :bind_service, 2)
|
341
|
+
|
342
|
+
when 'unbind-service', 'unbind_service'
|
343
|
+
usage('vmc unbind-service <servicename> <appname>')
|
344
|
+
set_cmd(:services, :unbind_service, 2)
|
345
|
+
|
346
|
+
when 'clone-services'
|
347
|
+
usage('vmc clone-services <src-app> <dest-app>')
|
348
|
+
set_cmd(:services, :clone_services, 2)
|
349
|
+
|
350
|
+
when 'aliases'
|
351
|
+
usage('vmc aliases')
|
352
|
+
set_cmd(:misc, :aliases)
|
353
|
+
|
354
|
+
when 'alias'
|
355
|
+
usage('vmc alias <alias[=]command>')
|
356
|
+
if @args.size == 1
|
357
|
+
set_cmd(:misc, :alias, 1)
|
358
|
+
elsif @args.size == 2
|
359
|
+
set_cmd(:misc, :alias, 2)
|
360
|
+
end
|
361
|
+
|
362
|
+
when 'unalias'
|
363
|
+
usage('vmc unalias <alias>')
|
364
|
+
set_cmd(:misc, :unalias, 1)
|
365
|
+
|
366
|
+
when 'help'
|
367
|
+
display_help if @args.size == 0
|
368
|
+
@help_only = true
|
369
|
+
parse_command!
|
370
|
+
|
371
|
+
when 'usage'
|
372
|
+
display basic_usage
|
373
|
+
exit(true)
|
374
|
+
|
375
|
+
when 'options'
|
376
|
+
# Simulate --options
|
377
|
+
@args = @args.unshift('--options')
|
378
|
+
parse_options!
|
379
|
+
|
380
|
+
else
|
381
|
+
if verb
|
382
|
+
display "vmc: Unknown command [#{verb}]"
|
383
|
+
display basic_usage
|
384
|
+
exit(false)
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
def process_aliases!
|
390
|
+
return if @args.empty?
|
391
|
+
aliases = VMC::Cli::Config.aliases
|
392
|
+
aliases.each_pair do |k,v|
|
393
|
+
if @args[0] == k
|
394
|
+
display "[#{@args[0]} aliased to #{aliases.invert[key]}]" if @options[:verbose]
|
395
|
+
@args[0] = v
|
396
|
+
break;
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def usage(msg = nil)
|
402
|
+
@usage = msg if msg
|
403
|
+
@usage
|
404
|
+
end
|
405
|
+
|
406
|
+
def usage_error(msg = nil)
|
407
|
+
@usage_error = msg if msg
|
408
|
+
@usage_error
|
409
|
+
end
|
410
|
+
|
411
|
+
def run
|
412
|
+
|
413
|
+
trap('TERM') { print "\nTerminated\n"; exit(false)}
|
414
|
+
|
415
|
+
parse_options!
|
416
|
+
|
417
|
+
@options[:colorize] = false unless STDOUT.tty?
|
418
|
+
|
419
|
+
VMC::Cli::Config.colorize = @options.delete(:colorize)
|
420
|
+
VMC::Cli::Config.nozip = @options.delete(:nozip)
|
421
|
+
VMC::Cli::Config.trace = @options.delete(:trace)
|
422
|
+
VMC::Cli::Config.output ||= STDOUT unless @options[:quiet]
|
423
|
+
|
424
|
+
process_aliases!
|
425
|
+
parse_command!
|
426
|
+
|
427
|
+
if @namespace && @action
|
428
|
+
eval("VMC::Cli::Command::#{@namespace.to_s.capitalize}").new(@options).send(@action.to_sym, *@args)
|
429
|
+
elsif @help_only || @usage
|
430
|
+
display_usage
|
431
|
+
else
|
432
|
+
display basic_usage
|
433
|
+
exit(false)
|
434
|
+
end
|
435
|
+
|
436
|
+
rescue OptionParser::InvalidOption => e
|
437
|
+
rescue OptionParser::AmbiguousOption => e
|
438
|
+
puts(e.message.red)
|
439
|
+
puts("\n")
|
440
|
+
puts(basic_usage)
|
441
|
+
@exit_status = false
|
442
|
+
rescue VMC::Client::AuthError => e
|
443
|
+
if VMC::Cli::Config.auth_token.nil?
|
444
|
+
puts "Login Required".red
|
445
|
+
else
|
446
|
+
puts "Not Authorized".red
|
447
|
+
end
|
448
|
+
@exit_status = false
|
449
|
+
rescue VMC::Client::TargetError, VMC::Client::NotFound, VMC::Client::BadTarget => e
|
450
|
+
puts e.message.red
|
451
|
+
@exit_status = false
|
452
|
+
rescue VMC::Client::HTTPException => e
|
453
|
+
puts e.message.red
|
454
|
+
@exit_status = false
|
455
|
+
rescue VMC::Cli::GracefulExit => e
|
456
|
+
# Redirected commands end up generating this exception (kind of goto)
|
457
|
+
rescue VMC::Cli::CliExit => e
|
458
|
+
puts e.message.red
|
459
|
+
@exit_status = false
|
460
|
+
rescue VMC::Cli::CliError => e
|
461
|
+
say("Error #{e.error_code}: #{e.message}".red)
|
462
|
+
@exit_status = false
|
463
|
+
rescue SystemExit => e
|
464
|
+
@exit_status = e.success?
|
465
|
+
rescue SyntaxError => e
|
466
|
+
puts e.message.red
|
467
|
+
puts e.backtrace
|
468
|
+
@exit_status = false
|
469
|
+
rescue Interrupt => e
|
470
|
+
say("\nInterrupted".red)
|
471
|
+
@exit_status = false
|
472
|
+
rescue => e
|
473
|
+
puts e.message.red
|
474
|
+
puts e.backtrace
|
475
|
+
@exit_status = false
|
476
|
+
ensure
|
477
|
+
say("\n")
|
478
|
+
@exit_status == true if @exit_status.nil?
|
479
|
+
if @options[:verbose]
|
480
|
+
if @exit_status
|
481
|
+
puts "[#{@namespace}:#{@action}] SUCCEEDED".green
|
482
|
+
else
|
483
|
+
puts "[#{@namespace}:#{@action}] FAILED".red
|
484
|
+
end
|
485
|
+
say("\n")
|
486
|
+
end
|
487
|
+
exit(@exit_status)
|
488
|
+
end
|
489
|
+
|
490
|
+
end
|