olympe 0.1

Sign up to get free protection for your applications and to get access to all the features.
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