cluster_chef 3.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +51 -0
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +63 -0
  4. data/Gemfile +18 -0
  5. data/LICENSE +201 -0
  6. data/README.md +332 -0
  7. data/Rakefile +92 -0
  8. data/TODO.md +8 -0
  9. data/VERSION +1 -0
  10. data/chefignore +41 -0
  11. data/cluster_chef.gemspec +115 -0
  12. data/clusters/website_demo.rb +65 -0
  13. data/config/client.rb +59 -0
  14. data/lib/cluster_chef/chef_layer.rb +297 -0
  15. data/lib/cluster_chef/cloud.rb +409 -0
  16. data/lib/cluster_chef/cluster.rb +118 -0
  17. data/lib/cluster_chef/compute.rb +144 -0
  18. data/lib/cluster_chef/cookbook_munger/README.md.erb +47 -0
  19. data/lib/cluster_chef/cookbook_munger/licenses.yaml +16 -0
  20. data/lib/cluster_chef/cookbook_munger/metadata.rb.erb +23 -0
  21. data/lib/cluster_chef/cookbook_munger.rb +588 -0
  22. data/lib/cluster_chef/deprecated.rb +33 -0
  23. data/lib/cluster_chef/discovery.rb +158 -0
  24. data/lib/cluster_chef/dsl_object.rb +123 -0
  25. data/lib/cluster_chef/facet.rb +144 -0
  26. data/lib/cluster_chef/fog_layer.rb +134 -0
  27. data/lib/cluster_chef/private_key.rb +110 -0
  28. data/lib/cluster_chef/role_implications.rb +49 -0
  29. data/lib/cluster_chef/security_group.rb +103 -0
  30. data/lib/cluster_chef/server.rb +265 -0
  31. data/lib/cluster_chef/server_slice.rb +259 -0
  32. data/lib/cluster_chef/volume.rb +93 -0
  33. data/lib/cluster_chef.rb +137 -0
  34. data/notes/aws_console_screenshot.jpg +0 -0
  35. data/rspec.watchr +29 -0
  36. data/spec/cluster_chef/cluster_spec.rb +13 -0
  37. data/spec/cluster_chef/facet_spec.rb +70 -0
  38. data/spec/cluster_chef/server_slice_spec.rb +19 -0
  39. data/spec/cluster_chef/server_spec.rb +112 -0
  40. data/spec/cluster_chef_spec.rb +193 -0
  41. data/spec/spec_helper/dummy_chef.rb +25 -0
  42. data/spec/spec_helper.rb +50 -0
  43. data/spec/test_config.rb +20 -0
  44. data/tasks/chef_config.rb +38 -0
  45. data/tasks/jeweler_use_alt_branch.rb +47 -0
  46. metadata +227 -0
@@ -0,0 +1,588 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # cookbook_munger.rb -- keep cookbook metadata complete, consistent and correct.
5
+ #
6
+ # This script reads the actual content of a cookbook -- actually interpreting
7
+ # the metadata.rb and attribute files, along with recipes/resources/etc files'
8
+ # headers -- and re-generates the metadata.rb and README files.
9
+ #
10
+ # It also has hooks to do a limited amount of validation and linting.
11
+ #
12
+
13
+ require 'erubis'
14
+ require 'chef/mash'
15
+ require 'chef/mixin/from_file'
16
+
17
+ require 'configliere'
18
+ require 'gorillib/metaprogramming/class_attribute'
19
+ require 'gorillib/hash/reverse_merge'
20
+ require 'gorillib/object/blank'
21
+ require 'gorillib/hash/compact'
22
+ require 'gorillib/string/inflections'
23
+ require 'gorillib/string/human'
24
+ require 'gorillib/logger/log'
25
+ require 'set'
26
+
27
+ $:.unshift File.expand_path('..', File.dirname(__FILE__))
28
+ require 'cluster_chef/dsl_object'
29
+
30
+ # silence the chef log
31
+ class Chef ; class Log ; def self.info(*args) ; end ; def self.debug(*args) ; end ; end ; end
32
+
33
+ Settings.define :maintainer, :default => 'default mantainer name', :default => "Philip (flip) Kromer - Infochimps, Inc"
34
+ Settings.define :maintainer_email, :default => 'default email to add to cookbook headers', :default => "coders@infochimps.com"
35
+ Settings.define :license, :default => 'default license to apply to cookbooks', :default => "Apache 2.0"
36
+ #
37
+ Settings.define :cookbook_paths, :description => 'list of paths holding cookbooks', :type => Array, :default => ["./{site-cookbooks,meta-cookbooks}"]
38
+ #
39
+ Settings.use(:commandline)
40
+ Settings.resolve!
41
+
42
+ String.class_eval do
43
+ def commentize
44
+ self.gsub(/\n/, "\n# ").gsub(/\n# \n/, "\n#\n")
45
+ end
46
+ end
47
+
48
+ module CookbookMunger
49
+ TEMPLATE_ROOT = File.expand_path('cookbook_munger', File.dirname(__FILE__))
50
+
51
+ # ===========================================================================
52
+ #
53
+ # DummyAttribute -- holds metadata about a single cookbook attribute.
54
+ #
55
+ # named like a path: node[:pig][:home_dir] is 'pig/home_dir'
56
+ #
57
+ class DummyAttribute
58
+ attr_accessor :name
59
+ attr_accessor :display_name
60
+ attr_accessor :description
61
+ attr_accessor :choice
62
+ attr_accessor :calculated
63
+ attr_accessor :type
64
+ attr_accessor :required
65
+ attr_accessor :recipes
66
+ attr_accessor :default
67
+
68
+ def initialize(name, hsh={})
69
+ self.name = name
70
+ merge!(hsh)
71
+ @display_name ||= ''
72
+ end
73
+
74
+ def merge!(hsh)
75
+ hsh.each do |key, val|
76
+ self.send("#{key}=", val) unless val.blank?
77
+ end
78
+ end
79
+
80
+ def inspect
81
+ "attr[#{name}:#{default.inspect}]"
82
+ end
83
+
84
+ def bracketed_name
85
+ name.split("/").map{|s| "[:#{s}]" }.join
86
+ end
87
+
88
+ def keys
89
+ [:display_name, :description, :choice, :calculated, :type, :required, :recipes, :default]
90
+ end
91
+
92
+ def to_hash
93
+ hsh = {}
94
+ self.description = display_name if description.blank?
95
+ keys.each do |key|
96
+ hsh[key] = self.send(key) if instance_variable_defined?("@#{key}")
97
+ end
98
+ case hsh[:default]
99
+ when Symbol, Numeric, TrueClass, NilClass, FalseClass then hsh[:default] = hsh[:default].to_s
100
+ when Hash then hsh[:type] ||= 'hash'
101
+ when Array then hsh[:type] ||= 'array'
102
+ end
103
+ hsh
104
+ end
105
+
106
+ def pretty_str
107
+ str = [ %Q{attribute "#{name}"} ]
108
+ to_hash.each do |key, val|
109
+ str << (" :%-21s => %s" % [ key, val.inspect ])
110
+ end
111
+ str.flatten.join(",\n")
112
+ end
113
+
114
+ end
115
+
116
+ # ===========================================================================
117
+ #
118
+ # DummyAttributeCollection -- the cascading buckets to hold attributes
119
+ #
120
+ # This auto-vivifies: just saying `foo[:bar][:baz][:bing]` results in
121
+ # foo becoming
122
+ # `{ :bar => { :baz => { :bing => {} } } }`
123
+ #
124
+ class DummyAttributeCollection < Mash
125
+ attr_accessor :path
126
+
127
+ def initialize(path='')
128
+ self.path = path
129
+ super(){|hsh,key| hsh[key] = DummyAttributeCollection.new(sub_path(key)) }
130
+ end
131
+
132
+ def setter(key=nil)
133
+ # key ? (self[key] = DummyAttributeCollection.new(sub_path(key))) : self
134
+ self
135
+ end
136
+
137
+ def sub_path(key)
138
+ path.blank? ? key.to_s : "#{path}/#{key}"
139
+ end
140
+
141
+ def []=(key, val)
142
+ unless val.is_a?(DummyAttributeCollection) || val.is_a?(DummyAttribute)
143
+ val = DummyAttribute.new(sub_path(key), :default =>val)
144
+ end
145
+ super(key, val)
146
+ end
147
+
148
+ def attrs
149
+ [ leafs.values, branches.map{|key,val| val.attrs } ].flatten
150
+ end
151
+
152
+ def leafs
153
+ select{|key,val| not val.is_a?(DummyAttributeCollection) }
154
+ end
155
+ def branches
156
+ select{|key,val| val.is_a?(DummyAttributeCollection) }
157
+ end
158
+
159
+ def pretty_str
160
+ str = []
161
+ attrs.each{|attrib| str << attrib.pretty_str }
162
+ str.join("\n\n")
163
+ end
164
+
165
+ end
166
+
167
+ # ===========================================================================
168
+ #
169
+ # CookbookComponent - shared mixin methods for Chef-DSL files (recipes,
170
+ # attributes, definitions, resources, etc)
171
+ #
172
+ module CookbookComponent
173
+ attr_reader :name, :desc, :filename
174
+ # the cookbook object this belongs to
175
+ attr_reader :cookbook
176
+ attr_accessor :header_lines, :body_lines
177
+
178
+ def initialize(cookbook, name, desc, filename, *args, &block)
179
+ super(*args, &block)
180
+ @cookbook = cookbook
181
+ @name = name
182
+ @desc = desc
183
+ @filename = filename
184
+ end
185
+
186
+ def raw_lines
187
+ begin
188
+ @raw_lines ||= File.readlines(filename).map(&:chomp)
189
+ rescue Errno::ENOENT => boom
190
+ warn boom.to_s
191
+ @raw_lines ||= []
192
+ end
193
+ end
194
+
195
+ def read
196
+ @header_lines = []
197
+ @body_lines = []
198
+ # Gobble the header -- all comment lines following the first
199
+ until raw_lines.first !~ /^#/ || raw_lines.empty?
200
+ line = raw_lines.first
201
+ header_lines << raw_lines.shift
202
+ process_header_line(line)
203
+ end
204
+ # skip blank lines that follow the header
205
+ until raw_lines.first =~ /\S+/ || raw_lines.empty?
206
+ raw_lines.shift
207
+ end
208
+ raw_lines.each do |line|
209
+ body_lines << line
210
+ process_body_line(line)
211
+ end
212
+ end
213
+
214
+ # called on each header line in #read
215
+ def process_header_line(line)
216
+ # override in subclass if you like
217
+ end
218
+ # called on each body line in #read
219
+ def process_body_line(line)
220
+ # override in subclass if you like
221
+ end
222
+
223
+ # save to {filename}.bak
224
+ def dump
225
+ File.open(filename+'.bak', 'w') do |f|
226
+ f << header_lines.join("\n")
227
+ f << "\n\n"
228
+ f << body_lines.join("\n")
229
+ f << "\n"
230
+ end
231
+ end
232
+
233
+ # Use the chef from_file mixin -- instance_exec the file
234
+ def execute!
235
+ from_file(filename)
236
+ end
237
+
238
+ module ClassMethods
239
+ def read(cookbook, name, desc, filename)
240
+ attr_file = self.new(cookbook, name, desc, filename)
241
+ attr_file.read
242
+ attr_file
243
+ end
244
+ end
245
+ def self.included(base) base.extend ClassMethods ; end
246
+ end
247
+
248
+ # ===========================================================================
249
+ #
250
+ # RecipeFile -- a chef recipe
251
+ #
252
+ class RecipeFile
253
+ attr_accessor :copyright_lines, :author_lines, :include_recipes, :include_cookbooks
254
+ include CookbookComponent
255
+
256
+ def initialize(*args, &block)
257
+ super
258
+ @include_recipes = []
259
+ @include_cookbooks = []
260
+ end
261
+
262
+ def process_header_line(line)
263
+ self.author_lines << "# Author:: #{$1}" if line =~ /^# Author::\s*(.*)/
264
+ self.copyright_lines << line if line =~ /^# Copyright / && line !~ /YOUR_COMPANY_NAME/
265
+ end
266
+
267
+ def process_body_line(line)
268
+ if line =~ /include_recipe\(?\s*[\"\']([^\"\'\:]*?)(::.*?)?[\"\']\s*\)?(?:[#;].*)?$/
269
+ i_cb, i_rp = [$1, $2]
270
+ i_rp = nil if i_rp == "default"
271
+ self.include_cookbooks << i_cb
272
+ self.include_recipes << [i_cb, i_rp].compact.join("::")
273
+ end
274
+ end
275
+
276
+ def read
277
+ self.author_lines = []
278
+ self.copyright_lines = []
279
+ super
280
+ self.copyright_lines = ["# Copyright #{cookbook.copyright_text}"] if copyright_lines.blank?
281
+ self.author_lines = ["# Author:: #{cookbook.maintainer}"] if author_lines.blank?
282
+ end
283
+
284
+ def dump
285
+ super
286
+ end
287
+
288
+ def lint
289
+ if self.name == 'default'
290
+ sketchy = (include_recipes & %w[ runit::default java::sun ])
291
+ if sketchy.present? then warn "Recipe #{cookbook.name}::#{name} includes #{sketchy.inspect} -- put these in component cookbooks, not the default." ; end
292
+ end
293
+ end
294
+
295
+ def generate_header!
296
+ new_header_lines = ['#']
297
+ new_header_lines << "# Cookbook Name:: #{cookbook.name}"
298
+ new_header_lines << "# Description:: #{desc.commentize}"
299
+ new_header_lines << "# Recipe:: #{name}"
300
+ new_header_lines += author_lines
301
+ new_header_lines << "#"
302
+ new_header_lines += copyright_lines
303
+ new_header_lines << "#"
304
+ new_header_lines << ("# "+cookbook.short_license_text.commentize) << '#'
305
+ self.header_lines = new_header_lines
306
+ end
307
+
308
+ end
309
+
310
+ # ===========================================================================
311
+ #
312
+ # AttributeFile -- a chef attribute file
313
+ #
314
+ # The metadata in here will be merged with anything found in the metadata.rb
315
+ # file, with these winning
316
+ #
317
+ class AttributeFile
318
+ include Chef::Mixin::FromFile
319
+ include CookbookComponent
320
+ attr_reader :all_attributes
321
+
322
+ def initialize(*args, &block)
323
+ super(*args, &block)
324
+ @all_attributes = DummyAttributeCollection.new
325
+ end
326
+
327
+ #
328
+ # Fake the DSL so we can run the attributes file in our context
329
+ #
330
+
331
+ def default
332
+ all_attributes
333
+ end
334
+ def set
335
+ all_attributes
336
+ end
337
+ def attribute?(key) node.has_key?(key.to_sym) ; end
338
+ def node
339
+ {
340
+ :hostname => 'hostname',
341
+ :cluster_name => :cluster_name,
342
+ :platform => 'ubuntu', :platform_version => '10.4',
343
+ :cloud => { :private_ips => ['10.20.30.40'] },
344
+ :cpu => { :total => 2 }, :memory => { :total => 2 },
345
+ :kernel => { :os => '', :release => '', :machine => '' ,},
346
+ :ec2 => { :instance_type => 'm1.large', },
347
+ :hbase => { :home_dir => '/usr/lib/hbase', },
348
+ :zookeeper => { :home_dir => '/usr/lib/zookeeper', },
349
+ :redis => { :slave => 'no' },
350
+ :ipaddress => '10.20.30.40',
351
+ :languages => { :ruby => { :version => "1.9" } },
352
+ :cassandra => { :mx4j_version => 'x.x' },
353
+ :ganglia => { :home_dir => '/var/lib/ganglia' },
354
+ }.merge(@all_attributes)
355
+ end
356
+ def method_missing(meth, *args)
357
+ if args.empty? && node.has_key?(meth)
358
+ node[meth]
359
+ else
360
+ super(meth, *args)
361
+ end
362
+ end
363
+
364
+ def value_for_platform(hsh)
365
+ hsh["default"] || hsh[hsh.keys.first]
366
+ end
367
+
368
+ end
369
+
370
+ # ===========================================================================
371
+ #
372
+ # CookbookMetadata -- the main deal. Unifies information from metadata.rb, the
373
+ # attributes/ files, the rest of the tree; produces a synthesized metadata.rb
374
+ # and README.md.
375
+ #
376
+ class CookbookMetadata < ClusterChef::DslObject
377
+ include Chef::Mixin::FromFile
378
+ attr_reader :dirname
379
+ has_keys :name, :author, :maintainer, :maintainer_email, :license, :version, :description, :long_desc_gen
380
+ has_keys :long_description
381
+ attr_reader :all_depends, :all_recipes, :all_attributes, :all_resources, :all_supports, :all_recommends
382
+ attr_reader :components, :attribute_files
383
+
384
+ # also: grouping, conflicts, provides, replaces, recommends, suggests
385
+
386
+ # definition: provides "here(:kitty, :time_to_eat)"
387
+ # resource: provides "service[snuggle]"
388
+
389
+ def initialize(nm, dirname, *args, &block)
390
+ super(*args, &block)
391
+ name(nm)
392
+ @dirname = dirname
393
+ @attribute_files = {}
394
+ @all_attributes = CookbookMunger::DummyAttributeCollection.new
395
+ @all_depends ||= {}
396
+ @all_recommends ||= {}
397
+ @all_supports ||= %w[ debian ubuntu ]
398
+ @all_recipes ||= {}
399
+ @all_resources ||= {}
400
+ long_desc_gen(%Q{IO.read(File.join(File.dirname(__FILE__), 'README.md'))}) unless long_desc_gen
401
+ end
402
+
403
+ #
404
+ # Fake DSL
405
+ #
406
+
407
+ # add dependency to list
408
+ def depends(nm, ver=nil) @all_depends[nm] = (ver ? %Q{"#{nm}", "#{ver}"} : %Q{"#{nm}"} ) ; end
409
+ # add recommended dependency to list
410
+ def recommends(nm, ver=nil) @all_recommends[nm] = (ver ? %Q{"#{nm}", "#{ver}"} : %Q{"#{nm}"} ) ; end
411
+ # add supported OS to list
412
+ def supports(nm, ver=nil) @all_supports << nm ; @all_supports.uniq! ; @all_supports ; end
413
+ # add resource to list
414
+ def resource(nm, desc) @all_resources[nm] = { :name => nm, :description => desc } ; end
415
+
416
+ # pull out the non-generated part of the README
417
+ def long_description(val=nil)
418
+ return @long_description.to_s if val.nil?
419
+ lines = val.split(/\n/)
420
+ until (not lines.last.blank?) || lines.empty? ; lines.pop ; end
421
+ if lines.last =~ /^> readme generated by \[cluster_chef\]/
422
+ # it's one of ours; strip out the generated material
423
+ until (lines.first =~ /^## (Overview|Attributes)/) || lines.empty?
424
+ lines.shift
425
+ end
426
+ desc = []
427
+ lines.shift if lines.first =~ /^## (Overview)/
428
+ until (lines.first =~ /^## Attributes/) || lines.empty?
429
+ desc << lines.shift
430
+ end
431
+ else
432
+ desc = lines
433
+ end
434
+ @long_description = desc.join("\n").strip
435
+ end
436
+
437
+
438
+ # add attribute to list
439
+ def attribute(nm, info={})
440
+ return if info[:type] == 'hash'
441
+ path_segs = nm.split("/")
442
+ leaf = path_segs.pop
443
+ attr_branch = @all_attributes
444
+ path_segs.each{|seg| attr_branch = attr_branch[seg] }
445
+ if info.present? || (not attr_branch.has_key?(leaf))
446
+ attr_branch[leaf] = CookbookMunger::DummyAttribute.new(nm, info)
447
+ end
448
+ attr_branch[leaf]
449
+ end
450
+
451
+ # add recipe to list
452
+ def recipe(recipe_name, desc=nil)
453
+ recipe_name = 'default' if recipe_name == name
454
+ recipe_name = recipe_name.gsub(/^#{name}::/, "")
455
+ #
456
+ desc = (recipe_name == 'default' ? "Base configuration for #{name}" : recipe_name.titleize) if (desc.blank? || desc == recipe_name.titleize)
457
+ filename = file_in_cookbook("recipes/#{recipe_name}.rb")
458
+ @all_recipes[recipe_name] ||= RecipeFile.read(self, recipe_name, desc, filename)
459
+ @all_recipes[recipe_name].desc ||= desc if desc.present?
460
+ @all_recipes[recipe_name]
461
+ end
462
+
463
+ #
464
+ # Read project
465
+ #
466
+
467
+ def file_in_cookbook(filename)
468
+ File.expand_path(filename, self.dirname)
469
+ end
470
+
471
+ def load_components
472
+ from_file(file_in_cookbook("metadata.rb"))
473
+
474
+ @components = {
475
+ :attributes => Dir[file_in_cookbook('attributes/*.rb') ].map{|f| nm = File.basename(f, '.rb') ; AttributeFile.read(self, nm, "attributes[#{self.name}::#{nm}", f) },
476
+ :recipes => Dir[file_in_cookbook('recipes/*.rb') ].map{|f| nm = File.basename(f, '.rb') ; recipe("#{name}::#{nm}") },
477
+ :resources => Dir[file_in_cookbook('resources/*.rb') ].map{|f| File.basename(f, '.rb') },
478
+ :providers => Dir[file_in_cookbook('providers/*.rb') ].map{|f| File.basename(f, '.rb') },
479
+ :templates => Dir[file_in_cookbook('templates/**/*.rb') ].map{|f| File.join(File.basename(File.dirname(f)), File.basename(f, '.rb')) },
480
+ :definitions => Dir[file_in_cookbook('definitions/*.rb') ].map{|f| File.basename(f, '.rb') },
481
+ :libraries => Dir[file_in_cookbook('definitions/*.rb') ].map{|f| File.basename(f, '.rb') },
482
+ }
483
+
484
+ components[:attributes].each do |attrib_file|
485
+ merge_attribute_file(attrib_file)
486
+ end
487
+ end
488
+
489
+ def merge_attribute_file(attrib_file)
490
+ attrib_file.execute!
491
+ attrib_file.all_attributes.attrs.each do |af_attrib|
492
+ my_attrib = attribute(af_attrib.name)
493
+ my_attrib.merge!(af_attrib.to_hash)
494
+ end
495
+ end
496
+
497
+ def lint!
498
+ # Settings.each do |attr, sval|
499
+ # my_val = self.send(attr) rescue nil
500
+ # warn([name, attr, sval, my_val ]) unless sval == my_val
501
+ # end
502
+ lint_dependencies
503
+ lint_presence
504
+ components[:recipes].each(&:lint)
505
+ end
506
+
507
+ def lint_dependencies
508
+ include_cookbooks = []
509
+ components[:recipes].each do |recipe|
510
+ include_cookbooks += recipe.include_cookbooks
511
+ end
512
+ include_cookbooks = include_cookbooks.sort.uniq
513
+ missing_dependencies = (include_cookbooks - all_depends.keys - [name])
514
+ missing_includes = (all_depends.keys - include_cookbooks - [name])
515
+ warn "Coookbook #{name} doesn't declare dependency on #{missing_dependencies.join(", ")}, but has an include_recipe that refers to it" if missing_dependencies.present?
516
+ warn "Coookbook #{name} declares dependency on #{missing_includes.join(", ")}, but never calls include_recipe with it" if missing_includes.present?
517
+ end
518
+
519
+ def lint_presence
520
+ components[:recipes].each do |recipe|
521
+ warn "Recipe #{name}::#{recipe.name} #{recipe.filename} missing, though it is alluded to in #{name}/metadata.rb" unless File.exists?(recipe.filename)
522
+ end
523
+ end
524
+
525
+ def dump
526
+ load_components
527
+ lint!
528
+ File.open(file_in_cookbook('metadata.rb.bak'), 'w') do |f|
529
+ f << render('metadata.rb')
530
+ end
531
+ File.open(file_in_cookbook('README.md.bak'), 'w') do |f|
532
+ f << render('README.md')
533
+ end
534
+ components[:recipes].each do |recipe|
535
+ recipe.generate_header!
536
+ recipe.dump
537
+ end
538
+ end
539
+
540
+ #
541
+ # Content
542
+ #
543
+
544
+ def self.licenses
545
+ return @licenses if @licenses
546
+ @licenses = YAML.load(File.read(File.expand_path("licenses.yaml", CookbookMunger::TEMPLATE_ROOT)))
547
+ end
548
+
549
+ def license_info
550
+ @license_info = self.class.licenses.values.detect{|lic| lic[:name] == license }
551
+ end
552
+
553
+ def short_license_text
554
+ license_info ? license_info[:short] : '(no license specified)'
555
+ end
556
+
557
+ def copyright_text
558
+ "2011, #{maintainer}"
559
+ end
560
+
561
+ #
562
+ # Display
563
+ #
564
+
565
+ def render(filename)
566
+ self.class.template(filename).result(self.send(:binding))
567
+ end
568
+
569
+ def self.template(filename)
570
+ template_text = File.read(File.expand_path("#{filename}.erb", CookbookMunger::TEMPLATE_ROOT))
571
+ Erubis::Eruby.new(template_text)
572
+ end
573
+ end
574
+
575
+ puts "-----------------------------------------------------------------------"
576
+ puts "\n\n++++++++++++++++ COOKBOOK MUNGE NOM NOM NOM +++++++++++++++++++\n\n"
577
+ Settings.cookbook_paths.each do |cookbook_path|
578
+
579
+ Dir["#{cookbook_path}/*/metadata.rb"].each do |f|
580
+ dirname = File.dirname(f)
581
+ nm = File.basename(dirname)
582
+ puts "====== %-20s ====================" % nm
583
+ cookbook_metadata = CookbookMetadata.new(nm, dirname, Settings.dup)
584
+ cookbook_metadata.dump
585
+ end
586
+ end
587
+
588
+ end
@@ -0,0 +1,33 @@
1
+ module ClusterChef
2
+
3
+ class Cluster
4
+ #
5
+ # **DEPRECATED**: This doesn't really work -- use +reverse_merge!+ instead
6
+ #
7
+ def use(*clusters)
8
+ ui.warn "The 'use' statement is deprecated #{caller.inspect}"
9
+ clusters.each do |c|
10
+ other_cluster = ClusterChef.load_cluster(c)
11
+ reverse_merge! other_cluster
12
+ end
13
+ self
14
+ end
15
+
16
+ end
17
+
18
+ class Server
19
+ # **DEPRECATED**: Please use +fullname+ instead.
20
+ def chef_node_name name
21
+ ui.warn "[DEPRECATION] `chef_node_name` is deprecated. Please use `fullname` instead."
22
+ fullname name
23
+ end
24
+ end
25
+
26
+ class Cloud::Ec2
27
+ # **DEPRECATED**: Please use +public_ip+ instead.
28
+ def elastic_ip(*args, &block)
29
+ public_ip(*args, &block)
30
+ end
31
+ end
32
+
33
+ end