vmc_knife 0.0.01

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