tac 0.6.0

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 +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