olympe 0.1

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