vmc 0.0.8 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +8 -3
- data/README.md +83 -0
- data/Rakefile +11 -65
- data/bin/vmc +3 -2
- data/lib/cli/commands/admin.rb +57 -0
- data/lib/cli/commands/apps.rb +828 -0
- data/lib/cli/commands/base.rb +56 -0
- data/lib/cli/commands/misc.rb +99 -0
- data/lib/cli/commands/services.rb +84 -0
- data/lib/cli/commands/user.rb +60 -0
- data/lib/cli/config.rb +109 -0
- data/lib/cli/core_ext.rb +119 -0
- data/lib/cli/errors.rb +19 -0
- data/lib/cli/frameworks.rb +97 -0
- data/lib/cli/runner.rb +437 -0
- data/lib/cli/services_helper.rb +74 -0
- data/lib/cli/usage.rb +94 -0
- data/lib/cli/version.rb +5 -0
- data/lib/cli/zip_util.rb +61 -0
- data/lib/cli.rb +30 -0
- data/lib/vmc/client.rb +415 -0
- data/lib/vmc/const.rb +19 -0
- data/lib/vmc.rb +2 -1589
- 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/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 +73 -0
- data/spec/unit/client_spec.rb +284 -0
- metadata +114 -71
- data/README +0 -58
- data/lib/parse.rb +0 -719
- data/lib/vmc_base.rb +0 -205
- data/vendor/gems/httpclient/VERSION +0 -1
- data/vendor/gems/httpclient/lib/http-access2/cookie.rb +0 -1
- data/vendor/gems/httpclient/lib/http-access2/http.rb +0 -1
- data/vendor/gems/httpclient/lib/http-access2.rb +0 -53
- data/vendor/gems/httpclient/lib/httpclient/auth.rb +0 -522
- data/vendor/gems/httpclient/lib/httpclient/cacert.p7s +0 -1579
- data/vendor/gems/httpclient/lib/httpclient/cacert_sha1.p7s +0 -1579
- data/vendor/gems/httpclient/lib/httpclient/connection.rb +0 -84
- data/vendor/gems/httpclient/lib/httpclient/cookie.rb +0 -562
- data/vendor/gems/httpclient/lib/httpclient/http.rb +0 -867
- data/vendor/gems/httpclient/lib/httpclient/session.rb +0 -864
- data/vendor/gems/httpclient/lib/httpclient/ssl_config.rb +0 -417
- data/vendor/gems/httpclient/lib/httpclient/timeout.rb +0 -136
- data/vendor/gems/httpclient/lib/httpclient/util.rb +0 -86
- data/vendor/gems/httpclient/lib/httpclient.rb +0 -1020
- data/vendor/gems/httpclient/lib/tags +0 -908
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'terminal-table/import'
|
4
|
+
require 'highline/import'
|
5
|
+
|
6
|
+
module VMC::Cli
|
7
|
+
|
8
|
+
module Command
|
9
|
+
|
10
|
+
class Base
|
11
|
+
attr_reader :no_prompt, :prompt_ok
|
12
|
+
|
13
|
+
def initialize(options={})
|
14
|
+
@options = options.dup
|
15
|
+
@no_prompt = @options[:noprompts]
|
16
|
+
@prompt_ok = !no_prompt
|
17
|
+
|
18
|
+
# Fix for system ruby and Highline (stdin) on MacOSX
|
19
|
+
if RUBY_PLATFORM =~ /darwin/ && RUBY_VERSION == '1.8.7' && RUBY_PATCHLEVEL <= 174
|
20
|
+
HighLine.track_eof = false
|
21
|
+
end
|
22
|
+
|
23
|
+
# Suppress colorize on Windows systems for now.
|
24
|
+
if !!RUBY_PLATFORM['mingw'] || !!RUBY_PLATFORM['mswin32'] || !!RUBY_PLATFORM['cygwin']
|
25
|
+
VMC::Cli::Config.colorize = false
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def client
|
31
|
+
return @client if @client
|
32
|
+
@client = VMC::Client.new(target_url, auth_token)
|
33
|
+
@client.trace = true if VMC::Cli::Config.trace
|
34
|
+
@client.proxy_for @options[:proxy] if @options[:proxy]
|
35
|
+
@client
|
36
|
+
end
|
37
|
+
|
38
|
+
def client_info
|
39
|
+
return @client_info if @client_info
|
40
|
+
@client_info = client.info
|
41
|
+
end
|
42
|
+
|
43
|
+
def target_url
|
44
|
+
return @target_url if @target_url
|
45
|
+
@target_url = VMC::Cli::Config.target_url
|
46
|
+
end
|
47
|
+
|
48
|
+
def auth_token
|
49
|
+
return @auth_token if @auth_token
|
50
|
+
@auth_token = VMC::Cli::Config.auth_token
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module VMC::Cli::Command
|
2
|
+
|
3
|
+
class Misc < Base
|
4
|
+
def version
|
5
|
+
say "vmc #{VMC::Cli::VERSION}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def target
|
9
|
+
return display JSON.pretty_generate({:target => target_url}) if @options[:json]
|
10
|
+
banner "[#{target_url}]"
|
11
|
+
end
|
12
|
+
|
13
|
+
def targets
|
14
|
+
targets = VMC::Cli::Config.targets
|
15
|
+
return display JSON.pretty_generate(targets) if @options[:json]
|
16
|
+
return display 'None specified' if targets.empty?
|
17
|
+
targets_table = table do |t|
|
18
|
+
t.headings = 'Target', 'Authorization'
|
19
|
+
targets.each { |target, token| t << [target, token] }
|
20
|
+
end
|
21
|
+
display "\n"
|
22
|
+
display targets_table
|
23
|
+
end
|
24
|
+
|
25
|
+
alias :tokens :targets
|
26
|
+
|
27
|
+
def set_target(target_url)
|
28
|
+
target_url = "http://#{target_url}" unless /^https?/ =~ target_url
|
29
|
+
client = VMC::Client.new(target_url)
|
30
|
+
unless client.target_valid?
|
31
|
+
if prompt_ok
|
32
|
+
display "Host is not valid: '#{target_url}'".red
|
33
|
+
show_response = ask "Would you like see the response [yN]? "
|
34
|
+
display "\n<<<\n#{client.raw_info}\n>>>\n" if show_response.upcase == 'Y'
|
35
|
+
end
|
36
|
+
exit(false)
|
37
|
+
else
|
38
|
+
VMC::Cli::Config.store_target(target_url)
|
39
|
+
say "Succesfully targeted to [#{target_url}]".green
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def info
|
44
|
+
info = client.info
|
45
|
+
return display JSON.pretty_generate(info) if @options[:json]
|
46
|
+
|
47
|
+
display "\n#{info[:description]}"
|
48
|
+
display "For support visit #{info[:support]}"
|
49
|
+
display ""
|
50
|
+
display "Target: #{target_url} (v#{info[:version]})"
|
51
|
+
display "Client: v#{VMC::Cli::VERSION}"
|
52
|
+
if info[:user]
|
53
|
+
display ''
|
54
|
+
display "User: #{info[:user]}"
|
55
|
+
end
|
56
|
+
if usage = info[:usage] and limits = info[:limits]
|
57
|
+
tmem = pretty_size(limits[:memory]*1024*1024)
|
58
|
+
mem = pretty_size(usage[:memory]*1024*1024)
|
59
|
+
tser = limits[:services]
|
60
|
+
ser = usage[:services]
|
61
|
+
tapps = limits[:apps] || 0
|
62
|
+
apps = usage[:apps] || 0
|
63
|
+
display "Usage: Memory (#{mem} of #{tmem} total)"
|
64
|
+
display " Services (#{ser} of #{tser} total)"
|
65
|
+
display " Apps (#{apps} of #{tapps} total)" if limits[:apps]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def aliases
|
70
|
+
aliases = VMC::Cli::Config.aliases
|
71
|
+
return display JSON.pretty_generate(aliases) if @options[:json]
|
72
|
+
return display "No Aliases" if aliases.empty?
|
73
|
+
atable = table do |t|
|
74
|
+
t.headings = 'Alias', "Command"
|
75
|
+
aliases.each { |k,v| t << [v, k] }
|
76
|
+
end
|
77
|
+
display "\n"
|
78
|
+
display atable
|
79
|
+
end
|
80
|
+
|
81
|
+
def alias(k, v=nil)
|
82
|
+
k,v = k.split('=') unless v
|
83
|
+
aliases = VMC::Cli::Config.aliases
|
84
|
+
aliases[k] = v
|
85
|
+
VMC::Cli::Config.store_aliases(aliases)
|
86
|
+
display "Successfully aliased '#{k}' to '#{v}'".green
|
87
|
+
end
|
88
|
+
|
89
|
+
def unalias(key)
|
90
|
+
aliases = VMC::Cli::Config.aliases
|
91
|
+
aliases.delete(key)
|
92
|
+
VMC::Cli::Config.store_aliases(aliases)
|
93
|
+
display "Successfully unaliased '#{key}'".green
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module VMC::Cli::Command
|
2
|
+
|
3
|
+
class Services < Base
|
4
|
+
include VMC::Cli::ServicesHelper
|
5
|
+
|
6
|
+
def services
|
7
|
+
ss = client.services_info
|
8
|
+
ps = client.services
|
9
|
+
if @options[:json]
|
10
|
+
services = { :system => ss, :provisioned => ps }
|
11
|
+
return display JSON.pretty_generate(services)
|
12
|
+
end
|
13
|
+
display_system_services(ss)
|
14
|
+
display_provisioned_services(ps)
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_service(service=nil, name=nil, appname=nil)
|
18
|
+
unless no_prompt || service
|
19
|
+
services = client.services_info
|
20
|
+
err 'No services available to provision' if services.empty?
|
21
|
+
choose do |menu|
|
22
|
+
menu.prompt = 'Please select one you wish to provision: '
|
23
|
+
menu.select_by = :index_or_name
|
24
|
+
services.each do |service_type, value|
|
25
|
+
value.each do |vendor, version|
|
26
|
+
menu.choice(vendor.to_s) { service = vendor.to_s }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
name = @options[:name] unless name
|
32
|
+
unless name
|
33
|
+
name = random_service_name(service)
|
34
|
+
picked_name = true
|
35
|
+
end
|
36
|
+
create_service_banner(service, name, picked_name)
|
37
|
+
appname = @options[:bind] unless appname
|
38
|
+
bind_service_banner(name, appname) if appname
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete_service(service=nil)
|
42
|
+
unless no_prompt || service
|
43
|
+
user_services = client.services
|
44
|
+
err 'No services available to delete' if user_services.empty?
|
45
|
+
choose do |menu|
|
46
|
+
menu.prompt = 'Please select one you wish to delete: '
|
47
|
+
menu.select_by = :index_or_name
|
48
|
+
user_services.each do |s|
|
49
|
+
menu.choice(s[:name]) { service = s[:name] }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
err "Service name required." unless service
|
54
|
+
display "Deleting service [#{service}]: ", false
|
55
|
+
client.delete_service(service)
|
56
|
+
display 'OK'.green
|
57
|
+
end
|
58
|
+
|
59
|
+
def bind_service(service, appname)
|
60
|
+
bind_service_banner(service, appname)
|
61
|
+
end
|
62
|
+
|
63
|
+
def unbind_service(service, appname)
|
64
|
+
unbind_service_banner(service, appname)
|
65
|
+
end
|
66
|
+
|
67
|
+
def clone_services(src_app, dest_app)
|
68
|
+
begin
|
69
|
+
src = client.app_info(src_app)
|
70
|
+
dest = client.app_info(dest_app)
|
71
|
+
rescue
|
72
|
+
end
|
73
|
+
|
74
|
+
err "Application '#{src_app}' does not exist" unless src
|
75
|
+
err "Application '#{dest_app}' does not exist" unless dest
|
76
|
+
|
77
|
+
services = src[:services]
|
78
|
+
err 'No services to clone' unless services && !services.empty?
|
79
|
+
services.each { |service| bind_service_banner(service, dest_app, false) }
|
80
|
+
check_app_for_restart(dest_app)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module VMC::Cli::Command
|
2
|
+
|
3
|
+
class User < Base
|
4
|
+
|
5
|
+
def info
|
6
|
+
info = client.info
|
7
|
+
username = info[:user] || 'N/A'
|
8
|
+
return display JSON.pretty_generate([username]) if @options[:json]
|
9
|
+
display "\n[#{username}]"
|
10
|
+
end
|
11
|
+
|
12
|
+
def login(email=nil)
|
13
|
+
email = @options[:email] unless email
|
14
|
+
password = @options[:password]
|
15
|
+
tries = 0
|
16
|
+
email = ask("Email: ") unless no_prompt || email
|
17
|
+
password = ask("Password: ") {|q| q.echo = '*'} unless no_prompt || password
|
18
|
+
err "Need a valid email" unless email
|
19
|
+
err "Need a password" unless password
|
20
|
+
login_and_save_token(email, password)
|
21
|
+
say "Successfully logged into [#{target_url}]".green
|
22
|
+
rescue VMC::Client::TargetError
|
23
|
+
display "Problem with login, invalid account or password.".red
|
24
|
+
retry if (tries += 1) < 3 && prompt_ok
|
25
|
+
exit 1
|
26
|
+
rescue => e
|
27
|
+
display "Problem with login, #{e}, try again or register for an account.".red
|
28
|
+
exit 1
|
29
|
+
end
|
30
|
+
|
31
|
+
def logout
|
32
|
+
VMC::Cli::Config.remove_token_file
|
33
|
+
say "Successfully logged out of [#{target_url}]".green
|
34
|
+
end
|
35
|
+
|
36
|
+
def change_password(password=nil)
|
37
|
+
info = client.info
|
38
|
+
email = info[:user]
|
39
|
+
err "Need to be logged in to change password." unless email
|
40
|
+
say "Changing password for '#{email}'\n"
|
41
|
+
unless no_prompt
|
42
|
+
password = ask("New Password: ") {|q| q.echo = '*'}
|
43
|
+
password2 = ask("Verify Password: ") {|q| q.echo = '*'}
|
44
|
+
err "Passwords did not match, try again" if password != password2
|
45
|
+
end
|
46
|
+
err "Password required" unless password
|
47
|
+
client.change_password(password)
|
48
|
+
say "\nSuccessfully changed password".green
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def login_and_save_token(email, password)
|
54
|
+
token = client.login(email, password)
|
55
|
+
VMC::Cli::Config.store_token(token)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/lib/cli/config.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'json/pure'
|
6
|
+
|
7
|
+
module VMC::Cli
|
8
|
+
class Config
|
9
|
+
|
10
|
+
DEFAULT_TARGET = 'api.vcap.me'
|
11
|
+
DEFAULT_SUGGEST = 'vcap.me'
|
12
|
+
|
13
|
+
TARGET_FILE = '~/.vmc_target'
|
14
|
+
TOKEN_FILE = '~/.vmc_token'
|
15
|
+
INSTANCES_FILE = '~/.vmc_instances'
|
16
|
+
ALIASES_FILE = '~/.vmc_aliases'
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_accessor :colorize
|
20
|
+
attr_accessor :output
|
21
|
+
attr_accessor :trace
|
22
|
+
attr_accessor :nozip
|
23
|
+
attr_reader :suggest_url
|
24
|
+
|
25
|
+
def target_url
|
26
|
+
return @target_url if @target_url
|
27
|
+
target_file = File.expand_path(TARGET_FILE)
|
28
|
+
if File.exists? target_file
|
29
|
+
@target_url = File.read(target_file).strip!
|
30
|
+
ha = @target_url.split('.')
|
31
|
+
ha.shift
|
32
|
+
@suggest_url = ha.join('.')
|
33
|
+
@suggest_url = DEFAULT_SUGGEST if @suggest_url.empty?
|
34
|
+
else
|
35
|
+
@target_url = DEFAULT_TARGET
|
36
|
+
@suggest_url = DEFAULT_SUGGEST
|
37
|
+
end
|
38
|
+
@target_url = "http://#{@target_url}" unless /^https?/ =~ @target_url
|
39
|
+
@target_url
|
40
|
+
end
|
41
|
+
|
42
|
+
def store_target(target_host)
|
43
|
+
target_file = File.expand_path(TARGET_FILE)
|
44
|
+
File.open(target_file, 'w+') { |f| f.puts target_host }
|
45
|
+
FileUtils.chmod 0600, target_file
|
46
|
+
end
|
47
|
+
|
48
|
+
def all_tokens
|
49
|
+
token_file = File.expand_path(TOKEN_FILE)
|
50
|
+
return nil unless File.exists? token_file
|
51
|
+
contents = File.read(token_file).strip
|
52
|
+
JSON.parse(contents)
|
53
|
+
end
|
54
|
+
|
55
|
+
alias :targets :all_tokens
|
56
|
+
|
57
|
+
def auth_token
|
58
|
+
return @token if @token
|
59
|
+
tokens = all_tokens
|
60
|
+
@token = tokens[target_url] if tokens
|
61
|
+
end
|
62
|
+
|
63
|
+
def remove_token_file
|
64
|
+
FileUtils.rm_f(File.expand_path(TOKEN_FILE))
|
65
|
+
end
|
66
|
+
|
67
|
+
def store_token(token)
|
68
|
+
tokens = all_tokens || {}
|
69
|
+
tokens[target_url] = token
|
70
|
+
token_file = File.expand_path(TOKEN_FILE)
|
71
|
+
File.open(token_file, 'w+') { |f| f.write(tokens.to_json) }
|
72
|
+
FileUtils.chmod 0600, token_file
|
73
|
+
end
|
74
|
+
|
75
|
+
def instances
|
76
|
+
instances_file = File.expand_path(INSTANCES_FILE)
|
77
|
+
return nil unless File.exists? instances_file
|
78
|
+
contents = File.read(instances_file).strip
|
79
|
+
JSON.parse(contents)
|
80
|
+
end
|
81
|
+
|
82
|
+
def store_instances(instances)
|
83
|
+
instances_file = File.expand_path(INSTANCES_FILE)
|
84
|
+
File.open(instances_file, 'w') { |f| f.write(instances.to_json) }
|
85
|
+
end
|
86
|
+
|
87
|
+
def aliases
|
88
|
+
aliases_file = File.expand_path(ALIASES_FILE)
|
89
|
+
# bacward compatible
|
90
|
+
unless File.exists? aliases_file
|
91
|
+
old_aliases_file = File.expand_path('~/.vmc-aliases')
|
92
|
+
FileUtils.mv(old_aliases_file, aliases_file) if File.exists? old_aliases_file
|
93
|
+
end
|
94
|
+
aliases = YAML.load_file(aliases_file) rescue {}
|
95
|
+
end
|
96
|
+
|
97
|
+
def store_aliases(aliases)
|
98
|
+
aliases_file = File.expand_path(ALIASES_FILE)
|
99
|
+
File.open(aliases_file, 'wb') {|f| f.write(aliases.to_yaml)}
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
def initialize(work_dir = Dir.pwd)
|
105
|
+
@work_dir = work_dir
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
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,97 @@
|
|
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' => ['rails/1.0', { :mem => '256M', :description => 'Rails Application'}],
|
10
|
+
'Spring' => ['spring_web/1.0', { :mem => '512M', :description => 'Java SpringSource Spring Application'}],
|
11
|
+
'Grails' => ['grails/1.0', { :mem => '512M', :description => 'Java SpringSource Grails Application'}],
|
12
|
+
'Roo' => ['spring_web/1.0', { :mem => '512M', :description => 'Java SpringSource Roo Application'}],
|
13
|
+
'JavaWeb' => ['spring_web/1.0', { :mem => '512M', :description => 'Java Web Application'}],
|
14
|
+
'Sinatra' => [DEFAULT_FRAMEWORK, { :mem => '128M', :description => 'Sinatra Application'}],
|
15
|
+
'Node' => ['nodejs/1.0', { :mem => '64M', :description => 'NodeJS Application'}],
|
16
|
+
}
|
17
|
+
|
18
|
+
class << self
|
19
|
+
|
20
|
+
def known_frameworks
|
21
|
+
FRAMEWORKS.keys
|
22
|
+
end
|
23
|
+
|
24
|
+
def lookup(name)
|
25
|
+
return Framework.new(*FRAMEWORKS[name])
|
26
|
+
end
|
27
|
+
|
28
|
+
def detect(path)
|
29
|
+
Dir.chdir(path) do
|
30
|
+
|
31
|
+
# Rails
|
32
|
+
if File.exist?('config/environment.rb')
|
33
|
+
return Framework.lookup('Rails')
|
34
|
+
|
35
|
+
# Java
|
36
|
+
elsif Dir.glob('*.war').first
|
37
|
+
war_file = Dir.glob('*.war').first
|
38
|
+
contents = ZipUtil.entry_lines(war_file)
|
39
|
+
|
40
|
+
# Spring Variations
|
41
|
+
if contents =~ /WEB-INF\/grails-app/
|
42
|
+
return Framework.lookup('Grails')
|
43
|
+
elsif contents =~ /WEB-INF\/classes\/org\/springframework/
|
44
|
+
return Framework.lookup('Spring')
|
45
|
+
elsif contents =~ /WEB-INF\/lib\/spring-core.*\.jar/
|
46
|
+
return Framework.lookup('Spring')
|
47
|
+
else
|
48
|
+
return Framework.lookup('JavaWeb')
|
49
|
+
end
|
50
|
+
|
51
|
+
# Simple Ruby Apps
|
52
|
+
elsif !Dir.glob('*.rb').empty?
|
53
|
+
matched_file = nil
|
54
|
+
Dir.glob('*.rb').each do |fname|
|
55
|
+
next if matched_file
|
56
|
+
File.open(fname, 'r') do |f|
|
57
|
+
str = f.read # This might want to be limited
|
58
|
+
matched_file = fname if (str && str.match(/^\s*require\s*'sinatra'/))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
if matched_file && !File.exist?('config.ru')
|
62
|
+
f = Framework.lookup('Sinatra')
|
63
|
+
f.exec = "ruby #{matched_file}"
|
64
|
+
return f
|
65
|
+
end
|
66
|
+
|
67
|
+
# Node.JS
|
68
|
+
elsif !Dir.glob('*.js').empty?
|
69
|
+
# Fixme, make other files work too..
|
70
|
+
if File.exist?('app.js') || File.exist?('index.js') || File.exist?('main.js')
|
71
|
+
return Framework.lookup('Node')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_reader :name, :description, :memory
|
81
|
+
attr_accessor :exec
|
82
|
+
|
83
|
+
alias :mem :memory
|
84
|
+
|
85
|
+
def initialize(framework=nil, opts={})
|
86
|
+
@name = framework || DEFAULT_FRAMEWORK
|
87
|
+
@memory = opts[:mem] || DEFAULT_MEM
|
88
|
+
@description = opts[:description] || 'Unknown Application Type'
|
89
|
+
@exec = opts[:exec]
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_s
|
93
|
+
description
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|