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.
- data/.gitignore +51 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +63 -0
- data/Gemfile +18 -0
- data/LICENSE +201 -0
- data/README.md +332 -0
- data/Rakefile +92 -0
- data/TODO.md +8 -0
- data/VERSION +1 -0
- data/chefignore +41 -0
- data/cluster_chef.gemspec +115 -0
- data/clusters/website_demo.rb +65 -0
- data/config/client.rb +59 -0
- data/lib/cluster_chef/chef_layer.rb +297 -0
- data/lib/cluster_chef/cloud.rb +409 -0
- data/lib/cluster_chef/cluster.rb +118 -0
- data/lib/cluster_chef/compute.rb +144 -0
- data/lib/cluster_chef/cookbook_munger/README.md.erb +47 -0
- data/lib/cluster_chef/cookbook_munger/licenses.yaml +16 -0
- data/lib/cluster_chef/cookbook_munger/metadata.rb.erb +23 -0
- data/lib/cluster_chef/cookbook_munger.rb +588 -0
- data/lib/cluster_chef/deprecated.rb +33 -0
- data/lib/cluster_chef/discovery.rb +158 -0
- data/lib/cluster_chef/dsl_object.rb +123 -0
- data/lib/cluster_chef/facet.rb +144 -0
- data/lib/cluster_chef/fog_layer.rb +134 -0
- data/lib/cluster_chef/private_key.rb +110 -0
- data/lib/cluster_chef/role_implications.rb +49 -0
- data/lib/cluster_chef/security_group.rb +103 -0
- data/lib/cluster_chef/server.rb +265 -0
- data/lib/cluster_chef/server_slice.rb +259 -0
- data/lib/cluster_chef/volume.rb +93 -0
- data/lib/cluster_chef.rb +137 -0
- data/notes/aws_console_screenshot.jpg +0 -0
- data/rspec.watchr +29 -0
- data/spec/cluster_chef/cluster_spec.rb +13 -0
- data/spec/cluster_chef/facet_spec.rb +70 -0
- data/spec/cluster_chef/server_slice_spec.rb +19 -0
- data/spec/cluster_chef/server_spec.rb +112 -0
- data/spec/cluster_chef_spec.rb +193 -0
- data/spec/spec_helper/dummy_chef.rb +25 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/test_config.rb +20 -0
- data/tasks/chef_config.rb +38 -0
- data/tasks/jeweler_use_alt_branch.rb +47 -0
- 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
|