vmcu 0.3.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/LICENSE +24 -0
  2. data/README.md +160 -0
  3. data/Rakefile +101 -0
  4. data/bin/vmcu +6 -0
  5. data/caldecott_helper/Gemfile +10 -0
  6. data/caldecott_helper/Gemfile.lock +48 -0
  7. data/caldecott_helper/server.rb +43 -0
  8. data/config/clients.yml +17 -0
  9. data/config/micro/offline.conf +2 -0
  10. data/config/micro/paths.yml +22 -0
  11. data/config/micro/refresh_ip.rb +20 -0
  12. data/lib/cli.rb +47 -0
  13. data/lib/cli/commands/admin.rb +80 -0
  14. data/lib/cli/commands/apps.rb +1128 -0
  15. data/lib/cli/commands/base.rb +238 -0
  16. data/lib/cli/commands/manifest.rb +56 -0
  17. data/lib/cli/commands/micro.rb +115 -0
  18. data/lib/cli/commands/misc.rb +277 -0
  19. data/lib/cli/commands/services.rb +180 -0
  20. data/lib/cli/commands/user.rb +96 -0
  21. data/lib/cli/config.rb +192 -0
  22. data/lib/cli/console_helper.rb +157 -0
  23. data/lib/cli/core_ext.rb +122 -0
  24. data/lib/cli/errors.rb +19 -0
  25. data/lib/cli/frameworks.rb +244 -0
  26. data/lib/cli/manifest_helper.rb +302 -0
  27. data/lib/cli/runner.rb +543 -0
  28. data/lib/cli/services_helper.rb +84 -0
  29. data/lib/cli/tunnel_helper.rb +332 -0
  30. data/lib/cli/usage.rb +118 -0
  31. data/lib/cli/version.rb +7 -0
  32. data/lib/cli/zip_util.rb +77 -0
  33. data/lib/vmc.rb +3 -0
  34. data/lib/vmc/client.rb +591 -0
  35. data/lib/vmc/const.rb +22 -0
  36. data/lib/vmc/micro.rb +56 -0
  37. data/lib/vmc/micro/switcher/base.rb +97 -0
  38. data/lib/vmc/micro/switcher/darwin.rb +19 -0
  39. data/lib/vmc/micro/switcher/dummy.rb +15 -0
  40. data/lib/vmc/micro/switcher/linux.rb +16 -0
  41. data/lib/vmc/micro/switcher/windows.rb +31 -0
  42. data/lib/vmc/micro/vmrun.rb +158 -0
  43. metadata +263 -0
@@ -0,0 +1,180 @@
1
+ require "uuidtools"
2
+
3
+ module VMC::Cli::Command
4
+
5
+ class Services < Base
6
+ include VMC::Cli::ServicesHelper
7
+ include VMC::Cli::TunnelHelper
8
+
9
+ def services
10
+ ss = client.services_info
11
+ ps = client.services
12
+ ps.sort! {|a, b| a[:name] <=> b[:name] }
13
+
14
+ if @options[:json]
15
+ services = { :system => ss, :provisioned => ps }
16
+ return display JSON.pretty_generate(services)
17
+ end
18
+ display_system_services(ss)
19
+ display_provisioned_services(ps)
20
+ end
21
+
22
+ def create_service(service=nil, name=nil, appname=nil)
23
+ unless no_prompt || service
24
+ services = client.services_info
25
+ err 'No services available to provision' if services.empty?
26
+ service = ask(
27
+ "Which service would you like to provision?",
28
+ { :indexed => true,
29
+ :choices =>
30
+ services.values.collect { |type|
31
+ type.keys.collect(&:to_s)
32
+ }.flatten
33
+ }
34
+ )
35
+ end
36
+ name = @options[:name] unless name
37
+ unless name
38
+ name = random_service_name(service)
39
+ picked_name = true
40
+ end
41
+ create_service_banner(service, name, picked_name)
42
+ appname = @options[:bind] unless appname
43
+ bind_service_banner(name, appname) if appname
44
+ end
45
+
46
+ def delete_service(service=nil)
47
+ unless no_prompt || service
48
+ user_services = client.services
49
+ err 'No services available to delete' if user_services.empty?
50
+ service = ask(
51
+ "Which service would you like to delete?",
52
+ { :indexed => true,
53
+ :choices => user_services.collect { |s| s[:name] }
54
+ }
55
+ )
56
+ end
57
+ err "Service name required." unless service
58
+ display "Deleting service [#{service}]: ", false
59
+ client.delete_service(service)
60
+ display 'OK'.green
61
+ end
62
+
63
+ def bind_service(service, appname)
64
+ bind_service_banner(service, appname)
65
+ end
66
+
67
+ def unbind_service(service, appname)
68
+ unbind_service_banner(service, appname)
69
+ end
70
+
71
+ def clone_services(src_app, dest_app)
72
+ begin
73
+ src = client.app_info(src_app)
74
+ dest = client.app_info(dest_app)
75
+ rescue
76
+ end
77
+
78
+ err "Application '#{src_app}' does not exist" unless src
79
+ err "Application '#{dest_app}' does not exist" unless dest
80
+
81
+ services = src[:services]
82
+ err 'No services to clone' unless services && !services.empty?
83
+ services.each { |service| bind_service_banner(service, dest_app, false) }
84
+ check_app_for_restart(dest_app)
85
+ end
86
+
87
+ def tunnel(service=nil, client_name=nil)
88
+ unless defined? Caldecott
89
+ display "To use `vmcu tunnel', you must first install Caldecott:"
90
+ display ""
91
+ display "\tgem install caldecott"
92
+ display ""
93
+ display "Note that you'll need a C compiler. If you're on OS X, Xcode"
94
+ display "will provide one. If you're on Windows, try DevKit."
95
+ display ""
96
+ display "This manual step will be removed in the future."
97
+ display ""
98
+ err "Caldecott is not installed."
99
+ end
100
+
101
+ ps = client.services
102
+ err "No services available to tunnel to" if ps.empty?
103
+
104
+ unless service
105
+ choices = ps.collect { |s| s[:name] }.sort
106
+ service = ask(
107
+ "Which service to tunnel to?",
108
+ :choices => choices,
109
+ :indexed => true
110
+ )
111
+ end
112
+
113
+ info = ps.select { |s| s[:name] == service }.first
114
+
115
+ err "Unknown service '#{service}'" unless info
116
+
117
+ port = pick_tunnel_port(@options[:port] || 10000)
118
+
119
+ raise VMC::Client::AuthError unless client.logged_in?
120
+
121
+ if not tunnel_pushed?
122
+ display "Deploying tunnel application '#{tunnel_appname}'."
123
+ auth = UUIDTools::UUID.random_create.to_s
124
+ push_caldecott(auth)
125
+ bind_service_banner(service, tunnel_appname, false)
126
+ start_caldecott
127
+ else
128
+ auth = tunnel_auth
129
+ end
130
+
131
+ if not tunnel_healthy?(auth)
132
+ display "Redeploying tunnel application '#{tunnel_appname}'."
133
+
134
+ # We don't expect caldecott not to be running, so take the
135
+ # most aggressive restart method.. delete/re-push
136
+ client.delete_app(tunnel_appname)
137
+ invalidate_tunnel_app_info
138
+
139
+ push_caldecott(auth)
140
+ bind_service_banner(service, tunnel_appname, false)
141
+ start_caldecott
142
+ end
143
+
144
+ if not tunnel_bound?(service)
145
+ bind_service_banner(service, tunnel_appname)
146
+ end
147
+
148
+ conn_info = tunnel_connection_info info[:vendor], service, auth
149
+ display_tunnel_connection_info(conn_info)
150
+ display "Starting tunnel to #{service.bold} on port #{port.to_s.bold}."
151
+ start_tunnel(port, conn_info, auth)
152
+
153
+ clients = get_clients_for(info[:vendor])
154
+
155
+ if clients.empty?
156
+ client_name ||= "none"
157
+ else
158
+ client_name ||= ask(
159
+ "Which client would you like to start?",
160
+ :choices => ["none"] + clients.keys,
161
+ :indexed => true
162
+ )
163
+ end
164
+
165
+ if client_name == "none"
166
+ wait_for_tunnel_end
167
+ else
168
+ wait_for_tunnel_start(port)
169
+ unless start_local_prog(clients, client_name, conn_info, port)
170
+ err "'#{client_name}' execution failed; is it in your $PATH?"
171
+ end
172
+ end
173
+ end
174
+
175
+ def get_clients_for(type)
176
+ conf = VMC::Cli::Config.clients
177
+ conf[type] || {}
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,96 @@
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
+ if VMC::Cli::Config.uhuru_target
14
+ begin
15
+ token = email
16
+ token = @options[:token] unless token
17
+
18
+ unless no_prompt
19
+ display "Attempting login to Uhuru AppCloud [#{target_url}]" if target_url
20
+ token ||= ask("Token")
21
+ end
22
+
23
+ err "Need a valid authentication token" unless token
24
+ uhuru_login_and_save_token(token)
25
+ say "Successfully logged into Uhuru AppCloud [#{target_url}]".green
26
+ rescue => e
27
+ display "Problem with login to Uhuru AppCloud '#{target_url}', #{e}, try again or get a new one time token or regular authentication token.".red
28
+ exit 1
29
+ end
30
+ else
31
+ begin
32
+ email = @options[:email] unless email
33
+ password = @options[:password]
34
+ tries ||= 0
35
+
36
+ unless no_prompt
37
+ display "Attempting login to [#{target_url}]" if target_url
38
+ email ||= ask("Email")
39
+ password ||= ask("Password", :echo => "*")
40
+ end
41
+
42
+ err "Need a valid email" unless email
43
+ err "Need a password" unless password
44
+ login_and_save_token(email, password)
45
+ say "Successfully logged into [#{target_url}]".green
46
+ rescue VMC::Client::TargetError
47
+ display "Problem with login, invalid account or password when attempting to login to '#{target_url}'".red
48
+ retry if (tries += 1) < 3 && prompt_ok && !@options[:password]
49
+ exit 1
50
+ rescue => e
51
+ display "Problem with login to '#{target_url}', #{e}, try again or register for an account.".red
52
+ exit 1
53
+ end
54
+ end
55
+ end
56
+
57
+
58
+ def logout
59
+ VMC::Cli::Config.remove_token_file
60
+ if VMC::Cli::Config.uhuru_target
61
+ VMC::Cli::Config.store_uhuru_target(target_url, {})
62
+ end
63
+ say "Successfully logged out of [#{target_url}]".green
64
+ end
65
+
66
+ def change_password(password=nil)
67
+ info = client_info
68
+ email = info[:user]
69
+ err "Need to be logged in to change password." unless email
70
+ say "Changing password for '#{email}'\n"
71
+ unless no_prompt
72
+ password = ask "New Password", :echo => "*"
73
+ password2 = ask "Verify Password", :echo => "*"
74
+ err "Passwords did not match, try again" if password != password2
75
+ end
76
+ err "Password required" unless password
77
+ client.change_password(password)
78
+ say "\nSuccessfully changed password".green
79
+ end
80
+
81
+ private
82
+
83
+ def uhuru_login_and_save_token(token)
84
+ uhuru_target = client.uhuru_login(token)
85
+ raise "Unable to login" unless uhuru_target
86
+ VMC::Cli::Config.store_uhuru_target(target_url, uhuru_target)
87
+ end
88
+
89
+ def login_and_save_token(email, password)
90
+ token = client.login(email, password)
91
+ VMC::Cli::Config.store_token(token, @options[:token_file])
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,192 @@
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
+
12
+ TARGET_FILE = '~/.vmc_target'
13
+ UHURU_TARGET_FILE = '~/.vmc_uhuru_target'
14
+ TOKEN_FILE = '~/.vmc_token'
15
+ INSTANCES_FILE = '~/.vmc_instances'
16
+ ALIASES_FILE = '~/.vmc_aliases'
17
+ CLIENTS_FILE = '~/.vmc_clients'
18
+ MICRO_FILE = '~/.vmc_micro'
19
+
20
+ STOCK_CLIENTS = File.expand_path("../../../config/clients.yml", __FILE__)
21
+
22
+ class << self
23
+ attr_accessor :colorize
24
+ attr_accessor :output
25
+ attr_accessor :trace
26
+ attr_accessor :nozip
27
+
28
+ def target_url
29
+ return @target_url if @target_url
30
+ target_file = File.expand_path(TARGET_FILE)
31
+ if File.exists? target_file
32
+ @target_url = lock_and_read(target_file).strip
33
+ else
34
+ @target_url = DEFAULT_TARGET
35
+ end
36
+ @target_url = "http://#{@target_url}" unless /^https?/ =~ @target_url
37
+ @target_url = @target_url.gsub(/\/+$/, '')
38
+ @target_url
39
+ end
40
+
41
+ def uhuru_target
42
+ (all_uhuru_targets || {})[target_url.to_sym]
43
+ end
44
+
45
+ def all_uhuru_targets
46
+ uhuru_target_file = File.expand_path(UHURU_TARGET_FILE)
47
+ return nil unless File.exists? uhuru_target_file
48
+ contents = lock_and_read(uhuru_target_file).strip
49
+ JSON.parse(contents, :symbolize_names => true)
50
+ end
51
+
52
+ def base_of(url)
53
+ url.sub(/^[^\.]+\./, "")
54
+ end
55
+
56
+ def suggest_url
57
+ @suggest_url ||= base_of(target_url)
58
+ end
59
+
60
+ def store_target(target_host)
61
+ target_file = File.expand_path(TARGET_FILE)
62
+ lock_and_write(target_file, target_host)
63
+ end
64
+
65
+ def store_uhuru_target(host, uhuru_target)
66
+ uhuru_target_urls = all_uhuru_targets || {}
67
+ uhuru_target_urls[host.to_sym] = uhuru_target
68
+ uhuru_target_file = File.expand_path(UHURU_TARGET_FILE)
69
+ lock_and_write(uhuru_target_file, JSON.pretty_generate(uhuru_target_urls))
70
+ end
71
+
72
+ def all_tokens(token_file_path=nil)
73
+ token_file = File.expand_path(token_file_path || TOKEN_FILE)
74
+ return nil unless File.exists? token_file
75
+ contents = lock_and_read(token_file).strip
76
+ JSON.parse(contents)
77
+ end
78
+
79
+ alias :targets :all_tokens
80
+
81
+ def auth_token(token_file_path=nil)
82
+ return @token if @token
83
+ tokens = all_tokens(token_file_path)
84
+ @token = tokens[target_url] if tokens
85
+ end
86
+
87
+ def remove_token_file
88
+ FileUtils.rm_f(File.expand_path(TOKEN_FILE))
89
+ end
90
+
91
+ def store_token(token, token_file_path=nil)
92
+ tokens = all_tokens(token_file_path) || {}
93
+ tokens[target_url] = token
94
+ token_file = File.expand_path(token_file_path || TOKEN_FILE)
95
+ lock_and_write(token_file, tokens.to_json)
96
+ end
97
+
98
+ def instances
99
+ instances_file = File.expand_path(INSTANCES_FILE)
100
+ return nil unless File.exists? instances_file
101
+ contents = lock_and_read(instances_file).strip
102
+ JSON.parse(contents)
103
+ end
104
+
105
+ def store_instances(instances)
106
+ instances_file = File.expand_path(INSTANCES_FILE)
107
+ lock_and_write(instances_file, instances.to_json)
108
+ end
109
+
110
+ def aliases
111
+ aliases_file = File.expand_path(ALIASES_FILE)
112
+ # bacward compatible
113
+ unless File.exists? aliases_file
114
+ old_aliases_file = File.expand_path('~/.vmc-aliases')
115
+ FileUtils.mv(old_aliases_file, aliases_file) if File.exists? old_aliases_file
116
+ end
117
+ aliases = YAML.load_file(aliases_file) rescue {}
118
+ end
119
+
120
+ def store_aliases(aliases)
121
+ aliases_file = File.expand_path(ALIASES_FILE)
122
+ File.open(aliases_file, 'wb') {|f| f.write(aliases.to_yaml)}
123
+ end
124
+
125
+ def micro
126
+ micro_file = File.expand_path(MICRO_FILE)
127
+ return {} unless File.exists? micro_file
128
+ contents = lock_and_read(micro_file).strip
129
+ JSON.parse(contents)
130
+ end
131
+
132
+ def store_micro(micro)
133
+ micro_file = File.expand_path(MICRO_FILE)
134
+ lock_and_write(micro_file, micro.to_json)
135
+ end
136
+
137
+ def deep_merge(a, b)
138
+ merge = proc do |_, old, new|
139
+ if new.is_a?(Hash) and old.is_a?(Hash)
140
+ old.merge(new, &merge)
141
+ else
142
+ new
143
+ end
144
+ end
145
+
146
+ a.merge(b, &merge)
147
+ end
148
+
149
+ def clients
150
+ return @clients if @clients
151
+
152
+ stock = YAML.load_file(STOCK_CLIENTS)
153
+ clients = File.expand_path CLIENTS_FILE
154
+ if File.exists? clients
155
+ user = YAML.load_file(clients)
156
+ @clients = deep_merge(stock, user)
157
+ else
158
+ @clients = stock
159
+ end
160
+ end
161
+
162
+ def lock_and_read(file)
163
+ File.open(file, File::RDONLY) {|f|
164
+ if defined? JRUBY_VERSION
165
+ f.flock(File::LOCK_SH)
166
+ else
167
+ f.flock(File::LOCK_EX)
168
+ end
169
+ contents = f.read
170
+ f.flock(File::LOCK_UN)
171
+ contents
172
+ }
173
+ end
174
+
175
+ def lock_and_write(file, contents)
176
+ File.open(file, File::RDWR | File::CREAT, 0600) {|f|
177
+ f.flock(File::LOCK_EX)
178
+ f.rewind
179
+ f.puts contents
180
+ f.flush
181
+ f.truncate(f.pos)
182
+ f.flock(File::LOCK_UN)
183
+ }
184
+ end
185
+ end
186
+
187
+ def initialize(work_dir = Dir.pwd)
188
+ @work_dir = work_dir
189
+ end
190
+
191
+ end
192
+ end