cluster_chef 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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