vmc_knife 0.0.01

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.
@@ -0,0 +1,748 @@
1
+ require 'vmc/client'
2
+ require 'json'
3
+ require 'yaml'
4
+
5
+ module VMC
6
+ module KNIFE
7
+
8
+ # Read/Write the JSON for a recipe.
9
+ # Does not map the actual JSON into a new ruby object.
10
+ class Root
11
+ attr_accessor :wrapped
12
+ def initialize(data)
13
+ if data.kind_of? Hash
14
+ @wrapped = data
15
+ elsif data.kind_of? String
16
+ @wrapped = VMC::KNIFE::JSON_EXPANDER.expand_json data
17
+ elsif data.kind_of? Root
18
+ @wrapped = data.wrapped
19
+ else
20
+ raise "Unexpected data #{data}"
21
+ end
22
+ end
23
+ def sub_domain()
24
+ @wrapped['sub_domain']
25
+ end
26
+ def target()
27
+ @wrapped['target']
28
+ end
29
+ def user()
30
+ @wrapped['user']
31
+ end
32
+ def target()
33
+ @wrapped['target']
34
+ end
35
+ def recipes(regexp=nil)
36
+ regexp||=/.*/
37
+ res = Array.new
38
+ @wrapped['recipes'].each do |recipe|
39
+ res << Recipe.new(self, recipe) if (regexp =~ recipe['name'])
40
+ end
41
+ res
42
+ end
43
+ def recipe(name)
44
+ res = @wrapped['recipes'].select {|v| v['name'] == name}
45
+ Recipe.new self, res.first unless res.empty?
46
+ end
47
+ def first_recipe(name)
48
+ Recipe.new self, @wrapped['recipes'][0] unless @wrapped['recipes'].empty?
49
+ end
50
+ def to_json()
51
+ @wrapped.to_json
52
+ end
53
+
54
+ end
55
+
56
+ class Recipe
57
+ attr_accessor :wrapped, :root
58
+ # root: Root
59
+ # data: The recipe's data. not the root of the json.
60
+ def initialize(root,data)
61
+ @wrapped = data
62
+ @root = root
63
+ end
64
+ #An application.
65
+ def application(name)
66
+ Application.new @root, @wrapped['applications'][name], name
67
+ end
68
+ def applications(regexp=nil)
69
+ regexp||=/.*/
70
+ res = Array.new
71
+ @wrapped['applications'].each_pair do |name,application|
72
+ res << Application.new(@root, application, name) if regexp =~ name
73
+ end
74
+ res
75
+ end
76
+
77
+ #A dataservice.
78
+ def data_service(name)
79
+ DataService.new @root, @wrapped['data_services'][name], name
80
+ end
81
+ def data_services(regexp=nil)
82
+ regexp||=/.*/
83
+ res = Array.new
84
+ @wrapped['data_services'].each_pair do |name,service|
85
+ res << DataService.new(@root, service, name) if regexp =~ name
86
+ end
87
+ res
88
+
89
+ end
90
+ def to_json()
91
+ @wrapped.to_json
92
+ end
93
+
94
+ end
95
+
96
+ # Read/Write the JSON for a dataservice.
97
+ # Does not map the actual JSON into a new ruby object.
98
+ class DataService
99
+ attr_accessor :wrapped, :role_name, :root
100
+ def initialize(root, data, role_name)
101
+ @root = root
102
+ @wrapped = data
103
+ @role_name = role_name
104
+ end
105
+ # returns the name of the service for cloudfoundry
106
+ def name()
107
+ @wrapped['name']
108
+ end
109
+ def vendor()
110
+ @wrapped['vendor']
111
+ end
112
+
113
+ # Returns a vcap manifest that can be used
114
+ # to create a new data-service to vcap's cloud_controller.
115
+ def to_vcap_manifest()
116
+ #TODO
117
+ @wrapped
118
+ end
119
+
120
+ end
121
+
122
+ # Read/Write the JSON for an application.
123
+ # Does not map the actual JSON into a new ruby object.
124
+ class Application
125
+ attr_accessor :wrapped, :role_name, :root
126
+ def initialize(root, data, role_name)
127
+ @root = root
128
+ @wrapped = data
129
+ @role_name = role_name
130
+ end
131
+ # Returns the application name (different from the application role name.)
132
+ def name()
133
+ @wrapped['name']
134
+ end
135
+ def uris()
136
+ @wrapped['uris']
137
+ end
138
+ def memory()
139
+ @wrapped['resources']['memory']
140
+ end
141
+ def env()
142
+ ApplicationEnvironment.new @wrapped['env'], self
143
+ end
144
+
145
+ # Returns a vcap manifest that can be used
146
+ # to push/update an application to vcap's cloud_controller.
147
+ def to_vcap_manifest()
148
+ # This is pretty much identical to the json wrapped here except for the environment variables.
149
+ # if there are differences we will take care of them here.
150
+ @wrapped
151
+ end
152
+
153
+ end
154
+
155
+ # Read/Write the application environment. a list of strings
156
+ # where the first '=' character separate the key and values.
157
+ class ApplicationEnvironment
158
+ attr_accessor :wrapped, :application
159
+ def initialize(data, application)
160
+ @wrapped = data
161
+ @application = application
162
+ end
163
+ #Sets a variable. Replaces other environment variables with the
164
+ #same name if there is such a thing.
165
+ def set(name,value)
166
+ foundit = false
167
+ @wrapped.map! do |item|
168
+ /^([\w]*)=(.*)$/ =~ item
169
+ #puts "#{name} 1=#{$1} and 2=#{$2}"
170
+ if ($1 == name)
171
+ #puts "updating #{$1}"
172
+ foundit = true
173
+ "#{$1}=#{value}"
174
+ else
175
+ item
176
+ end
177
+ end
178
+ #puts "appending #{name}=#{value}" unless foundit
179
+ append(name,value) unless foundit
180
+ end
181
+ def get(name)
182
+ @wrapped.each do |e|
183
+ /^([\w]*)=(.*)$/ =~ e
184
+ if ($1 == name)
185
+ #puts "#{k}=name{v}"
186
+ return $2
187
+ end
188
+ end
189
+ return nil
190
+ end
191
+ def del(name)
192
+ @wrapped.keep_if {|v| v =~ /^#{name}=/; $0.nil?}
193
+ end
194
+ def append(name,value)
195
+ @wrapped << "#{name}=#{value}"
196
+ end
197
+ end
198
+
199
+ class RecipesConfigurationApplier
200
+ attr_accessor :root, :client, :applications, :recipes, :data_services
201
+ # Select the applications and data-services to configure according to the values
202
+ # in the SaaS manifest. When the selector is nil all of them are selected.
203
+ def initialize(manifest, client, recipe_sel=nil, application_sel=nil, service_sel=nil)
204
+ @root = Root.new manifest
205
+ @client = client
206
+ @recipes = @root.recipes(recipe_sel)
207
+ @applications = Array.new
208
+ @data_services = Array.new
209
+ @recipes.each do |recipe|
210
+ @applications = @applications + recipe.applications(application_sel)
211
+ @data_services = @data_services + recipe.data_services(service_sel)
212
+ end
213
+ end
214
+ # Only for testing: inject json
215
+ def __set_current(current_services=nil,current_services_info=nil)
216
+ @current_services = current_services
217
+ @current_services_info = current_services_info
218
+ end
219
+ def updates_pending()
220
+ return @updates_report if @updates_report
221
+ @current_services ||= @client.services
222
+ @current_services_info ||= @client.services_info
223
+ res = Hash.new
224
+ data_services_updates = Hash.new
225
+ applications_updates = Hash.new
226
+ @data_service_updaters = Hash.new
227
+ @application_updaters = Hash.new
228
+ @data_services.each do |data_service|
229
+ unless @data_service_updaters[data_service.name]
230
+ data_service_updater = DataServiceManifestApplier.new data_service, @client, @current_services, @current_services_info
231
+ @data_service_updaters[data_service.name] = data_service_updater
232
+ updates = data_service_updater.updates_pending
233
+ data_services_updates[data_service.name] = updates if updates
234
+ end
235
+ end
236
+ @applications.each do |application|
237
+ unless @application_updaters[application.name]
238
+ application_updater = ApplicationManifestApplier.new application, @client
239
+ @application_updaters[application.name] = application_updater
240
+ updates = application_updater.updates_pending
241
+ applications_updates[application.name] = updates if updates
242
+ end
243
+ end
244
+ res['services'] = data_services_updates unless data_services_updates.empty?
245
+ res['applications'] = applications_updates unless applications_updates.empty?
246
+ @updates_report = res
247
+ puts JSON.pretty_generate @updates_report
248
+ @updates_report
249
+ end
250
+ def upload()
251
+ @applications.each do |application|
252
+ application_updater = ApplicationManifestApplier.new application, @client
253
+ application_updater.upload
254
+ end
255
+ end
256
+ def restart()
257
+ stop()
258
+ start()
259
+ end
260
+ def stop()
261
+ @applications.each do |application|
262
+ application_updater = ApplicationManifestApplier.new application, @client
263
+ application_updater.stop
264
+ end
265
+ end
266
+ def start()
267
+ @applications.each do |application|
268
+ application_updater = ApplicationManifestApplier.new application, @client
269
+ application_updater.start
270
+ end
271
+ end
272
+ def execute()
273
+ return if updates_pending.empty?
274
+ @data_service_updaters.each do |name,data_service_updater|
275
+ data_service_updater.execute
276
+ end
277
+ @application_updaters.each do |name,application_updater|
278
+ application_updater.execute
279
+ end
280
+ end
281
+ end
282
+ class DataServiceManifestApplier
283
+ attr_accessor :data_service_json, :client, :current_services, :current_services_info
284
+ def initialize(data_service,client,current_services=nil,current_services_info=nil)
285
+ @client = client
286
+ if data_service.kind_of? Hash
287
+ @data_service_json = data_service
288
+ elsif data_service.kind_of? DataService
289
+ @data_service_json = data_service.wrapped
290
+ else
291
+ raise "Unexpected type of object to describe the data_service #{data_service}"
292
+ end
293
+ raise "Can't find the name of the data_service" if @data_service_json['name'].nil?
294
+ end
295
+ def current()
296
+ return @current unless @current.nil?
297
+ @current_services ||= @client.services
298
+ @current_services_info ||= @client.services_info
299
+ @current_services.each do |service|
300
+ if service[:name] == @data_service_json['name']
301
+ @current = service
302
+ break
303
+ end
304
+ end
305
+ @current ||= Hash.new # that would be a new service.
306
+ end
307
+
308
+ # Only for testing: inject json
309
+ def __set_current(current,current_services=nil,current_services_info=nil)
310
+ @current = current
311
+ @current_services = current_services
312
+ @current_services_info = current_services_info
313
+ end
314
+ def updates_pending()
315
+ vendor = @data_service_json['vendor']
316
+ name = @data_service_json['name']
317
+ sh = service_hash()
318
+ return "Create data-service #{name} vendor #{vendor}" if current().empty?
319
+ end
320
+ def execute()
321
+ return unless updates_pending
322
+ service_man = service_hash()
323
+ puts "Calling client.create_service #{@data_service_json['vendor']}, #{@data_service_json['name']}"
324
+ client.create_service @data_service_json['vendor'], @data_service_json['name']
325
+ end
326
+ # Returns the service manifest for the vendor.
327
+ # If the service vendor ( = type) is not provided by this vcap install
328
+ # An exception is raised.
329
+ def service_hash()
330
+ searched_vendor = @data_service_json['vendor']
331
+ current()
332
+ # in the vmc.rb code there is a comment that says 'FIXME!'
333
+ @current_services_info.each do |service_type, value|
334
+ value.each do |vendor, version|
335
+ version.each do |version_str, service_descr|
336
+ if searched_vendor == service_descr[:vendor]
337
+ return {
338
+ "type" => service_descr[:type], "tier" => 'free',
339
+ "vendor" => searched_vendor, "version" => version_str
340
+ }
341
+ end
342
+ end
343
+ end
344
+ end
345
+ raise "vcap does not provide a data-service which vendor is #{searched_vendor}"
346
+ end
347
+
348
+
349
+ end
350
+ class ApplicationManifestApplier
351
+ attr_accessor :application_json, :client, :current_name
352
+ # @param application The application object as defined in the SaaS manifest
353
+ # or the JSON for it.
354
+ # @param client the vmc client object. assumed that it is logged in.
355
+ def initialize(application, client, old_name=nil)
356
+ @client = client
357
+ if application.kind_of? Hash
358
+ @application_json = application
359
+ elsif application.kind_of? Application
360
+ @application_json = application.wrapped
361
+ else
362
+ raise "Unexpected type of object to describe the application #{application}"
363
+ end
364
+ raise "Can't find the name of the application" if @application_json['name'].nil?
365
+ @current_name = old_name
366
+ @current_name ||= @application_json['old_name']
367
+ @current_name ||= @application_json['name']
368
+ end
369
+
370
+ def safe_app_info(name)
371
+ begin
372
+ return @client.app_info(name)
373
+ rescue VMC::Client::NotFound
374
+ #expected
375
+ return
376
+ end
377
+ end
378
+
379
+ def current()
380
+ return @current unless @current.nil?
381
+ @current = safe_app_info(@current_name)
382
+ @current ||= safe_app_info(@application_json['name']) # in case the rename occurred already.
383
+ @current ||= Hash.new # that would be a new app.
384
+ end
385
+
386
+ # Only for testing: inject json
387
+ def __set_current(current)
388
+ @current = current
389
+ end
390
+
391
+ def execute()
392
+ diff = updates_pending()
393
+ if diff && diff.size > 0
394
+ if @current['name'].nil? && @current[:name].nil?
395
+ puts "Creating #{@application_json['name']} with #{updated_manifest.inspect}"
396
+ @client.create_app(@application_json['name'], updated_manifest)
397
+ elsif @current['name'] != @application_json['name']
398
+ # This works for renaming the application too.
399
+ puts "Updating #{@application_json['name']} with #{updated_manifest.inspect}"
400
+ @client.update_app(@application_json['name'], updated_manifest)
401
+ end
402
+ end
403
+ end
404
+
405
+ def upload()
406
+ raise "The application #{@application_json['name']} does not exist yet" if current().empty?
407
+ return unless @application_json['repository']
408
+ url = @application_json['repository']['url']
409
+ Dir.chdir(ENV['HOME']) do
410
+ FileUtils.mkdir_p "vmc_knife_downloads/#{@application_json['name']}"
411
+ Dir.chdir("vmc_knife_downloads/#{@application_json['name']}") do
412
+ if Dir.entries(Dir.pwd).size == 2
413
+ puts "Dir.entries(#{Dir.pwd}).size #{Dir.entries(Dir.pwd).size}"
414
+ #empty directory.
415
+ `wget --output-document=_download_.zip #{url}`
416
+ raise "Unable to download #{url}" unless $? == 0
417
+ `unzip _download_.zip`
418
+ `rm _download_.zip`
419
+ end
420
+ VMC::KNIFE::HELPER.static_upload_app_bits(@client,@application_json['name'],Dir.pwd)
421
+ end
422
+ end
423
+ end
424
+
425
+ def start()
426
+ raise "The application #{@application_json['name']} does not exist yet" if current().empty?
427
+ return if current[:state] == 'STARTED'
428
+ current[:state] = 'STARTED'
429
+ client.update_app(@application_json['name'], current())
430
+ end
431
+
432
+ def stop()
433
+ raise "The application #{@application_json['name']} does not exist yet" if current().empty?
434
+ return if current[:state] == 'STOPPED'
435
+ current[:state] = 'STOPPED'
436
+ client.update_app(@application_json['name'], current())
437
+ end
438
+
439
+ # Generate the updated application manifest:
440
+ # take the manifest defined in the saas recipe
441
+ # merge it with the current manifest of the application.
442
+ def updated_manifest()
443
+ new_app_manifest = JSON.parse(current.to_json) # a deep clone.
444
+ #now let's update everything.
445
+ new_mem = @application_json['resources']['memory'] unless @application_json['resources'].nil?
446
+ new_app_manifest[:name] = @application_json['name']
447
+ new_app_manifest['resources'] = Hash.new if new_app_manifest['resources'].nil?
448
+ new_app_manifest['resources']['memory'] = new_mem unless new_mem.nil?
449
+ unless @application_json['staging'].nil?
450
+ new_app_manifest['staging'] = Hash.new if new_app_manifest['staging'].nil?
451
+ new_app_manifest['staging']['model'] = @application_json['staging']['model'] unless @application_json['staging']['model'].nil?
452
+ #new_app_manifest['staging']['framework'] = new_app_manifest['staging']['model']
453
+ new_app_manifest['staging']['stack'] = @application_json['staging']['stack'] unless @application_json['staging']['stack'].nil?
454
+ end
455
+ new_app_manifest['uris'] = @application_json['uris'] unless @application_json['uris'].nil?
456
+ new_app_manifest['services'] = @application_json['services'] unless @application_json['services'].nil?
457
+ new_app_manifest['env'] = @application_json['env'] unless @application_json['env'].nil?
458
+ new_app_manifest
459
+ end
460
+
461
+ # Returns a json object where we see the differences.
462
+ def updates_pending()
463
+ name = update_name_pending()
464
+ services = update_services_pending()
465
+ env = update_env_pending()
466
+ memory = update_memory_pending()
467
+ uris = update_uris_pending()
468
+ updates = Hash.new
469
+ updates['name'] = name if name
470
+ updates['services'] = services if services
471
+ updates['env'] = services if services
472
+ updates['uris'] = uris if uris
473
+ updates['services'] = services if services
474
+ updates unless updates.empty?
475
+ end
476
+
477
+ def update_name_pending()
478
+ if current['name'].nil?
479
+ return "Create #{@application_json['name']}"
480
+ end
481
+ if @application_json['name'] != @current['name']
482
+ return "#{@current['name']} => #{@application_json['name']}"
483
+ end
484
+ end
485
+ def update_memory_pending()
486
+ old_mem = current['resources']['memory'] unless current['resources'].nil?
487
+ new_mem = @application_json['resources']['memory'] unless @application_json['resources'].nil?
488
+ if old_mem != new_mem
489
+ return "#{old_mem} => #{new_mem}"
490
+ end
491
+ end
492
+ def update_staging_pending()
493
+ old_model = current['staging']['model'] unless current['staging'].nil?
494
+ old_stack = current['staging']['stack'] unless current['staging'].nil?
495
+ new_model = @application_json['staging']['model'] unless @application_json['staging'].nil?
496
+ new_stack = @application_json['staging']['stack'] unless @application_json['staging'].nil?
497
+ if old_model != new_model
498
+ model_change "#{old_model} => #{new_model}"
499
+ end
500
+ if old_stack != new_stack
501
+ stack_change "#{old_stack} => #{new_stack}"
502
+ end
503
+ return if model_change.nil? && stack_change.nil?
504
+ return { "stack" => stack_change } if model_change.empty?
505
+ return { "model" => model_change } if stack_change.empty?
506
+ return { "stack" => stack_change, "model" => model_change }
507
+ end
508
+ def update_services_pending()
509
+ old_services = current['services']
510
+ new_services = @application_json['services']
511
+ diff_lists(old_services,new_services)
512
+ end
513
+ def update_env_pending()
514
+ old_services = current['env']
515
+ new_services = @application_json['env']
516
+ diff_lists(old_services,new_services)
517
+ end
518
+ def update_uris_pending()
519
+ old_services = current['uris']
520
+ new_services = @application_json['uris']
521
+ diff_lists(old_services,new_services)
522
+ end
523
+ def diff_lists(old_services,new_services)
524
+ new_services ||= Array.new
525
+ old_services ||= Array.new
526
+ add = Array.new
527
+ remove = Array.new
528
+ new_services.each do |item|
529
+ add << item unless old_services.include? item
530
+ end
531
+ old_services.each do |item|
532
+ remove << item unless new_services.include? item
533
+ end
534
+ return if add.empty? && remove.empty?
535
+ return { "add" => add } if remove.empty?
536
+ return { "remove" => remove } if add.empty?
537
+ return { "add" => add, "remove" => remove }
538
+ end
539
+ end
540
+
541
+ # This is really a server-side vcap admin feature.
542
+ class VCAPUpdateCloudControllerConfig
543
+ def initialize(uri, cloud_controller_config=nil)
544
+ @config = cloud_controller_config
545
+ @config ||="#{ENV['HOME']}/cloudfoundry/config/cloud_controller.yml"
546
+ @uri = uri
547
+ raise "The config file #{@config} does not exist." unless File.exists? @config
548
+ end
549
+ def update_pending()
550
+ res = false
551
+ File.open(@config, "r") do |file|
552
+ file.each_line do |s|
553
+ if /^[\s]*external_uri:/ =~ s
554
+ res = true unless /#{@uri}[\s]*$/ =~ s
555
+ end
556
+ end
557
+ end
558
+ return res
559
+ end
560
+ # look for
561
+ # cloud_controller_uri: http://api.intalio.me
562
+ # and replace it with the new one.
563
+ def update_gateway(config)
564
+ changed = false
565
+ lines = IO.readlines config
566
+ File.open(config, "w") do |file|
567
+ lines.each do |s|
568
+ if /^[\s]*cloud_controller_uri:/ =~ s
569
+ changed = true unless /#{@uri}[\s]*$/ =~ s
570
+ file.puts "cloud_controller_uri: http://#{@uri}\n"
571
+ else
572
+ file.puts s
573
+ end
574
+ end
575
+ end
576
+ changed
577
+ end
578
+ def execute()
579
+ @changed = false
580
+ @changed_gateways = Array.new
581
+ # look for the line that starts with external_uri:
582
+ # replace it with the new uri if indeed there was a change.
583
+ lines = IO.readlines @config
584
+ File.open(@config, "w") do |file|
585
+ lines.each do |s|
586
+ if /^[\s]*external_uri:/ =~ s
587
+ @changed = true unless /#{@uri}[\s]*$/ =~ s
588
+ file.puts "external_uri: #{@uri}\n"
589
+ else
590
+ file.puts s
591
+ end
592
+ end
593
+ end
594
+ if @changed
595
+ #update the gateways too.
596
+ @changed_gateways = Array.new
597
+ yaml_files = Dir.glob File.join(File.dirname(@config), "*_gateway.yml")
598
+ yaml_files.each do |path|
599
+ if update_gateway(path)
600
+ gateway = File.basename(path, '.yml')
601
+ @changed_gateways << gateway
602
+ end
603
+ end
604
+ cc_yml = File.open( @config ) { |yf| YAML::load( yf ) }
605
+ pid = cc_yml['pid']
606
+ if pid!=nil && File.exists?(pid)
607
+ display "Restarting the reconfigured cloud_controller"
608
+ #assuming that the vcap symlink is in place. maker sure the aliases
609
+ # will be resolved.
610
+ #`shopt -s expand_aliases; vcap restart cloud_controller`
611
+ #TODO: something that looks nicer?
612
+ system ". ~/.bashrc;\n vcap restart cloud_controller"
613
+ @changed_gateways.each do |gateway|
614
+ system ". ~/.bashrc;\n vcap restart #{gateway}"
615
+ end
616
+ end
617
+ end
618
+ end
619
+ def was_changed()
620
+ @changed
621
+ end
622
+ end
623
+
624
+ # This is really a server-side feature.
625
+ # Replace the 127.0.0.1 localhost #{old_uri} with the new uri
626
+ class VCAPUpdateEtcHosts
627
+ def initialize(uri, etc_hosts_path=nil)
628
+ @config = etc_hosts_path
629
+ @config ||="/etc/hosts"
630
+ @uri = uri
631
+ raise "The config file #{@config} does not exist." unless File.exists? @config
632
+ end
633
+ def update_pending()
634
+ #could also use:
635
+ found_it=`sed -n '/^127\.0\.0\.1[[:space:]]*localhost[[:space:]]*#{@uri}/p' #{@config}`
636
+ return true unless found_it && found_it.strip.length != 0
637
+ return false
638
+ end
639
+ def execute()
640
+ return unless update_pending
641
+ @changed = false
642
+ # look for the line that starts with external_uri:
643
+ # replace it with the new uri if indeed there was a change.
644
+ if true
645
+ # use sudo.
646
+ puts "Executing sudo sed -i 's/^127\.0\.0\.1[[:space:]]*localhost.*$/127.0.0.1 localhost #{@uri}/g' #{@config}"
647
+ `sudo sed -i 's/^127\.0\.0\.1[[:space:]]*localhost.*$/127.0.0.1 localhost #{@uri}/g' #{@config}`
648
+ else
649
+ lines = IO.readlines @config
650
+ File.open(@config, "w") do |file|
651
+ lines.each do |s|
652
+ if /^127.0.0.1[\s]+localhost[\s]*/ =~ s
653
+ @changed = true unless /^127.0.0.1[\s]+localhost[\s]+#{@uri}[\s]*/ =~ s
654
+ file.puts "127.0.0.1\tlocalhost #{@uri}\n"
655
+ else
656
+ file.puts s
657
+ end
658
+ end
659
+ end
660
+ end
661
+ #Edit the /etc/hostname file: (not a necessity so far).
662
+ #`sudo hostname #{@uri}`
663
+ end
664
+ def was_changed()
665
+ @changed
666
+ end
667
+ end
668
+
669
+ # This is really a server-side feature.
670
+ # Regenerates the urls to publish as aliases.
671
+ # use vmc apps to read the uris of each app and also the manifest.
672
+ class VCAPUpdateAvahiAliases
673
+ attr_accessor :do_exec
674
+ def initialize(avahi_aliases_path=nil, manifest_path=nil,client=nil)
675
+ @manifest_path = manifest_path
676
+ @client = client
677
+ @config = avahi_aliases_path
678
+ @config ||= '/etc/avahi/aliases'
679
+ end
680
+ def apps_uris()
681
+ return @apps_uris unless @apps_uris.nil?
682
+ uris = Array.new
683
+ return uris unless @client
684
+ apps = @client.apps
685
+ api_uri = URI.parse(@client.target).host
686
+ uris << api_uri if /\.local$/ =~ api_uri
687
+ apps.each do |app|
688
+ app[:uris].each do |uri|
689
+ #only publish the uris in the local domain.
690
+ uris << uri if /\.local$/ =~ uri
691
+ end
692
+ end
693
+ uris.uniq!
694
+ uris.sort!
695
+ @apps_uris = uris
696
+ @apps_uris
697
+ end
698
+ def manifest_uris()
699
+ uris = Array.new
700
+ return uris unless @manifest_path
701
+ root = Root.new @manifest_path
702
+ root.recipes.each do |recipe|
703
+ recipe.applications.each do |application|
704
+ application.uris.each do |uri|
705
+ uris << uri if /\.local$/ =~ uri
706
+ end
707
+ end
708
+ end
709
+ uris.uniq!
710
+ uris.sort!
711
+ uris
712
+ end
713
+ def all_uris()
714
+ uris = manifest_uris+apps_uris
715
+ uris.uniq!
716
+ uris.sort!
717
+ uris
718
+ end
719
+ def already_published_uris()
720
+ already = IO.readlines @config
721
+ already.collect! {|item| item.strip}
722
+ already.select! {|item| !item.empty?}
723
+ already.uniq!
724
+ already.sort!
725
+ already
726
+ end
727
+ def execute()
728
+ return unless update_pending()
729
+ File.open(@config, "w") do |file|
730
+ all_uris().each do |uri|
731
+ file.puts uri + "\n"
732
+ end
733
+ end
734
+ #configured so that we don't need root privileges on /etc/avahi/aliases:
735
+ #the backticks don't work; system() works:
736
+ system('avahi-publish-aliases') if @do_exec
737
+ end
738
+ def update_pending()
739
+ already = already_published_uris()
740
+ length_already = already.length
741
+ allall = already + all_uris()
742
+ allall.uniq!
743
+ return length_already != allall.length
744
+ end
745
+ end
746
+
747
+ end # end of KNIFE
748
+ end