tac 0.6.0

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 +102 -0
  3. data/Rakefile +101 -0
  4. data/bin/tac +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/commands/admin.rb +108 -0
  13. data/lib/cli/commands/apps.rb +1133 -0
  14. data/lib/cli/commands/base.rb +232 -0
  15. data/lib/cli/commands/manifest.rb +56 -0
  16. data/lib/cli/commands/micro.rb +115 -0
  17. data/lib/cli/commands/misc.rb +129 -0
  18. data/lib/cli/commands/services.rb +242 -0
  19. data/lib/cli/commands/user.rb +65 -0
  20. data/lib/cli/config.rb +173 -0
  21. data/lib/cli/console_helper.rb +160 -0
  22. data/lib/cli/core_ext.rb +122 -0
  23. data/lib/cli/errors.rb +19 -0
  24. data/lib/cli/frameworks.rb +276 -0
  25. data/lib/cli/manifest_helper.rb +341 -0
  26. data/lib/cli/runner.rb +547 -0
  27. data/lib/cli/services_helper.rb +92 -0
  28. data/lib/cli/tunnel_helper.rb +332 -0
  29. data/lib/cli/usage.rb +115 -0
  30. data/lib/cli/version.rb +7 -0
  31. data/lib/cli/zip_util.rb +77 -0
  32. data/lib/cli.rb +47 -0
  33. data/lib/vmc/client.rb +573 -0
  34. data/lib/vmc/const.rb +24 -0
  35. data/lib/vmc/micro/switcher/base.rb +97 -0
  36. data/lib/vmc/micro/switcher/darwin.rb +19 -0
  37. data/lib/vmc/micro/switcher/dummy.rb +15 -0
  38. data/lib/vmc/micro/switcher/linux.rb +16 -0
  39. data/lib/vmc/micro/switcher/windows.rb +31 -0
  40. data/lib/vmc/micro/vmrun.rb +158 -0
  41. data/lib/vmc/micro.rb +56 -0
  42. data/lib/vmc.rb +3 -0
  43. metadata +295 -0
data/lib/vmc/client.rb ADDED
@@ -0,0 +1,573 @@
1
+ # VMC client
2
+ #
3
+ # Example:
4
+ #
5
+ # require 'vmc'
6
+ # client = VMC::Client.new('api.vcap.me')
7
+ # client.login(:user, :pass)
8
+ # client.create('myapplication', manifest)
9
+ # client.create_service('redis', 'my_redis_service', opts);
10
+ #
11
+
12
+ require 'rubygems'
13
+ require 'json/pure'
14
+ require 'open-uri'
15
+
16
+ require File.expand_path('../const', __FILE__)
17
+
18
+ class VMC::Client
19
+
20
+ def self.version
21
+ VMC::VERSION
22
+ end
23
+
24
+ attr_reader :target, :host, :user, :proxy, :auth_token
25
+ attr_accessor :trace
26
+
27
+ # Error codes
28
+ VMC_HTTP_ERROR_CODES = [ 400, 500 ]
29
+
30
+ # Errors
31
+ class BadTarget < RuntimeError; end
32
+ class AuthError < RuntimeError; end
33
+ class TargetError < RuntimeError; end
34
+ class NotFound < RuntimeError; end
35
+ class BadResponse < RuntimeError; end
36
+ class HTTPException < RuntimeError; end
37
+
38
+ # Initialize new client to the target_uri with optional auth_token
39
+ def initialize(target_url=VMC::DEFAULT_TARGET, auth_token=nil)
40
+ target_url = "http://#{target_url}" unless /^https?/ =~ target_url
41
+ target_url = target_url.gsub(/\/+$/, '')
42
+ @target = target_url
43
+ @auth_token = auth_token
44
+ end
45
+
46
+ ######################################################
47
+ # Target info
48
+ ######################################################
49
+
50
+ # Retrieves information on the target cloud, and optionally the logged in user
51
+ def info
52
+ # TODO: Should merge for new version IMO, general, services, user_account
53
+ json_get(VMC::INFO_PATH)
54
+ end
55
+
56
+ def quota_info
57
+ # TODO: Should merge for new version IMO, general, services, user_account
58
+ json_get(VMC::QUOTA_INFO_PATH)
59
+ end
60
+
61
+ def raw_info
62
+ http_get(VMC::INFO_PATH)
63
+ end
64
+
65
+ # Global listing of services that are available on the target system
66
+ def services_info
67
+ check_login_status
68
+ json_get(path(VMC::GLOBAL_SERVICES_PATH))
69
+ end
70
+
71
+ def runtimes_info
72
+ json_get(path(VMC::GLOBAL_RUNTIMES_PATH))
73
+ end
74
+
75
+ ######################################################
76
+ # Apps
77
+ ######################################################
78
+
79
+ def apps
80
+ check_login_status
81
+ json_get(VMC::APPS_PATH)
82
+ end
83
+
84
+ def create_app(name, manifest={})
85
+ check_login_status
86
+ app = manifest.dup
87
+ app[:name] = name
88
+ app[:instances] ||= 1
89
+ json_post(VMC::APPS_PATH, app)
90
+ end
91
+
92
+ def update_app(name, manifest)
93
+ check_login_status
94
+ json_put(path(VMC::APPS_PATH, name), manifest)
95
+ end
96
+
97
+ def upload_app(name, zipfile, resource_manifest=nil)
98
+ #FIXME, manifest should be allowed to be null, here for compatability with old cc's
99
+ resource_manifest ||= []
100
+ check_login_status
101
+ upload_data = {:_method => 'put'}
102
+ if zipfile
103
+ if zipfile.is_a? File
104
+ file = zipfile
105
+ else
106
+ file = File.new(zipfile, 'rb')
107
+ end
108
+ upload_data[:application] = file
109
+ end
110
+ upload_data[:resources] = resource_manifest.to_json if resource_manifest
111
+ http_post(path(VMC::APPS_PATH, name, "application"), upload_data)
112
+ rescue RestClient::ServerBrokeConnection
113
+ retry
114
+ end
115
+
116
+ def delete_app(name)
117
+ check_login_status
118
+ http_delete(path(VMC::APPS_PATH, name))
119
+ end
120
+
121
+ def app_info(name)
122
+ check_login_status
123
+ json_get(path(VMC::APPS_PATH, name))
124
+ end
125
+
126
+ def app_update_info(name)
127
+ check_login_status
128
+ json_get(path(VMC::APPS_PATH, name, "update"))
129
+ end
130
+
131
+ def app_stats(name)
132
+ check_login_status
133
+ stats_raw = json_get(path(VMC::APPS_PATH, name, "stats"))
134
+ stats = []
135
+ stats_raw.each_pair do |k, entry|
136
+ # Skip entries with no stats
137
+ next unless entry[:stats]
138
+ entry[:instance] = k.to_s.to_i
139
+ entry[:state] = entry[:state].to_sym if entry[:state]
140
+ stats << entry
141
+ end
142
+ stats.sort { |a,b| a[:instance] - b[:instance] }
143
+ end
144
+
145
+ def app_instances(name)
146
+ check_login_status
147
+ json_get(path(VMC::APPS_PATH, name, "instances"))
148
+ end
149
+
150
+ def app_crashes(name)
151
+ check_login_status
152
+ json_get(path(VMC::APPS_PATH, name, "crashes"))
153
+ end
154
+
155
+ # List the directory or download the actual file indicated by
156
+ # the path.
157
+ def app_files(name, path, instance='0')
158
+ check_login_status
159
+ path = path.gsub('//', '/')
160
+ url = path(VMC::APPS_PATH, name, "instances", instance, "files", path)
161
+ _, body, headers = http_get(url)
162
+ body
163
+ end
164
+
165
+ ######################################################
166
+ # Services
167
+ ######################################################
168
+
169
+ # listing of services that are available in the system
170
+ def services
171
+ check_login_status
172
+ json_get(VMC::SERVICES_PATH)
173
+ end
174
+
175
+ =begin
176
+ def create_service(service, name)
177
+ check_login_status
178
+ services = services_info
179
+ services ||= []
180
+ service_hash = nil
181
+
182
+ service = service.to_s
183
+
184
+ # FIXME!
185
+ services.each do |service_type, value|
186
+ value.each do |vendor, version|
187
+ version.each do |version_str, service_descr|
188
+ if service == service_descr[:vendor]
189
+ service_hash = {
190
+ :type => service_descr[:type], :tier => 'free',
191
+ :vendor => service, :version => version_str
192
+ }
193
+ break
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ raise TargetError, "Service [#{service}] is not a valid service choice" unless service_hash
200
+ service_hash[:name] = name
201
+ json_post(path(VMC::SERVICES_PATH), service_hash)
202
+ end
203
+ =end
204
+
205
+ def create_service(service, name, dbflag , dbjndi, dbusername, dbpasswd, dburl, dbsize)
206
+
207
+ check_login_status
208
+ services = services_info
209
+ services ||= []
210
+ service_hash = nil
211
+
212
+ service = service.to_s
213
+
214
+ # FIXME!
215
+ services.each do |service_type, value|
216
+ value.each do |vendor, version|
217
+ version.each do |version_str, service_descr|
218
+ if service == service_descr[:vendor]
219
+ service_hash = {
220
+ :type => service_descr[:type], :tier => 'free',
221
+ :vendor => service, :version => version_str
222
+ }
223
+ break
224
+ end
225
+ end
226
+ end
227
+ end
228
+
229
+ raise TargetError, "Service [#{service}] is not a valid service choice" unless service_hash
230
+ service_hash[:name] = name
231
+ service_hash[:db_flag] = dbflag
232
+ service_hash[:db_jndi] = dbjndi
233
+ service_hash[:db_uname] = dbusername
234
+ service_hash[:db_passwd] = dbpasswd
235
+ service_hash[:db_url] = dburl
236
+ service_hash[:max_db_size] = dbsize
237
+ #puts service_hash
238
+ json_post(path(VMC::SERVICES_PATH), service_hash)
239
+ end
240
+
241
+ def delete_service(name)
242
+ check_login_status
243
+ svcs = services || []
244
+ names = svcs.collect { |s| s[:name] }
245
+ raise TargetError, "Service [#{name}] not a valid service" unless names.include? name
246
+ http_delete(path(VMC::SERVICES_PATH, name))
247
+ end
248
+
249
+ def check_quota(name)
250
+ check_login_status
251
+ svcs = services || []
252
+ type = nil;
253
+ max_db_size = 0
254
+ names = svcs.collect { |s|
255
+ if name == s[:name]
256
+ max_db_size = s[:max_db_size]
257
+ type = s[:vendor]
258
+ end
259
+ s[:name]
260
+ }
261
+ raise TargetError, "Temporary does not support" unless "mysql" == type
262
+ raise TargetError, "Service [#{name}] not a valid service" unless names.include? name
263
+ max_db_size
264
+ end
265
+
266
+ def quota_service(name, dbsize, update_flag)
267
+ service_hash ={
268
+ :max_db_size => dbsize
269
+ }
270
+ service_hash[:update_flag] = update_flag
271
+ #puts service_hash
272
+ http_put(path(VMC::SERVICES_PATH, name), service_hash)
273
+ end
274
+
275
+ def bind_service(service, appname)
276
+ check_login_status
277
+ app = app_info(appname)
278
+ services = app[:services] || []
279
+ app[:services] = services << service
280
+ update_app(appname, app)
281
+ end
282
+
283
+ def unbind_service(service, appname)
284
+ check_login_status
285
+ app = app_info(appname)
286
+ services = app[:services] || []
287
+ services.delete(service)
288
+ app[:services] = services
289
+ update_app(appname, app)
290
+ end
291
+
292
+ ######################################################
293
+ # Resources
294
+ ######################################################
295
+
296
+ # Send in a resources manifest array to the system to have
297
+ # it check what is needed to actually send. Returns array
298
+ # indicating what is needed. This returned manifest should be
299
+ # sent in with the upload if resources were removed.
300
+ # E.g. [{:sha1 => xxx, :size => xxx, :fn => filename}]
301
+ def check_resources(resources)
302
+ check_login_status
303
+ status, body, headers = json_post(VMC::RESOURCES_PATH, resources)
304
+ json_parse(body)
305
+ end
306
+
307
+ ######################################################
308
+ # Validation Helpers
309
+ ######################################################
310
+
311
+ # Checks that the target is valid
312
+ def target_valid?
313
+ return false unless descr = info
314
+ return false unless descr[:name]
315
+ return false unless descr[:build]
316
+ return false unless descr[:version]
317
+ return false unless descr[:support]
318
+ true
319
+ rescue
320
+ false
321
+ end
322
+
323
+ # Checks that the auth_token is valid
324
+ def logged_in?
325
+ descr = info
326
+ if descr
327
+ return false unless descr[:user]
328
+ return false unless descr[:usage]
329
+ @user = descr[:user]
330
+ true
331
+ end
332
+ end
333
+
334
+ ######################################################
335
+ # User login/password
336
+ ######################################################
337
+
338
+ # login and return an auth_token
339
+ # Auth token can be retained and used in creating
340
+ # new clients, avoiding login.
341
+ def login(user, password)
342
+ status, body, headers = json_post(path(VMC::USERS_PATH, user, "tokens"), {:password => password})
343
+ response_info = json_parse(body)
344
+ if response_info
345
+ @user = user
346
+ @auth_token = response_info[:token]
347
+ end
348
+ end
349
+
350
+ # sets the password for the current logged user
351
+ def change_password(new_password)
352
+ check_login_status
353
+ user_info = json_get(path(VMC::USERS_PATH, @user))
354
+ #puts "changepasswd"
355
+ #puts user_info
356
+ if user_info
357
+ user_info[:password] = new_password
358
+ # puts path(VMC::USERS_PATH, @user)
359
+ json_put(path(VMC::USERS_PATH, @user), user_info)
360
+ end
361
+ end
362
+
363
+ def get_user_info(email)
364
+ check_login_status
365
+ user_info = json_get(path(VMC::USERSQUOTA_PATH, email))
366
+ end
367
+
368
+ # sets the password for the current logged user
369
+ def change_quota(mem, apps, uris, services, user_info, email)
370
+ check_login_status
371
+ #user_info = json_get(path(VMC::USERS_PATH, @user))
372
+ #if user_info
373
+ user_info[:mem] = mem
374
+ user_info[:app] = apps
375
+ user_info[:uri] = uris
376
+ user_info[:service] = services
377
+ user_info[:email] = email
378
+ json_put(path(VMC::USERSQUOTA_PATH, user_info), user_info)
379
+ #end
380
+ end
381
+
382
+ ######################################################
383
+ # System administration
384
+ ######################################################
385
+
386
+
387
+ def proxy=(proxy)
388
+ @proxy = proxy
389
+ end
390
+
391
+ def proxy_for(proxy)
392
+ @proxy = proxy
393
+ end
394
+
395
+ def users
396
+ check_login_status
397
+ json_get(VMC::USERS_PATH)
398
+ end
399
+
400
+ # def add_user(user_email, password, app)
401
+ # json_post(VMC::USERS_PATH, { :email => user_email, :password => password, :app => app})
402
+ #end
403
+
404
+ def add_user(user_email, password)
405
+ json_post(VMC::USERS_PATH, { :email => user_email, :password => password })
406
+ end
407
+
408
+ def delete_user(user_email)
409
+ check_login_status
410
+ http_delete(path(VMC::USERS_PATH, user_email))
411
+ end
412
+
413
+ ######################################################
414
+
415
+ def self.path(*path)
416
+ path.flatten.collect { |x|
417
+ URI.encode x.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
418
+ }.join("/")
419
+ end
420
+
421
+ private
422
+
423
+ def path(*args, &blk)
424
+ self.class.path(*args, &blk)
425
+ end
426
+
427
+ def json_get(url)
428
+ status, body, headers = http_get(url, 'application/json')
429
+ json_parse(body)
430
+ rescue JSON::ParserError
431
+ raise BadResponse, "Can't parse response into JSON", body
432
+ end
433
+
434
+ def json_post(url, payload)
435
+ http_post(url, payload.to_json, 'application/json')
436
+ end
437
+
438
+ def json_put(url, payload)
439
+ #puts url
440
+ http_put(url, payload.to_json, 'application/json')
441
+ end
442
+
443
+ def json_parse(str)
444
+ if str
445
+ JSON.parse(str, :symbolize_names => true)
446
+ end
447
+ end
448
+
449
+ require 'rest_client'
450
+
451
+ # HTTP helpers
452
+
453
+ def http_get(path, content_type=nil)
454
+ request(:get, path, content_type)
455
+ end
456
+
457
+ def http_post(path, body, content_type=nil)
458
+ #puts 4
459
+ request(:post, path, content_type, body)
460
+ end
461
+
462
+ def http_put(path, body, content_type=nil)
463
+ request(:put, path, content_type, body)
464
+ end
465
+
466
+ def http_delete(path)
467
+ request(:delete, path)
468
+ end
469
+
470
+ def request(method, path, content_type = nil, payload = nil, headers = {})
471
+
472
+ #puts 5
473
+ #puts path
474
+ headers = headers.dup
475
+ headers['AUTHORIZATION'] = @auth_token if @auth_token
476
+ headers['PROXY-USER'] = @proxy if @proxy
477
+
478
+ if content_type
479
+ headers['Content-Type'] = content_type
480
+ headers['Accept'] = content_type
481
+ end
482
+
483
+ req = {
484
+ :method => method, :url => "#{@target}/#{path}",
485
+ :payload => payload, :headers => headers, :multipart => true
486
+ }
487
+ #puts req
488
+ status, body, response_headers = perform_http_request(req)
489
+
490
+ if request_failed?(status)
491
+ # FIXME, old cc returned 400 on not found for file access
492
+ err = (status == 404 || status == 400) ? NotFound : TargetError
493
+ raise err, parse_error_message(status, body)
494
+ else
495
+ return status, body, response_headers
496
+ end
497
+ rescue URI::Error, SocketError, Errno::ECONNREFUSED => e
498
+ raise BadTarget, "Cannot access target (%s)" % [ e.message ]
499
+ end
500
+
501
+ def request_failed?(status)
502
+ VMC_HTTP_ERROR_CODES.detect{|error_code| status >= error_code}
503
+ end
504
+
505
+ def perform_http_request(req)
506
+ proxy_uri = URI.parse(req[:url]).find_proxy()
507
+ RestClient.proxy = proxy_uri.to_s if proxy_uri
508
+
509
+ # Setup tracing if needed
510
+ unless trace.nil?
511
+ req[:headers]['X-VCAP-Trace'] = (trace == true ? '22' : trace)
512
+ end
513
+
514
+ result = nil
515
+ RestClient::Request.execute(req) do |response, request|
516
+ result = [ response.code, response.body, response.headers ]
517
+ unless trace.nil?
518
+ puts '>>>'
519
+ puts "PROXY: #{RestClient.proxy}" if RestClient.proxy
520
+ puts "REQUEST: #{req[:method]} #{req[:url]}"
521
+ puts "RESPONSE_HEADERS:"
522
+ response.headers.each do |key, value|
523
+ puts " #{key} : #{value}"
524
+ end
525
+ puts "REQUEST_BODY: #{req[:payload]}" if req[:payload]
526
+ puts "RESPONSE: [#{response.code}]"
527
+ begin
528
+ puts JSON.pretty_generate(JSON.parse(response.body))
529
+ rescue
530
+ puts "#{response.body}"
531
+ end
532
+ puts '<<<'
533
+ end
534
+ end
535
+ result
536
+ rescue Net::HTTPBadResponse => e
537
+ raise BadTarget "Received bad HTTP response from target: #{e}"
538
+ rescue SystemCallError, RestClient::Exception => e
539
+ raise HTTPException, "HTTP exception: #{e.class}:#{e}"
540
+ end
541
+
542
+ def truncate(str, limit = 30)
543
+ etc = '...'
544
+ stripped = str.strip[0..limit]
545
+ if stripped.length > limit
546
+ stripped + etc
547
+ else
548
+ stripped
549
+ end
550
+ end
551
+
552
+ def parse_error_message(status, body)
553
+ parsed_body = json_parse(body.to_s)
554
+ if parsed_body && parsed_body[:code] && parsed_body[:description]
555
+ desc = parsed_body[:description].gsub("\"","'")
556
+ "Error #{parsed_body[:code]}: #{desc}"
557
+ else
558
+ "Error (HTTP #{status}): #{body}"
559
+ end
560
+ rescue JSON::ParserError
561
+ if body.nil? || body.empty?
562
+ "Error (#{status}): No Response Received"
563
+ else
564
+ body_out = trace ? body : truncate(body)
565
+ "Error (JSON #{status}): #{body_out}"
566
+ end
567
+ end
568
+
569
+ def check_login_status
570
+ raise AuthError unless @user || logged_in?
571
+ end
572
+
573
+ end
data/lib/vmc/const.rb ADDED
@@ -0,0 +1,24 @@
1
+ module VMC
2
+
3
+ # This is the internal VMC version number, and is not necessarily
4
+ # the same as the RubyGem version (VMC::Cli::VERSION).
5
+ VERSION = '0.3.2'
6
+
7
+ # Targets
8
+ DEFAULT_TARGET = 'https://api.cloudfoundry.com'
9
+ DEFAULT_LOCAL_TARGET = 'http://api.vcap.me'
10
+
11
+ # General Paths
12
+ INFO_PATH = 'info'
13
+ QUOTA_INFO_PATH = 'quotaInfo'
14
+ GLOBAL_SERVICES_PATH = ['info', 'services']
15
+ GLOBAL_RUNTIMES_PATH = ['info', 'runtimes']
16
+ RESOURCES_PATH = 'resources'
17
+
18
+ # User specific paths
19
+ APPS_PATH = 'apps'
20
+ SERVICES_PATH = 'services'
21
+ USERS_PATH = 'users'
22
+ USERSQUOTA_PATH = 'usersQuota'
23
+
24
+ end
@@ -0,0 +1,97 @@
1
+ require 'interact'
2
+
3
+ module VMC::Micro::Switcher
4
+ class Base
5
+ include Interactive
6
+
7
+ def initialize(config)
8
+ @config = config
9
+
10
+ @vmrun = VMC::Micro::VMrun.new(config)
11
+ unless @vmrun.running?
12
+ if ask("Micro Cloud Foundry VM is not running. Do you want to start it?", :choices => ['y', 'n']) == 'y'
13
+ display "Starting Micro Cloud Foundry VM: ", false
14
+ @vmrun.start
15
+ say "done".green
16
+ else
17
+ err "Micro Cloud Foundry VM needs to be running."
18
+ end
19
+ end
20
+
21
+ err "Micro Cloud Foundry VM initial setup needs to be completed before using 'vmc micro'" unless @vmrun.ready?
22
+ end
23
+
24
+ def offline
25
+ unless @vmrun.offline?
26
+ # save online connection type so we can restore it later
27
+ @config['online_connection_type'] = @vmrun.connection_type
28
+
29
+ if (@config['online_connection_type'] != 'nat')
30
+ if ask("Reconfigure Micro Cloud Foundry VM network to nat mode and reboot?", :choices => ['y', 'n']) == 'y'
31
+ display "Rebooting Micro Cloud Foundry VM: ", false
32
+ @vmrun.connection_type = 'nat'
33
+ @vmrun.reset
34
+ say "done".green
35
+ else
36
+ err "Aborted"
37
+ end
38
+ end
39
+
40
+ display "Setting Micro Cloud Foundry VM to offline mode: ", false
41
+ @vmrun.offline!
42
+ say "done".green
43
+ display "Setting host DNS server: ", false
44
+
45
+ @config['domain'] = @vmrun.domain
46
+ @config['ip'] = @vmrun.ip
47
+ set_nameserver(@config['domain'], @config['ip'])
48
+ say "done".green
49
+ else
50
+ say "Micro Cloud Foundry VM already in offline mode".yellow
51
+ end
52
+ end
53
+
54
+ def online
55
+ if @vmrun.offline?
56
+ current_connection_type = @vmrun.connection_type
57
+ @config['online_connection_type'] ||= current_connection_type
58
+
59
+ if (@config['online_connection_type'] != current_connection_type)
60
+ # TODO handle missing connection type in saved config
61
+ question = "Reconfigure Micro Cloud Foundry VM network to #{@config['online_connection_type']} mode and reboot?"
62
+ if ask(question, :choices => ['y', 'n']) == 'y'
63
+ display "Rebooting Micro Cloud Foundry VM: ", false
64
+ @vmrun.connection_type = @config['online_connection_type']
65
+ @vmrun.reset
66
+ say "done".green
67
+ else
68
+ err "Aborted"
69
+ end
70
+ end
71
+
72
+ display "Unsetting host DNS server: ", false
73
+ # TODO handle missing domain and ip in saved config (look at the VM)
74
+ @config['domain'] ||= @vmrun.domain
75
+ @config['ip'] ||= @vmrun.ip
76
+ unset_nameserver(@config['domain'], @config['ip'])
77
+ say "done".green
78
+
79
+ display "Setting Micro Cloud Foundry VM to online mode: ", false
80
+ @vmrun.online!
81
+ say "done".green
82
+ else
83
+ say "Micro Cloud Foundry already in online mode".yellow
84
+ end
85
+ end
86
+
87
+ def status
88
+ mode = @vmrun.offline? ? 'offline' : 'online'
89
+ say "Micro Cloud Foundry VM currently in #{mode.green} mode"
90
+ # should the VMX path be unescaped?
91
+ say "VMX Path: #{@vmrun.vmx}"
92
+ say "Domain: #{@vmrun.domain.green}"
93
+ say "IP Address: #{@vmrun.ip.green}"
94
+ end
95
+ end
96
+
97
+ end
@@ -0,0 +1,19 @@
1
+ module VMC::Micro::Switcher
2
+
3
+ class Darwin < Base
4
+ def adminrun(command)
5
+ VMC::Micro.run_command("osascript", "-e 'do shell script \"#{command}\" with administrator privileges'")
6
+ end
7
+
8
+ def set_nameserver(domain, ip)
9
+ File.open("/tmp/#{domain}", 'w') { |file| file.write("nameserver #{ip}") }
10
+ adminrun("mkdir -p /etc/resolver;mv /tmp/#{domain} /etc/resolver/")
11
+ end
12
+
13
+ def unset_nameserver(domain, ip)
14
+ err "domain missing" unless domain
15
+ adminrun("rm -f /etc/resolver/#{domain}")
16
+ end
17
+ end
18
+
19
+ end