devstructure 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -235,8 +235,7 @@ EOF
235
235
 
236
236
  # This is the Puppet code generator.
237
237
  def puppet(io=StringIO.new)
238
- io.puts disclaimer
239
- manifest = Puppet::Manifest.new(@name)
238
+ manifest = Puppet::Manifest.new(@name, nil, disclaimer)
240
239
 
241
240
  # Right out of the gate, we set a default `PATH` because Puppet does not.
242
241
  manifest << Puppet::Exec.defaults(:path => ENV["PATH"].split(":"))
@@ -264,7 +263,7 @@ EOF
264
263
  if 0 < classes.length
265
264
  manifest << Puppet::File.defaults(:before => classes)
266
265
  end
267
- files.sort.each do |pathname, content|
266
+ files.each do |pathname, content|
268
267
 
269
268
  # Resources for all parent directories are created ahead of
270
269
  # any file. Puppet's autorequire mechanism will ensure that a
@@ -272,7 +271,7 @@ EOF
272
271
  dirnames = File.dirname(pathname).split("/")
273
272
  dirnames.shift
274
273
  (0..(dirnames.length - 1)).each do |i|
275
- manifest << Puppet::File.new("/#{dirnames[0..i].join("/")}",
274
+ manifest << Puppet::File.new("/#{dirnames[0..i].join("/")}", nil,
276
275
  :ensure => :directory)
277
276
  end
278
277
 
@@ -284,10 +283,12 @@ EOF
284
283
  options[:group] = content["_group"] if content["_group"]
285
284
  if content["_target"]
286
285
  options[:ensure] = content["_target"]
286
+ content = nil
287
287
  else
288
288
  options[:mode] = content["_mode"] if content["_mode"]
289
289
  options[:ensure] = :file
290
- options[:content] = if content["_base64"]
290
+ options[:content] = :"template(\"#{manifest.name}#{pathname}\")"
291
+ content = if content["_base64"]
291
292
  Base64.decode64(content["_base64"])
292
293
  else
293
294
  content["_content"]
@@ -299,12 +300,12 @@ EOF
299
300
  # be set according to the `umask`.
300
301
  else
301
302
  options = {
302
- :content => content,
303
+ :content => :"template(\"#{manifest.name}#{pathname}\")",
303
304
  :ensure => :file,
304
305
  }
305
306
  end
306
307
 
307
- manifest << Puppet::File.new(pathname, options)
308
+ manifest << Puppet::File.new(pathname, content, options)
308
309
  end
309
310
  end
310
311
 
@@ -426,7 +427,7 @@ EOF
426
427
  # such as this in a shell invocation. This is a pretty direct Puppet
427
428
  # equivalent to the shell version above.
428
429
  if sources && 0 < sources.length
429
- sources.sort.each do |dirname, filename|
430
+ sources.each do |dirname, filename|
430
431
  manifest << Puppet::Exec.new(filename,
431
432
  :command => "/bin/sh -c 'wget http://s3.amazonaws.com/blueprint-sources/#{filename}; tar xf #{filename}; rm #{filename}'",
432
433
  :cwd => dirname
@@ -434,9 +435,10 @@ EOF
434
435
  end
435
436
  end
436
437
 
437
- io.puts manifest
438
- io.close
439
- io
438
+ # Generate a module tarball containing the manifest and any templates
439
+ # needed to compile a catalog that includes this module.
440
+ manifest.to_gz(io)
441
+
440
442
  end
441
443
 
442
444
  # This is the Chef code generator. It is the first of its kind in
@@ -1,6 +1,6 @@
1
1
  # The Chef code generator is structured much like the shell code generator
2
2
  # because Chef doesn't include any sort of dependency management like
3
- # Puppet. As expected, we'll start with packages, follow them with files,
3
+ # Puppet. As expected, we'll start with files, follow them with packages,
4
4
  # and finish with source tarballs.
5
5
  #
6
6
  # The monkeywrench is that the ultimate output is a tarball of a Chef
@@ -94,20 +94,20 @@ module Chef
94
94
  :mtime => mtime
95
95
  }) { |w| w.write resources }
96
96
 
97
- # Included any files referenced by `cookbook_file` resources. They
97
+ # Include any files referenced by `cookbook_file` resources. They
98
98
  # all appear in `files/default/` as if that is the root of the
99
99
  # filesystem.
100
100
  if 0 < @files.length
101
101
  tar.mkdir "#{@name}/files", :mode => 0755, :mtime => mtime
102
102
  tar.mkdir "#{@name}/files/default", :mode => 0755, :mtime => mtime
103
- @files.each do |name, content|
104
- dirnames = File.dirname(name).split("/")
103
+ @files.each do |pathname, content|
104
+ dirnames = File.dirname(pathname).split("/")
105
105
  dirnames.shift
106
106
  (0..(dirnames.length - 1)).each do |i|
107
107
  tar.mkdir "#{@name}/files/default/#{dirnames[0..i].join("/")}",
108
108
  :mode => 0755, :mtime => mtime
109
109
  end
110
- tar.add_file_simple("#{@name}/files/default#{name}", {
110
+ tar.add_file_simple("#{@name}/files/default#{pathname}", {
111
111
  :mode => 0644,
112
112
  :size => content.length,
113
113
  :mtime => mtime
@@ -14,6 +14,13 @@
14
14
  # [sandbox-blueprint]: http://devstructure.github.com/contractor/sandbox-blueprint.1.html
15
15
  require 'devstructure'
16
16
 
17
+ # Unfortunately, RubyGems rears its ugly head. The call to `gem` here is
18
+ # just because the gem's name is different than the path being `require`d.
19
+ gem 'archive-tar-minitar', :require => 'archive/tar/minitar'
20
+ require 'archive/tar/minitar'
21
+
22
+ require 'zlib'
23
+
17
24
  # Make `Symbol`s and `nil`s `Comparable` so we can sort each list of resource
18
25
  # attributes before converting to a string.
19
26
  class Symbol
@@ -37,11 +44,13 @@ module DevStructure::Puppet
37
44
 
38
45
  # Each class must have a name and might have a parent. If a manifest
39
46
  # has a parent, this signals it to `include` itself in the parent.
40
- def initialize(name, parent=nil)
41
- @name, @parent = name, parent
47
+ def initialize(name, parent=nil, comment=nil)
48
+ @name, @parent, @comment = name.gsub(".", "--"), parent, comment
42
49
  @manifests, @resources = {}, {}
43
50
  end
44
51
 
52
+ attr_reader :name
53
+
45
54
  # Manifests behave a bit like hashes in that their children can be
46
55
  # traversed. Note the children can't be assigned directly because
47
56
  # we must maintain parent-child relationships.
@@ -68,6 +77,18 @@ module DevStructure::Puppet
68
77
  end
69
78
  end
70
79
 
80
+ # Return a hash of `pathname`s to `content`s for all the templates
81
+ # referenced in this manifest. `file` resources which define the
82
+ # `content` attribute must have a template containing the file itself.
83
+ def templates
84
+ out = {}
85
+ (@resources["file"] || {}).select do |name, resource|
86
+ next unless resource[:content] && resource.content
87
+ out[name] = resource.content
88
+ end
89
+ out
90
+ end
91
+
71
92
  # Turn this manifest into a Puppet class. We start with a base level
72
93
  # of indentation that we carry through our resources and manifests.
73
94
  # Order is again not important so we don't make much effort. The
@@ -80,7 +101,8 @@ module DevStructure::Puppet
80
101
  # ourselves in the parent.
81
102
  def to_s(tab="")
82
103
  out = []
83
- out << "#{tab}class #{@name.gsub(".", "--")} {"
104
+ out << @comment if @comment
105
+ out << "#{tab}class #{@name} {"
84
106
  @manifests.each_value do |manifest|
85
107
  out << manifest.to_s("#{tab}\t")
86
108
  end
@@ -100,10 +122,54 @@ module DevStructure::Puppet
100
122
  end
101
123
  end
102
124
  out << "#{tab}}"
103
- out << "#{tab}include #{@name.gsub(".", "--")}" if @parent
125
+ out << "#{tab}include #{@name}" if @parent
104
126
  out.join("\n")
105
127
  end
106
128
 
129
+ # Create a Puppet module containing a manifest and the files being
130
+ # distributed as templates.
131
+ def to_gz(io)
132
+ mtime = Time.now
133
+ gz = Zlib::GzipWriter.new(io)
134
+ tar = Archive::Tar::Minitar::Writer.new(gz)
135
+ tar.mkdir @name, :mode => 0755, :mtime => mtime
136
+
137
+ # Store the manifest in the tarball.
138
+ manifest = to_s
139
+ tar.mkdir "#{@name}/manifests", :mode => 0755, :mtime => mtime
140
+ tar.add_file_simple("#{@name}/manifests/init.pp", {
141
+ :mode => 0644,
142
+ :size => manifest.length,
143
+ :mtime => mtime
144
+ }) { |w| w.write manifest }
145
+
146
+ # Include as templates the content of any files in the module.
147
+ templates = self.templates
148
+ @manifests.each_value { |manifest| templates.merge! manifest.templates }
149
+ if 0 < templates.length
150
+ tar.mkdir "#{@name}/templates", :mode => 0755, :mtime => mtime
151
+ templates.each do |pathname, content|
152
+ dirnames = ::File.dirname(pathname).split("/")
153
+ dirnames.shift
154
+ (0..(dirnames.length - 1)).each do |i|
155
+ tar.mkdir "#{@name}/templates/#{dirnames[0..i].join("/")}",
156
+ :mode => 0755, :mtime => mtime
157
+ end
158
+ tar.add_file_simple("#{@name}/templates#{pathname}", {
159
+ :mode => 0644,
160
+ :size => content.length,
161
+ :mtime => mtime
162
+ }) { |w| w.write content }
163
+ end
164
+ end
165
+
166
+ # Return the finalized tarball.
167
+ tar.close
168
+ gz.close
169
+ io
170
+
171
+ end
172
+
107
173
  end
108
174
 
109
175
  # A Puppet resource is basically a named hash. The name is unique
@@ -113,12 +179,13 @@ module DevStructure::Puppet
113
179
  # name to determine the type, so do not instantiate `Resource`
114
180
  # directly.
115
181
  class Resource < Hash
116
- attr_accessor :type, :name, :style
182
+ attr_reader :type
183
+ attr_accessor :name, :style
117
184
 
118
185
  def initialize(name, options={})
119
186
  super nil
120
187
  clear
121
- options.each { |k, v| self[k.to_s] = v }
188
+ options.each { |k, v| self[k.to_sym] = v }
122
189
  @type = self.class.to_s.downcase[/[^:]+$/]
123
190
  @name = name && name.to_s
124
191
  @style = :complete
@@ -237,7 +304,16 @@ module DevStructure::Puppet
237
304
  # type. The most common types are `Package`, `Exec`, and `File`.
238
305
  class Package < Resource; end
239
306
  class Exec < Resource; end
240
- class File < Resource; end
307
+
308
+ # `File` resources are a special because they need their content stored
309
+ # in a template.
310
+ class File < Resource
311
+ def initialize(name, content, options={})
312
+ super name, options
313
+ @content = content
314
+ end
315
+ attr_reader :content
316
+ end
241
317
 
242
318
  # `Class` is also a useful resource type but needs a little more help
243
319
  # because of the stricter rules governing class names in the Puppet
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devstructure
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 4
8
+ - 5
9
9
  - 0
10
- version: 0.4.0
10
+ version: 0.5.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Richard Crowley
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-09-19 00:00:00 +00:00
18
+ date: 2010-10-05 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency