snappycode-shadow_puppet 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
data/README.rdoc ADDED
@@ -0,0 +1,11 @@
1
+ = ShadowPuppet
2
+
3
+ ShadowPuppet is a Ruby DSL for Puppet, extracted out of the work we at Rails
4
+ Machine are doing on Moonshine[http://blog.railsmachine.com/articles/2009/01/16/moonshine-configuration-management-and-deployment/].
5
+
6
+ ShadowPuppet provides a DSL for creating collections ("manifests") of Puppet
7
+ Resources in Ruby. For documentation on writing these manifests, please see
8
+ ShadowPuppet::Manifest.
9
+
10
+ A binary[http://railsmachine.github.com/shadow_puppet/files/bin/shadow_puppet.html] is provided to parse and execute a
11
+ ShadowPuppet::Manifest.
data/bin/shadow_puppet ADDED
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+ #== Sample manifest:
3
+ #
4
+ # $ cat examples/foo.rb
5
+ # class Foo < ShadowPuppet::Manifest
6
+ # recipe :demo, :text => 'foo'
7
+ # recipe :some_gems
8
+ #
9
+ # def some_gems
10
+ # package 'rails', :ensure => :updated, :provider => :gem
11
+ # package 'railsmachine', :ensure => '1.0.5', :provider => :gem, :require => package('capistrano')
12
+ # package 'capistrano', :ensure => :updated, :provider => :gem
13
+ # end
14
+ #
15
+ # def demo(options = {})
16
+ # exec 'sample', :command => "echo '#{options[:text]}' > /tmp/sample.txt"
17
+ # file '/tmp/sample2.txt', :ensure => :present, :content => Facter.to_hash.inspect
18
+ # end
19
+ # end
20
+ #
21
+ #== Executing this manifest:
22
+ #
23
+ # $ shadow_puppet examples/foo.rb
24
+ # notice: /shadow_puppet:19861340/Exec[foo]/returns: executed successfully
25
+ # $
26
+ #
27
+ #The shadow_puppet binary parses the given ruby code, which is
28
+ #expected to contain a class named Foo that inherits from
29
+ #ShadowPuppet::Manifest. An instance of this class is created, and the
30
+ #<tt>execute</tt> method is called. All output is printed to the console.
31
+
32
+ begin
33
+
34
+ require 'optparse'
35
+ opts = OptionParser.new do |opts|
36
+ opts.banner = <<-EOF
37
+ NAME
38
+ Shadow Puppet
39
+
40
+ AUTHOR
41
+ Jesse Newland
42
+ jesse@railsmachine.com
43
+
44
+ DESCRIPTION
45
+ A Ruby DSL for puppet
46
+
47
+ EXAMPLES
48
+ Sample manifest:
49
+
50
+ #foo.rb
51
+ class Foo < ShadowPuppet::Manifest
52
+ recipe :foo
53
+
54
+ def foo
55
+ exec :foo, :command => 'echo "foo" > /tmp/foo.txt'
56
+ file '/tmp/example.txt', :ensure => :present, :content => Facter.to_hash.inspect
57
+ end
58
+ end
59
+
60
+ Executing this manifest:
61
+
62
+ $ shadow_puppet foo.rb
63
+ notice: /shadow_puppet:19861340/Exec[foo]/returns: executed successfully
64
+ $
65
+
66
+ The shadow_puppet binary parses the given ruby code, which is
67
+ expected to contain a class named Foo that inherits from
68
+ ShadowPuppet::Manifest. An instance of this class is created, and the
69
+ execute method is called. All output is printed to the console.
70
+ EOF
71
+
72
+ end
73
+
74
+ opts.parse!
75
+
76
+ require 'rubygems'
77
+ require 'shadow_puppet'
78
+ require 'active_support/inflector'
79
+ require 'active_support/core_ext/string/inflections'
80
+ unless String.included_modules.include?(ActiveSupport::CoreExtensions::String::Inflections)
81
+ String.send :include, ActiveSupport::CoreExtensions::String::Inflections
82
+ end
83
+ require 'fileutils'
84
+
85
+ filename = ARGV[0]
86
+ raise ArgumentError unless filename
87
+
88
+ klass = File.basename(filename, ".rb")
89
+ require filename
90
+ manifest = klass.classify.constantize.new
91
+ manifest.execute! && exit(0)
92
+ rescue Errno::EACCES
93
+ puts "Please run shadow_puppet as root"
94
+ rescue Exception => e
95
+ if e.instance_of?(SystemExit)
96
+ raise
97
+ else
98
+ puts 'Uncaught exception'
99
+ puts e.class
100
+ puts e.message
101
+ puts e.backtrace.join("\n")
102
+ exit(1)
103
+ end
104
+ end
data/examples/foo.rb ADDED
@@ -0,0 +1,15 @@
1
+ class Foo < ShadowPuppet::Manifest
2
+ recipe :demo, :text => 'foo'
3
+ recipe :some_gems
4
+
5
+ def some_gems
6
+ package 'rails', :ensure => :updated, :provider => :gem
7
+ package 'railsmachine', :ensure => '1.0.5', :provider => :gem, :require => package('capistrano')
8
+ package 'capistrano', :ensure => :updated, :provider => :gem
9
+ end
10
+
11
+ def demo(options = {})
12
+ exec 'sample', :command => "echo '#{options[:text]}' > /tmp/sample.txt"
13
+ file '/tmp/sample2.txt', :ensure => :present, :content => Facter.to_hash.inspect
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ require 'puppet'
2
+ require 'erb'
3
+ require File.join(File.dirname(__FILE__) + '/shadow_puppet', 'core_ext.rb')
4
+ require File.join(File.dirname(__FILE__) + '/shadow_puppet', 'manifest.rb')
5
+
6
+ class ShadowPuppet::Manifest::Setup < ShadowPuppet::Manifest
7
+ recipe :setup_directories
8
+
9
+ def setup_directories()
10
+ if Process.uid == 0
11
+ file "/var/shadow_puppet",
12
+ :ensure => "directory",
13
+ :backup => false
14
+ file "/etc/shadow_puppet",
15
+ :ensure => "directory",
16
+ :backup => false
17
+ else
18
+ file ENV["HOME"] + "/.shadow_puppet",
19
+ :ensure => "directory",
20
+ :backup => false
21
+ file ENV["HOME"] + "/.shadow_puppet/var",
22
+ :ensure => "directory",
23
+ :backup => false,
24
+ :require => file(ENV["HOME"] + "/.shadow_puppet")
25
+ end
26
+ end
27
+ end
28
+
29
+ setup = ShadowPuppet::Manifest::Setup.new
30
+ setup.execute
@@ -0,0 +1,24 @@
1
+ require 'active_support/core_ext/class/attribute_accessors'
2
+ require 'active_support/core_ext/array'
3
+ require 'active_support/inflector'
4
+ require 'active_support/core_ext/class/inheritable_attributes'
5
+ require 'active_support/core_ext/object/duplicable'
6
+ class Hash #:nodoc:
7
+ def deep_merge(other_hash)
8
+ self.merge(other_hash) do |key, oldval, newval|
9
+ oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
10
+ newval = newval.to_hash if newval.respond_to?(:to_hash)
11
+ oldval.is_a?(Hash) && newval.is_a?(Hash) ? oldval.deep_merge(newval) : newval
12
+ end
13
+ end
14
+ def deep_merge!(other_hash)
15
+ replace(deep_merge(other_hash))
16
+ end
17
+ def deep_symbolize_keys
18
+ self.inject({}) { |result, (key, value)|
19
+ value = value.deep_symbolize_keys if value.is_a?(Hash)
20
+ result[(key.to_sym rescue key) || key] = value
21
+ result
22
+ }
23
+ end
24
+ end
@@ -0,0 +1,372 @@
1
+ module ShadowPuppet
2
+ # A Manifest is an executable collection of Puppet Resources[http://reductivelabs.com/trac/puppet/wiki/TypeReference].
3
+ #
4
+ # ===Example
5
+ #
6
+ # class ManifestExample < ShadowPuppet::Manifest
7
+ # recipe :sample
8
+ # recipe :lamp, :ruby # queue calls to self.lamp and
9
+ # # self.ruby when executing
10
+ #
11
+ # recipe :mysql, { # queue a call to self.mysql
12
+ # :root_password => 'OMGSEKRET' # passing the provided hash
13
+ # } # as an option
14
+ #
15
+ # def sample
16
+ # exec :foo, :command => 'echo "foo" > /tmp/foo.txt'
17
+ #
18
+ # package :foo, :ensure => :installed
19
+ #
20
+ # file '/tmp/example.txt',
21
+ # :ensure => :present,
22
+ # :contents => Facter.to_hash_inspect,
23
+ # :require => package(:foo)
24
+ # end
25
+ #
26
+ # def lamp
27
+ # #install a basic LAMP stack
28
+ # end
29
+ #
30
+ # def ruby
31
+ # #install a ruby interpreter and tools
32
+ # end
33
+ #
34
+ # def mysql(options)
35
+ # #install a mysql server and set the root password to options[:root_password]
36
+ # end
37
+ #
38
+ # end
39
+ #
40
+ # To execute the above manifest, instantiate it and call execute on it:
41
+ #
42
+ # m = ManifestExample.new
43
+ # m.execute
44
+ #
45
+ # As shown in the +sample+ method in ManifestExample above, instance
46
+ # methods are created for each Puppet::Type available on your system. These
47
+ # methods behave identally to the Puppet Resources methods. See here[http://reductivelabs.com/trac/puppet/wiki/TypeReference]
48
+ # for documentation on these methods.
49
+ #
50
+ # To view a list of all defined methods on your system, run:
51
+ #
52
+ # ruby -rubygems -e 'require "shadow_puppet";puts ShadowPuppet::Manifest.puppet_type_methods'
53
+ #
54
+ # The use of methods (+sample+, +lamp+, +ruby+, and +mysql+ above) as a
55
+ # container for resources facilitates recipie re-use through the use of Ruby
56
+ # Modules. For example:
57
+ #
58
+ # module ApachePuppet
59
+ # # Required options:
60
+ # # domain
61
+ # # path
62
+ # def php_vhost(options)
63
+ # #...
64
+ # end
65
+ # end
66
+ #
67
+ # class MyWebMainfest < ShadowPuppet::Manifest
68
+ # include ApachePuppet
69
+ # recipe :php_vhost, {
70
+ # :domain => 'foo.com',
71
+ # :path => '/var/www/apps/foo'
72
+ # }
73
+ # end
74
+ class Manifest
75
+
76
+ class_inheritable_accessor :recipes
77
+ write_inheritable_attribute(:recipes, [])
78
+ attr_reader :puppet_resources
79
+ class_inheritable_accessor :__config__
80
+ write_inheritable_attribute(:__config__, Hash.new)
81
+
82
+ # Initialize a new instance of this manifest. This can take a
83
+ # config hash, which is immediately passed on to the configure
84
+ # method
85
+ def initialize(config = {})
86
+ if Process.uid == 0
87
+ Puppet[:confdir] = File.expand_path("/etc/shadow_puppet")
88
+ Puppet[:vardir] = File.expand_path("/var/shadow_puppet")
89
+ else
90
+ Puppet[:confdir] = File.expand_path("~/.shadow_puppet")
91
+ Puppet[:vardir] = File.expand_path("~/.shadow_puppet/var")
92
+ end
93
+ Puppet[:user] = Process.uid
94
+ Puppet[:group] = Process.gid
95
+ Puppet::Util::Log.newdestination(:console)
96
+
97
+ configure(config)
98
+ @executed = false
99
+ @puppet_resources = Hash.new do |hash, key|
100
+ hash[key] = {}
101
+ end
102
+ end
103
+
104
+ # Declares that the named method or methods will be called whenever
105
+ # execute is called on an instance of this class. If the last argument is
106
+ # a Hash, this hash is passed as an argument to all provided methods.
107
+ # If no options hash is provided, each method is passed the contents of
108
+ # <tt>configuration[method]</tt>.
109
+ #
110
+ # Subclasses of the Manifest class properly inherit the parent classes'
111
+ # calls to recipe.
112
+ def self.recipe(*methods)
113
+ return nil if methods.nil? || methods == []
114
+ options = methods.extract_options!
115
+ methods.each do |meth|
116
+ options = configuration[meth.to_sym] if options == {}
117
+ options ||= {}
118
+ recipes << [meth.to_sym, options]
119
+ end
120
+ end
121
+
122
+ # A hash describing any configuration that has been
123
+ # performed on the class. Modify this hash by calling configure:
124
+ #
125
+ # class SampleManifest < ShadowPuppet::Manifest
126
+ # configure(:name => 'test')
127
+ # end
128
+ #
129
+ # >> SampleManifest.configuration
130
+ # => {:name => 'test'}
131
+ #
132
+ # All keys on this hash are coerced into symbols for ease of access.
133
+ #
134
+ # Subclasses of the Manifest class properly inherit the parent classes'
135
+ # configuration.
136
+ def self.configuration
137
+ __config__.deep_symbolize_keys
138
+ end
139
+
140
+ # Access to the configuration of the class of this instance.
141
+ #
142
+ # class SampleManifest < ShadowPuppet::Manifest
143
+ # configure(:name => 'test')
144
+ # end
145
+ #
146
+ # @manifest = SampleManifest.new
147
+ # @manifest.configuration[:name] => "test"
148
+ def configuration
149
+ self.class.configuration
150
+ end
151
+
152
+ # Define configuration on this manifest. This is useful for storing things
153
+ # such as hostnames, password, or usernames that may change between
154
+ # different implementations of a shared manifest. Access this hash by
155
+ # calling <tt>configuration</tt>:
156
+ #
157
+ # class SampleManifest < ShadowPuppet::Manifest
158
+ # configure('name' => 'test')
159
+ # end
160
+ #
161
+ # >> SampleManifest.configuration
162
+ # => {:name => 'test'}
163
+ #
164
+ # All keys on this hash are coerced into symbols for ease of access.
165
+ #
166
+ # Subsequent calls to configure perform a deep_merge of the provided
167
+ # <tt>hash</tt> into the pre-existing configuration.
168
+ def self.configure(hash)
169
+ __config__.deep_merge!(hash)
170
+ end
171
+
172
+ # Update the configuration of this manifest instance's class.
173
+ #
174
+ # class SampleManifest < ShadowPuppet::Manifest
175
+ # configure({})
176
+ # end
177
+ #
178
+ # @manifest = SampleManifest.new
179
+ # @manifest.configure(:name => "test")
180
+ # @manifest.configuration[:name] => "test"
181
+ def configure(hash)
182
+ self.class.configure(hash)
183
+ end
184
+ alias_method :configuration=, :configure
185
+
186
+ #An array of all methods defined for creation of Puppet Resources
187
+ def self.puppet_type_methods
188
+ Puppet::Type.eachtype { |t| t.name }.keys.map { |n| n.to_s }.sort.inspect
189
+ end
190
+
191
+ def name
192
+ @name ||= "#{self.class}##{self.object_id}"
193
+ end
194
+
195
+ #Create an instance method for every type that either creates or references
196
+ #a resource
197
+ def self.register_puppet_types
198
+ Puppet::Type.loadall
199
+ Puppet::Type.eachtype do |type|
200
+ #remove the method rdoc placeholders
201
+ remove_method(type.name) rescue nil
202
+ define_method(type.name) do |*args|
203
+ if args && args.flatten.size == 1
204
+ reference(type.name, args.first)
205
+ else
206
+ new_resource(type, args.first, args.last)
207
+ end
208
+ end
209
+ end
210
+ end
211
+ register_puppet_types
212
+
213
+ # Returns true if this Manifest <tt>respond_to?</tt> all methods named by
214
+ # calls to recipe, and if this Manifest has not been executed before.
215
+ def executable?
216
+ self.class.recipes.each do |meth,args|
217
+ return false unless respond_to?(meth)
218
+ end
219
+ return false if executed?
220
+ true
221
+ end
222
+
223
+ def missing_recipes
224
+ missing = self.class.recipes.each do |meth,args|
225
+ !respond_to?(meth)
226
+ end
227
+ end
228
+
229
+ # Execute this manifest, applying all resources defined. Execute returns
230
+ # true if successfull, and false if unsucessfull. By default, this
231
+ # will only execute a manifest that has not already been executed?.
232
+ # The +force+ argument, if true, removes this check.
233
+ def execute(force=false)
234
+ return false if executed? && !force
235
+ evaluate_recipes
236
+ apply
237
+ rescue Exception => e
238
+ false
239
+ else
240
+ true
241
+ ensure
242
+ @executed = true
243
+ end
244
+
245
+ # Execute this manifest, applying all resources defined. Execute returns
246
+ # true if successfull, and raises an exception if not. By default, this
247
+ # will only execute a manifest that has not already been executed?.
248
+ # The +force+ argument, if true, removes this check.
249
+ def execute!(force=false)
250
+ return false if executed? && !force
251
+ evaluate_recipes
252
+ apply
253
+ rescue Exception => e
254
+ raise e
255
+ else
256
+ true
257
+ ensure
258
+ @executed = true
259
+ end
260
+
261
+ protected
262
+
263
+ #Has this manifest instance been executed?
264
+ def executed?
265
+ @executed
266
+ end
267
+
268
+ #An Array of all currently defined resources.
269
+ def flat_resources
270
+ a = []
271
+ @puppet_resources.each_value do |by_type|
272
+ by_type.each_value do |by_name|
273
+ a << by_name
274
+ end
275
+ end
276
+ a
277
+ end
278
+
279
+ #A Puppet::TransBucket of all defined resoureces.
280
+ def export
281
+ transportable_objects = flat_resources.dup.reject { |a| a.nil? }.flatten.collect do |obj|
282
+ obj.to_trans
283
+ end
284
+ b = Puppet::TransBucket.new(transportable_objects)
285
+ b.name = name
286
+ b.type = "class"
287
+
288
+ return b
289
+ end
290
+
291
+ private
292
+
293
+ #Evaluate the methods calls queued in self.recipes
294
+ def evaluate_recipes
295
+ self.class.recipes.each do |meth, args|
296
+ case arity = method(meth).arity
297
+ when 1, -1
298
+ send(meth, args)
299
+ else
300
+ send(meth)
301
+ end
302
+ end
303
+ end
304
+
305
+ # Create a catalog of all contained Puppet Resources and apply that
306
+ # catalog to the currently running system
307
+ def apply(bucket = nil)
308
+ bucket ||= export()
309
+ catalog = bucket.to_catalog
310
+ catalog.apply
311
+ catalog.clear
312
+ end
313
+
314
+ def scope #:nodoc:
315
+ unless defined?(@scope)
316
+ # Set the code to something innocuous; we just need the
317
+ # scopes, not the interpreter. Hackish, but true.
318
+ Puppet[:code] = " "
319
+ @interp = Puppet::Parser::Interpreter.new
320
+ require 'puppet/node'
321
+ @node = Puppet::Node.new(Facter.value(:hostname))
322
+ if env = Puppet[:environment] and env == ""
323
+ env = nil
324
+ end
325
+ @node.parameters = Facter.to_hash
326
+ @compile = Puppet::Parser::Compiler.new(@node, @interp.send(:parser, env))
327
+ @scope = @compile.topscope
328
+ end
329
+ @scope
330
+ end
331
+
332
+ #Create a reference to another Puppet Resource.
333
+ def reference(type, title)
334
+ Puppet::Parser::Resource::Reference.new(:type => type.to_s, :title => title.to_s, :scope => scope)
335
+ end
336
+
337
+ #Creates a new Puppet Resource.
338
+ def new_resource(type, name, params = {})
339
+ unless obj = @puppet_resources[type][name]
340
+ obj = Puppet::Parser::Resource.new(
341
+ :title => name,
342
+ :type => type.name,
343
+ :source => self,
344
+ :scope => scope
345
+ )
346
+ @puppet_resources[type][name] = obj
347
+ end
348
+
349
+ case type.name
350
+ when :exec
351
+ param = Puppet::Parser::Resource::Param.new(
352
+ :name => :path,
353
+ :value => ENV["PATH"],
354
+ :source => self
355
+ )
356
+ obj.send(:set_parameter, param)
357
+ end
358
+
359
+ params.each do |param_name, param_value|
360
+ param = Puppet::Parser::Resource::Param.new(
361
+ :name => param_name,
362
+ :value => param_value,
363
+ :source => self
364
+ )
365
+ obj.send(:set_parameter, param)
366
+ end
367
+
368
+ obj
369
+ end
370
+
371
+ end
372
+ end
@@ -0,0 +1,69 @@
1
+ module Puppet
2
+ module Parser
3
+ class Resource
4
+
5
+ # clearing out some puppet methods that we probably won't need for testing
6
+ # that are also used in the params hash when defining the resource.
7
+ undef path
8
+ undef source
9
+ undef require
10
+
11
+ # This allows access to resource options as methods on the resource.
12
+ def method_missing name, *args
13
+ if params.keys.include? name.to_sym
14
+ params[name.to_sym].value
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ module ShadowPuppet
22
+ # To test manifests, access puppet resources using the plural form of the resource name.
23
+ # This returns a hash of all resources of that type.
24
+ #
25
+ # manifest.execs
26
+ # manifest.packages
27
+ #
28
+ # You can access resource options as methods on the resource
29
+ #
30
+ # manifest.files['/etc/motd'].content
31
+ # manifest.execs['service ssh restart'].onlyif
32
+ #
33
+ # ===Example
34
+ #
35
+ # Given this manifest:
36
+ #
37
+ # class TestManifest < ShadowPuppet::Manifest
38
+ # def myrecipe
39
+ # file '/etc/motd', :content => 'Welcome to the machine!', :mode => '644'
40
+ # exec 'newaliases', :refreshonly => true
41
+ # end
42
+ # recipe :myrecipe
43
+ # end
44
+ #
45
+ # A test for the manifest could look like this:
46
+ #
47
+ # manifest = TestManifest.new
48
+ # manifest.myrecipe
49
+ # assert_match /Welcome/, manifest.files['/etc/motd']
50
+ # assert manifest.execs['newaliases'].refreshonly
51
+ #
52
+ class Manifest
53
+ # Creates an instance method for every puppet type
54
+ # that either creates or references a resource
55
+ def self.register_puppet_types_for_testing
56
+ Puppet::Type.loadall
57
+ Puppet::Type.eachtype do |type|
58
+ plural_type = type.name.to_s.downcase.pluralize
59
+ #undefine the method rdoc placeholders
60
+ undef_method(plural_type) rescue nil
61
+ define_method(plural_type) do |*args|
62
+ puppet_resources[type]
63
+ end
64
+ end
65
+ end
66
+ register_puppet_types_for_testing
67
+
68
+ end
69
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snappycode-shadow_puppet
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 2
9
+ version: 0.3.2
10
+ platform: ruby
11
+ authors:
12
+ - Jesse Newland
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2009-12-15 00:00:00 +00:00
18
+ default_executable: shadow_puppet
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: puppet
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 24
30
+ - 8
31
+ version: 0.24.8
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: facter
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 1
43
+ - 5
44
+ - 4
45
+ version: 1.5.4
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: highline
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 1
57
+ - 5
58
+ - 0
59
+ version: 1.5.0
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: builder
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 2
71
+ - 1
72
+ - 2
73
+ version: 2.1.2
74
+ type: :runtime
75
+ version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: activesupport
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ segments:
84
+ - 2
85
+ - 0
86
+ - 0
87
+ version: 2.0.0
88
+ type: :runtime
89
+ version_requirements: *id005
90
+ description: A Ruby Puppet DSL
91
+ email:
92
+ - jesse@railsmachine.com
93
+ executables:
94
+ - shadow_puppet
95
+ extensions: []
96
+
97
+ extra_rdoc_files:
98
+ - README.rdoc
99
+ - bin/shadow_puppet
100
+ files:
101
+ - README.rdoc
102
+ - LICENSE
103
+ - bin/shadow_puppet
104
+ - examples/foo.rb
105
+ - lib/shadow_puppet.rb
106
+ - lib/shadow_puppet/manifest.rb
107
+ - lib/shadow_puppet/core_ext.rb
108
+ - lib/shadow_puppet/test.rb
109
+ has_rdoc: true
110
+ homepage: http://railsmachine.github.com/shadow_puppet
111
+ licenses: []
112
+
113
+ post_install_message:
114
+ rdoc_options:
115
+ - --main
116
+ - README.rdoc
117
+ - --inline-source
118
+ - --webcvs=http://github.com/railsmachine/shadow_puppet/tree/master/
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ segments:
126
+ - 0
127
+ version: "0"
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ segments:
133
+ - 0
134
+ version: "0"
135
+ requirements: []
136
+
137
+ rubyforge_project: moonshine
138
+ rubygems_version: 1.3.6
139
+ signing_key:
140
+ specification_version: 2
141
+ summary: A Ruby Puppet DSL
142
+ test_files: []
143
+