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

catalog[R]

Public Class Methods

configuration() click to toggle source

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
configure(hash) click to toggle source

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
new(config = {}) click to toggle source

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
puppet_type_methods() click to toggle source

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
recipe(*methods) click to toggle source

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
register_puppet_types() click to toggle source

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
register_puppet_types_for_testing() click to toggle source

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

configuration() click to toggle source

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
configuration=(hash)
Alias for: configure
configure(hash) click to toggle source

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
Also aliased as: configuration=
executable?() click to toggle source

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(force=false) click to toggle source

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!(force=false) click to toggle source

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
graph_to(name, destination) click to toggle source
# 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
missing_recipes() click to toggle source
# File lib/shadow_puppet/manifest.rb, line 232
def missing_recipes
  missing = self.class.recipes.each do |meth,args|
    !respond_to?(meth)
  end
end
name() click to toggle source
# File lib/shadow_puppet/manifest.rb, line 204
def name
  @name ||= "#{self.class}##{self.object_id}"
end
recipe(*methods) click to toggle source

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

executed?() click to toggle source

Has this manifest instance been executed?

# File lib/shadow_puppet/manifest.rb, line 292
def executed?
  @executed
end

Private Instance Methods

add_resource(type, title, params = {}) click to toggle source

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
apply() click to toggle source

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_recipes() click to toggle source

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
existing_resource(type, title) click to toggle source
# File lib/shadow_puppet/manifest.rb, line 333
def existing_resource(type, title)
  catalog.resources.detect { |r| r.type == type && r.title == title }
end
new_resource(type, title, params = {}) click to toggle source
# 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
reference(type, title, params = {}) click to toggle source

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
resource_or_reference(type, *args) click to toggle source
# 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