class ShadowPuppet::Manifest
A Manifest
is an executable collection of Puppet
Resources.
Example¶ ↑
class ManifestExample < ShadowPuppet::Manifest recipe :sample recipe :lamp, :ruby # queue calls to self.lamp and # self.ruby when executing recipe :mysql, { # queue a call to self.mysql :root_password => 'OMGSEKRET' # passing the provided hash } # as an option def sample exec :foo, :command => 'echo "foo" > /tmp/foo.txt' package :foo, :ensure => :installed file '/tmp/example.txt', :ensure => :present, :contents => Facter.to_hash_inspect, :require => package(:foo) end def lamp # install a basic LAMP stack end def ruby # install a ruby interpreter and tools end def mysql(options) # install a mysql server and set the root password to options[:root_password] end end
To execute the above manifest, instantiate it and call execute on it:
m = ManifestExample.new m.execute
As shown in the sample
method in ManifestExample above, instance methods are created for each Puppet::Type
available on your system. These methods behave identally to the Puppet
Resources methods. See here for documentation on these methods.
To view a list of all defined methods on your system, run:
ruby -rubygems -e 'require "shadow_puppet";puts ShadowPuppet::Manifest.puppet_type_methods'
The use of methods (sample
, lamp
, ruby
, and mysql
above) as a container for resources facilitates recipie re-use through the use of Ruby Modules. For example:
module ApachePuppet # Required options: # domain # path def php_vhost(options) #... end end class MyWebMainfest < ShadowPuppet::Manifest include ApachePuppet recipe :php_vhost, { :domain => 'foo.com', :path => '/var/www/apps/foo' } end
To test manifests, access puppet resources using the plural form of the resource name. This returns a hash of all resources of that type.
manifest.execs manifest.packages
You can access resource options as methods on the resource
manifest.files['/etc/motd'].content manifest.execs['service ssh restart'].onlyif
Example¶ ↑
Given this manifest:
class TestManifest < ShadowPuppet::Manifest def myrecipe file '/etc/motd', :content => 'Welcome to the machine!', :mode => '644' exec 'newaliases', :refreshonly => true end recipe :myrecipe end
A test for the manifest could look like this:
manifest = TestManifest.new manifest.myrecipe assert_match /Welcome/, manifest.files['/etc/motd'] assert manifest.execs['newaliases'].refreshonly
Attributes
Public Class Methods
A HashWithIndifferentAccess describing any configuration that has been performed on the class. Modify this hash by calling configure:
class SampleManifest < ShadowPuppet::Manifest configure(:name => 'test') end >> SampleManifest.configuration => {:name => 'test'} #
Subclasses of the Manifest
class properly inherit the parent classes' configuration.
# File lib/shadow_puppet/manifest.rb, line 151 def self.configuration __config__.with_indifferent_access end
Define configuration on this manifest. This is useful for storing things such as hostnames, password, or usernames that may change between different implementations of a shared manifest. Access this hash by calling configuration
:
class SampleManifest < ShadowPuppet::Manifest configure('name' => 'test') end >> SampleManifest.configuration => {:name => 'test'} #
Subsequent calls to configure perform a deep_merge of the provided hash
into the pre-existing configuration.
# File lib/shadow_puppet/manifest.rb, line 181 def self.configure(hash) __config__.replace(__config__.deep_symbolize_keys.deep_merge(hash.deep_symbolize_keys)) end
Initialize a new instance of this manifest. This can take a config hash, which is immediately passed on to the configure method
# File lib/shadow_puppet/manifest.rb, line 85 def initialize(config = {}) if Process.uid == 0 Puppet[:confdir] = File.expand_path("/etc/shadow_puppet") Puppet[:vardir] = File.expand_path("/var/shadow_puppet") Puppet[:codedir] = File.expand_path("/etc/shadow_puppet/code") Puppet[:logdir] = File.expand_path("/var/log/shadow_puppet") else Puppet[:confdir] = File.expand_path("~/.shadow_puppet") Puppet[:vardir] = File.expand_path("~/.shadow_puppet/var") Puppet[:codedir] = File.expand_path("~/.shadow_puppet/code") Puppet[:logdir] = File.expand_path("~/shadow_puppet/log") end Puppet[:user] = Process.uid Puppet[:group] = Process.gid Puppet::Util::Log.newdestination(:console) Puppet[:diff_args] = "-u" Puppet.push_context(Puppet.base_context(Puppet.settings), "Update for application's settings") configure(config) @executed = false @catalog = Puppet::Resource::Catalog.new @catalog.host_config = false @catalog.name = self.name end
An array of all methods defined for creation of Puppet
Resources
# File lib/shadow_puppet/manifest.rb, line 200 def self.puppet_type_methods Puppet::Type.eachtype { |t| t.name }.keys.map { |n| n.to_s }.sort.inspect end
Declares that the named method or methods will be called whenever execute is called on an instance of this class. If the last argument is a Hash
, this hash is passed as an argument to all provided methods. If no options hash is provided, each method is passed the contents of configuration[method]
.
Subclasses of the Manifest
class properly inherit the parent classes' calls to recipe.
# File lib/shadow_puppet/manifest.rb, line 118 def self.recipe(*methods) return nil if methods.nil? || methods == [] # TODO can probably replace with if methods.blank? methods.each do |meth| options = methods.extract_options! options = configuration[meth.to_sym] if options == {} # TODO can probably be replaced with options.blank? options ||= {} recipes << [meth.to_sym, options] end end
Create an instance method for every type that either creates or references a resource
# File lib/shadow_puppet/manifest.rb, line 210 def self.register_puppet_types Puppet::Type.loadall Puppet::Type.eachtype do |type| # remove the method rdoc placeholders remove_method(type.name) rescue nil define_method(type.name) do |*args| resource_or_reference(type, *args) end end end
Creates an instance method for every puppet type that either creates or references a resource
# File lib/shadow_puppet/test.rb, line 52 def self.register_puppet_types_for_testing Puppet::Type.loadall Puppet::Type.eachtype do |type| plural_type = type.name.to_s.downcase.pluralize #undefine the method rdoc placeholders undef_method(plural_type) rescue nil define_method(plural_type) do |*args| catalog.resources.select { |r| r.type == type.name }.inject({}) do |hash, resource| hash[resource.title] = resource; hash end end end end
Public Instance Methods
Access to the configuration of the class of this instance.
class SampleManifest < ShadowPuppet::Manifest configure(:name => 'test') end @manifest = SampleManifest.new @manifest.configuration[:name] => "test"
# File lib/shadow_puppet/manifest.rb, line 163 def configuration self.class.configuration end
Update the configuration of this manifest instance's class.
class SampleManifest < ShadowPuppet::Manifest configure({}) end @manifest = SampleManifest.new @manifest.configure(:name => "test") @manifest.configuration[:name] => "test"
# File lib/shadow_puppet/manifest.rb, line 194 def configure(hash) self.class.configure(hash) end
Returns true if this Manifest
respond_to?
all methods named by calls to recipe, and if this Manifest
has not been executed before.
# File lib/shadow_puppet/manifest.rb, line 224 def executable? self.class.recipes.each do |meth,args| return false unless respond_to?(meth) end return false if executed? true end
Execute this manifest, applying all resources defined. Execute returns true if successfull, and false if unsucessfull. By default, this will only execute a manifest that has not already been executed?. The force
argument, if true, removes this check.
# File lib/shadow_puppet/manifest.rb, line 242 def execute(force=false) return false if executed? && !force evaluate_recipes transaction = apply rescue Exception => e false else not transaction.any_failed? ensure @executed = true end
Execute this manifest, applying all resources defined. Execute returns true if successfull, and raises an exception if not. By default, this will only execute a manifest that has not already been executed?. The force
argument, if true, removes this check.
# File lib/shadow_puppet/manifest.rb, line 258 def execute!(force=false) return false if executed? && !force evaluate_recipes transaction = apply rescue Exception => e raise e else not transaction.any_failed? ensure @executed = true end
# File lib/shadow_puppet/manifest.rb, line 270 def graph_to(name, destination) evaluate_recipes relationship_graph = @catalog.relationship_graph graph = relationship_graph.to_dot_graph("name" => "#{name} Relationships".gsub(/\W+/, '_')) graph.options['label'] = "#{name} Relationships" # The graph ends up having all of the edges backwards graph.each_node do |node| next unless node.is_a?(DOT::DOTEdge) node.to, node.from = node.from, node.to end File.open(destination, "w") { |f| f.puts graph.to_s } end
# File lib/shadow_puppet/manifest.rb, line 232 def missing_recipes missing = self.class.recipes.each do |meth,args| !respond_to?(meth) end end
# File lib/shadow_puppet/manifest.rb, line 204 def name @name ||= "#{self.class}##{self.object_id}" end
Access to a recipe of the class of this instance.
class SampleManifest < ShadowPuppet::Manifest def my_recipe recipe :other_recipe end end
# File lib/shadow_puppet/manifest.rb, line 135 def recipe(*methods) self.class.recipe *methods end
Protected Instance Methods
Has this manifest instance been executed?
# File lib/shadow_puppet/manifest.rb, line 292 def executed? @executed end
Private Instance Methods
Creates a new Puppet
Resource and adds it to the Catalog.
# File lib/shadow_puppet/manifest.rb, line 343 def add_resource(type, title, params = {}) catalog.add_resource(new_resource(type, title, params)) end
Create a catalog of all contained Puppet
Resources and apply that catalog to the currently running system
# File lib/shadow_puppet/manifest.rb, line 312 def apply transaction = catalog.apply catalog.clear transaction end
Evaluate the methods calls queued in self.recipes
# File lib/shadow_puppet/manifest.rb, line 299 def evaluate_recipes self.class.recipes.each do |meth, args| case arity = method(meth).arity when 1, -1 send(meth, args) else send(meth) end end end
# File lib/shadow_puppet/manifest.rb, line 333 def existing_resource(type, title) catalog.resources.detect { |r| r.type == type && r.title == title } end
# File lib/shadow_puppet/manifest.rb, line 347 def new_resource(type, title, params = {}) params.merge!({:title => title.to_s}) params.merge!({:catalog => catalog}) params.merge!({:path => ENV["PATH"]}) if type.name == :exec && params[:path].nil? params.merge!({:cwd => params[:cwd].to_s}) if params[:cwd] Puppet::Type.type(type.name).new(params) end
Create a reference to another Puppet
Resource.
# File lib/shadow_puppet/manifest.rb, line 338 def reference(type, title, params = {}) Puppet::Resource.new(type.name.to_s.capitalize, title.to_s) end
# File lib/shadow_puppet/manifest.rb, line 318 def resource_or_reference(type, *args) if args if args.flatten.size == 1 reference(type, args.first) elsif resource = existing_resource(type.name, args.first) new_resource(type, args.first, args.last).parameters.each do |name, param| resource[name] = param.value if param.value end resource else add_resource(type, args.first, args.last) end end end