vmcu 0.3.17

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.
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,84 @@
1
+
2
+ module VMC::Cli
3
+ module ServicesHelper
4
+ def display_system_services(services=nil)
5
+ services ||= client.services_info
6
+
7
+ display "\n============== System Services ==============\n\n"
8
+
9
+ return display "No system services available" if services.empty?
10
+
11
+ displayed_services = []
12
+ services.each do |service_type, value|
13
+ value.each do |vendor, version|
14
+ version.each do |version_str, service|
15
+ displayed_services << [ vendor, version_str, service[:description] ]
16
+ end
17
+ end
18
+ end
19
+ displayed_services.sort! { |a, b| a.first.to_s <=> b.first.to_s}
20
+
21
+ services_table = table do |t|
22
+ t.headings = 'Service', 'Version', 'Description'
23
+ displayed_services.each { |s| t << s }
24
+ end
25
+ display services_table
26
+ end
27
+
28
+ def display_provisioned_services(services=nil)
29
+ services ||= client.services
30
+ display "\n=========== Provisioned Services ============\n\n"
31
+ display_provisioned_services_table(services)
32
+ end
33
+
34
+ def display_provisioned_services_table(services)
35
+ return unless services && !services.empty?
36
+ services_table = table do |t|
37
+ t.headings = 'Name', 'Service'
38
+ services.each do |service|
39
+ t << [ service[:name], service[:vendor] ]
40
+ end
41
+ end
42
+ display services_table
43
+ end
44
+
45
+ def create_service_banner(service, name, display_name=false)
46
+ sn = " [#{name}]" if display_name
47
+ display "Creating Service#{sn}: ", false
48
+ client.create_service(service, name)
49
+ display 'OK'.green
50
+ end
51
+
52
+ def bind_service_banner(service, appname, check_restart=true)
53
+ display "Binding Service [#{service}]: ", false
54
+ client.bind_service(service, appname)
55
+ display 'OK'.green
56
+ check_app_for_restart(appname) if check_restart
57
+ end
58
+
59
+ def unbind_service_banner(service, appname, check_restart=true)
60
+ display "Unbinding Service [#{service}]: ", false
61
+ client.unbind_service(service, appname)
62
+ display 'OK'.green
63
+ check_app_for_restart(appname) if check_restart
64
+ end
65
+
66
+ def delete_service_banner(service)
67
+ display "Deleting service [#{service}]: ", false
68
+ client.delete_service(service)
69
+ display 'OK'.green
70
+ end
71
+
72
+ def random_service_name(service)
73
+ r = "%04x" % [rand(0x0100000)]
74
+ "#{service.to_s}-#{r}"
75
+ end
76
+
77
+ def check_app_for_restart(appname)
78
+ app = client.app_info(appname)
79
+ cmd = VMC::Cli::Command::Apps.new(@options)
80
+ cmd.restart(appname) if app[:state] == 'STARTED'
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,332 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'addressable/uri'
4
+
5
+ begin
6
+ require 'caldecott'
7
+ rescue LoadError
8
+ end
9
+
10
+ module VMC::Cli
11
+ module TunnelHelper
12
+ PORT_RANGE = 10
13
+
14
+ HELPER_APP = File.expand_path("../../../caldecott_helper", __FILE__)
15
+
16
+ # bump this AND the version info reported by HELPER_APP/server.rb
17
+ # this is to keep the helper in sync with any updates here
18
+ HELPER_VERSION = '0.0.4'
19
+
20
+ def tunnel_uniquename
21
+ random_service_name(tunnel_appname)
22
+ end
23
+
24
+ def tunnel_appname
25
+ "caldecott"
26
+ end
27
+
28
+ def tunnel_app_info
29
+ return @tun_app_info if @tunnel_app_info
30
+ begin
31
+ @tun_app_info = client.app_info(tunnel_appname)
32
+ rescue => e
33
+ @tun_app_info = nil
34
+ end
35
+ end
36
+
37
+ def tunnel_auth
38
+ tunnel_app_info[:env].each do |e|
39
+ name, val = e.split("=", 2)
40
+ return val if name == "CALDECOTT_AUTH"
41
+ end
42
+ nil
43
+ end
44
+
45
+ def tunnel_url
46
+ return @tunnel_url if @tunnel_url
47
+
48
+ tun_url = tunnel_app_info[:uris][0]
49
+
50
+ ["https", "http"].each do |scheme|
51
+ url = "#{scheme}://#{tun_url}"
52
+ begin
53
+ RestClient.get(url)
54
+
55
+ # https failed
56
+ rescue Errno::ECONNREFUSED
57
+
58
+ # we expect a 404 since this request isn't auth'd
59
+ rescue RestClient::ResourceNotFound
60
+ return @tunnel_url = url
61
+ end
62
+ end
63
+
64
+ err "Cannot determine URL for #{tun_url}"
65
+ end
66
+
67
+ def invalidate_tunnel_app_info
68
+ @tunnel_url = nil
69
+ @tunnel_app_info = nil
70
+ end
71
+
72
+ def tunnel_pushed?
73
+ not tunnel_app_info.nil?
74
+ end
75
+
76
+ def tunnel_healthy?(token)
77
+ return false unless tunnel_app_info[:state] == 'STARTED'
78
+
79
+ begin
80
+ response = RestClient.get(
81
+ "#{tunnel_url}/info",
82
+ "Auth-Token" => token
83
+ )
84
+
85
+ info = JSON.parse(response)
86
+ if info["version"] == HELPER_VERSION
87
+ true
88
+ else
89
+ stop_caldecott
90
+ false
91
+ end
92
+ rescue RestClient::Exception
93
+ stop_caldecott
94
+ false
95
+ end
96
+ end
97
+
98
+ def tunnel_bound?(service)
99
+ tunnel_app_info[:services].include?(service)
100
+ end
101
+
102
+ def tunnel_connection_info(type, service, token)
103
+ display "Getting tunnel connection info: ", false
104
+ response = nil
105
+ 10.times do
106
+ begin
107
+ response = RestClient.get(tunnel_url + "/" + VMC::Client.path("services", service), "Auth-Token" => token)
108
+ break
109
+ rescue RestClient::Exception
110
+ sleep 1
111
+ end
112
+
113
+ display ".", false
114
+ end
115
+
116
+ unless response
117
+ err "Expected remote tunnel to know about #{service}, but it doesn't"
118
+ end
119
+
120
+ display "OK".green
121
+
122
+ info = JSON.parse(response)
123
+ case type
124
+ when "rabbitmq"
125
+ uri = Addressable::URI.parse info["url"]
126
+ info["hostname"] = uri.host
127
+ info["port"] = uri.port
128
+ info["vhost"] = uri.path[1..-1]
129
+ info["user"] = uri.user
130
+ info["password"] = uri.password
131
+ info.delete "url"
132
+
133
+ # we use "db" as the "name" for mongo
134
+ # existing "name" is junk
135
+ when "mongodb"
136
+ info["name"] = info["db"]
137
+ info.delete "db"
138
+
139
+ # our "name" is irrelevant for redis
140
+ when "redis"
141
+ info.delete "name"
142
+ end
143
+
144
+ ['hostname', 'port', 'password'].each do |k|
145
+ err "Could not determine #{k} for #{service}" if info[k].nil?
146
+ end
147
+
148
+ info
149
+ end
150
+
151
+ def display_tunnel_connection_info(info)
152
+ display ''
153
+ display "Service connection info: "
154
+
155
+ to_show = [nil, nil, nil] # reserved for user, pass, db name
156
+ info.keys.each do |k|
157
+ case k
158
+ when "host", "hostname", "port", "node_id"
159
+ # skip
160
+ when "user", "username"
161
+ # prefer "username" over "user"
162
+ to_show[0] = k unless to_show[0] == "username"
163
+ when "password"
164
+ to_show[1] = k
165
+ when "name"
166
+ to_show[2] = k
167
+ else
168
+ to_show << k
169
+ end
170
+ end
171
+ to_show.compact!
172
+
173
+ align_len = to_show.collect(&:size).max + 1
174
+
175
+ to_show.each do |k|
176
+ # TODO: modify the server services rest call to have explicit knowledge
177
+ # about the items to return. It should return all of them if
178
+ # the service is unknown so that we don't have to do this weird
179
+ # filtering.
180
+ display " #{k.ljust align_len}: ", false
181
+ display "#{info[k]}".yellow
182
+ end
183
+ display ''
184
+ end
185
+
186
+ def start_tunnel(local_port, conn_info, auth)
187
+ @local_tunnel_thread = Thread.new do
188
+ Caldecott::Client.start({
189
+ :local_port => local_port,
190
+ :tun_url => tunnel_url,
191
+ :dst_host => conn_info['hostname'],
192
+ :dst_port => conn_info['port'],
193
+ :log_file => STDOUT,
194
+ :log_level => ENV["VMC_TUNNEL_DEBUG"] || "ERROR",
195
+ :auth_token => auth,
196
+ :quiet => true
197
+ })
198
+ end
199
+
200
+ at_exit { @local_tunnel_thread.kill }
201
+ end
202
+
203
+
204
+
205
+ def pick_tunnel_port(port)
206
+ original = port
207
+
208
+ PORT_RANGE.times do |n|
209
+ begin
210
+ TCPSocket.open('localhost', port)
211
+ port += 1
212
+ rescue
213
+ return port
214
+ end
215
+ end
216
+
217
+ grab_ephemeral_port
218
+ end
219
+
220
+ def grab_ephemeral_port
221
+ socket = TCPServer.new('0.0.0.0', 0)
222
+ socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
223
+ Socket.do_not_reverse_lookup = true
224
+ port = socket.addr[1]
225
+ socket.close
226
+ return port
227
+ end
228
+
229
+ def wait_for_tunnel_start(port)
230
+ 10.times do |n|
231
+ begin
232
+ client = TCPSocket.open('localhost', port)
233
+ display '' if n > 0
234
+ client.close
235
+ return true
236
+ rescue => e
237
+ display "Waiting for local tunnel to become available", false if n == 0
238
+ display '.', false
239
+ sleep 1
240
+ end
241
+ end
242
+ err "Could not connect to local tunnel."
243
+ end
244
+
245
+ def wait_for_tunnel_end
246
+ display "Open another shell to run command-line clients or"
247
+ display "use a UI tool to connect using the displayed information."
248
+ display "Press Ctrl-C to exit..."
249
+ @local_tunnel_thread.join
250
+ end
251
+
252
+ def resolve_symbols(str, info, local_port)
253
+ str.gsub(/\$\{\s*([^\}]+)\s*\}/) do
254
+ case $1
255
+ when "host"
256
+ # TODO: determine proper host
257
+ "localhost"
258
+ when "port"
259
+ local_port
260
+ when "user", "username"
261
+ info["username"]
262
+ else
263
+ info[$1] || ask($1)
264
+ end
265
+ end
266
+ end
267
+
268
+ def start_local_prog(clients, command, info, port)
269
+ client = clients[File.basename(command)]
270
+
271
+ cmdline = "#{command} "
272
+
273
+ case client
274
+ when Hash
275
+ cmdline << resolve_symbols(client["command"], info, port)
276
+ client["environment"].each do |e|
277
+ if e =~ /([^=]+)=(["']?)([^"']*)\2/
278
+ ENV[$1] = resolve_symbols($3, info, port)
279
+ else
280
+ err "Invalid environment variable: #{e}"
281
+ end
282
+ end
283
+ when String
284
+ cmdline << resolve_symbols(client, info, port)
285
+ else
286
+ err "Unknown client info: #{client.inspect}."
287
+ end
288
+
289
+ display "Launching '#{cmdline}'"
290
+ display ''
291
+
292
+ system(cmdline)
293
+ end
294
+
295
+ def push_caldecott(token)
296
+ client.create_app(
297
+ tunnel_appname,
298
+ { :name => tunnel_appname,
299
+ :staging => {:framework => "sinatra"},
300
+ :uris => ["#{tunnel_uniquename}.#{target_base}"],
301
+ :instances => 1,
302
+ :resources => {:memory => 64},
303
+ :env => ["CALDECOTT_AUTH=#{token}"]
304
+ }
305
+ )
306
+
307
+ apps_cmd.send(:upload_app_bits, tunnel_appname, HELPER_APP)
308
+
309
+ invalidate_tunnel_app_info
310
+ end
311
+
312
+ def stop_caldecott
313
+ apps_cmd.stop(tunnel_appname)
314
+
315
+ invalidate_tunnel_app_info
316
+ end
317
+
318
+ def start_caldecott
319
+ apps_cmd.start(tunnel_appname)
320
+
321
+ invalidate_tunnel_app_info
322
+ end
323
+
324
+ private
325
+
326
+ def apps_cmd
327
+ a = Command::Apps.new(@options)
328
+ a.client client
329
+ a
330
+ end
331
+ end
332
+ end
@@ -0,0 +1,118 @@
1
+ class VMC::Cli::Runner
2
+
3
+ def basic_usage
4
+ "Usage: vmcu [options] command [<args>] [command_options]\n" +
5
+ "Try 'vmcu help [command]' or 'vmcu help options' for more information."
6
+ end
7
+
8
+ def display_usage
9
+ if @usage
10
+ say @usage_error if @usage_error
11
+ say "Usage: #{@usage}"
12
+ return
13
+ elsif @verb_usage
14
+ say @verb_usage
15
+ return
16
+ end
17
+ say command_usage
18
+ end
19
+
20
+ def command_usage
21
+ <<-USAGE
22
+
23
+ #{basic_usage}
24
+
25
+ Currently available vmcu commands are:
26
+
27
+ Getting Started
28
+ target [url] Reports current target or sets a new target
29
+ login [email] [--email, --passwd] Login
30
+ login [token] [--token UHURU_TOKEN] Login for Uhuru AppCloud
31
+ cloud-team [name or id] [--realm REALM] Set cloud team for Uhuru AppCloud. Sys admins can proxy realms
32
+ import-uhuru Import targets from Uhuru Cloud Foundry Manager
33
+ info System and account information
34
+
35
+ Applications
36
+ apps List deployed applications
37
+
38
+ Application Creation
39
+ push [appname] Create, push, map, and start a new application
40
+ push [appname] --path Push application from specified path
41
+ push [appname] --url Set the url for the application
42
+ push [appname] --instances <N> Set the expected number <N> of instances
43
+ push [appname] --mem M Set the memory reservation for the application
44
+ push [appname] --runtime RUNTIME Set the runtime to use for the application
45
+ push [appname] --debug [MODE] Push application and start in a debug mode
46
+ push [appname] --no-start Do not auto-start the application
47
+
48
+ Application Operations
49
+ start <appname> [--debug [MODE]] Start the application
50
+ stop <appname> Stop the application
51
+ restart <appname> [--debug [MODE]] Restart the application
52
+ delete <appname> Delete the application
53
+
54
+ Application Updates
55
+ update <appname> [--path,--debug [MODE]] Update the application bits
56
+ mem <appname> [memsize] Update the memory reservation for an application
57
+ map <appname> <url> Register the application to the url
58
+ unmap <appname> <url> Unregister the application from the url
59
+ instances <appname> <num|delta> Scale the application instances up or down
60
+
61
+ Application Information
62
+ crashes <appname> List recent application crashes
63
+ crashlogs <appname> Display log information for crashed applications
64
+ logs <appname> [--all] Display log information for the application
65
+ files <appname> [path] [--all] Display directory listing or file download for [path]
66
+ stats <appname> Display resource usage for the application
67
+ instances <appname> List application instances
68
+
69
+ Application Environment
70
+ env <appname> List application environment variables
71
+ env-add <appname> <variable[=]value> Add an environment variable to an application
72
+ env-del <appname> <variable> Delete an environment variable to an application
73
+
74
+ Services
75
+ services Lists of services available and provisioned
76
+ create-service <service> [--name,--bind] Create a provisioned service
77
+ create-service <service> <name> Create a provisioned service and assign it <name>
78
+ create-service <service> <name> <app> Create a provisioned service and assign it <name>, and bind to <app>
79
+ delete-service [servicename] Delete a provisioned service
80
+ bind-service <servicename> <appname> Bind a service to an application
81
+ unbind-service <servicename> <appname> Unbind service from the application
82
+ clone-services <src-app> <dest-app> Clone service bindings from <src-app> application to <dest-app>
83
+ tunnel <servicename> [--port] Create a local tunnel to a service
84
+ tunnel <servicename> <clientcmd> Create a local tunnel to a service and start a local client
85
+
86
+ Administration
87
+ user Display user account information
88
+ passwd Change the password for the current user
89
+ logout Logs current user out of the target system
90
+ add-user [--email, --passwd] Register a new user (requires admin privileges)
91
+ delete-user <user> Delete a user and all apps and services (requires admin privileges)
92
+
93
+ System
94
+ runtimes Display the supported runtimes of the target system
95
+ frameworks Display the recognized frameworks of the target system
96
+
97
+ Micro Cloud Foundry
98
+ micro status Display Micro Cloud Foundry VM status
99
+ micro offline Configure Micro Cloud Foundry VM for offline mode
100
+ micro online Configure Micro Cloud Foundry VM for online mode
101
+ [--vmx file] Path to micro.vmx
102
+ [--vmrun executable] Path to vmrun executable
103
+ [--password cleartext] Cleartext password for guest VM vcap user
104
+ [--save] Save cleartext password in ~/.vmc_micro
105
+
106
+ Misc
107
+ aliases List aliases
108
+ alias <alias[=]command> Create an alias for a command
109
+ unalias <alias> Remove an alias
110
+ targets List known targets and associated authorization tokens
111
+
112
+ Help
113
+ help [command] Get general help or help on a specific command
114
+ help options Get help on available options
115
+ USAGE
116
+
117
+ end
118
+ end