Skip to content

Build a complex puppet custom type and provider, the easy way

License

Notifications You must be signed in to change notification settings

CDLSoftware/easy_type

 
 

Repository files navigation

Code Climate Build Status Dependency Status Coverage Status Inline docs

#easy_type

Robert scratched his head. How would he get a Puppet class to manage a complex resource on his systems? I guess I’ll have to make a Custom Type, he thought. But last time I looked into that, I noticed you need to know a lot about Puppet Internals.

If you recognize this thought process, easy_type is for you. Like the name says, easy type is designed to make it easy to build a Custom Puppet Type.

##Tutorial Check this blog post for a tutorial on using easy_type.

##Documentation Check the rdoc documentation for easy_type

##Get Started To get started, you first need to include easy_type in your Puppetfile or otherwise get it into your puppet directories. To add it to your Puppetfile, you can add the following line:

mod "hajee/easy_type", “0.x.0

Change the x to the version you would like. You can also use the latest from git.

mod  easy_type, :git => "git@github.com:hajee/easy_type.git"

After that run the librarian to add the right modules to your puppet tree:

librarian-puppet install

#Creating your type

To create your custom type you will have to create the right directory structure. Go to your module directory and create the next directories:

mkdir -p module_name/lib

To create a good starting point for defining your type, copy the scaffold

cp -Rv easy_type/scaffold module_name/lib/puppet

Start editing the example_type.rb file

Here is it's content:

require 'easy_type'

module Puppet
  newtype(:example_type) do
    include EasyType

    ensurable
    #
    # Use set_command to set the base command given on creating, destroying and modifying 
    # the resource. This can either be an existing class method on the example_type, or if 
    # a method doesn't exist, it will translate to an os command given.
    # 
    # Example: 
    #   def self.an_existing_class_method(commmand_string)
    #     # DO some important stuff
    #   end
    #
    # set_command(:an_existing_class_method)
    # 
    #
    set_command(:just_a_method)



    to_get_raw_resources do
      #
      # to_get_raw_resources
      # =====================
      # Fill in the code needed to get an array of resources. The array must contain Hashes
      # the Hash can have arbitrary elements. The Hash will be 'picked' by the `to_translate_to_resource`.
      # See the definition of `to_translate_to_resource` in either the parameter or property definition.
      #
      # Although technically not necessary, it is logical to use the defined command. (See set_command)
      #
      # Example:
      #
      # to_get_raw_resources do
      #   packages_string = rpm('-qa','--qf','%{NAME}, %{VERSION}-%{RELEASE}\n')
      #   convert_csv_data_to_hash(packages_string,[:name, :version])
      # end
      #
      # The convert_csv_data_to_hash, is a helper. Like the name says, it converts a comma separated string 
      # to a Hash, The second argument is an Array that contains the elements of the hash. If your string 
      # a header as the first line, you can pass nil for the header Array.
      #
    end

    on_create do
      #
      # on_create
      # =========
      # When Puppet signals it needs to create the resource, it will call this method. The return value of 
      # this method is appended to the command given in set_command
      #
      # Example:
      # --------
      # 
      # do_command(:sql)
      #
      # on_create do
      #    "create user #{self[:name]}"
      # end
      #
      # Explanation:
      # ------------
      # When Puppet needs to create the resource, the sql method is called with parameter "create user username"
      # The `on_create` method will be called in the context of a `provider`. This means you can reference the current
      # resource through `self`.
      # 
      # Property information
      # ------------------------------
      # If you have defined an `on_apply` method in any property or parameter, it's return value will be 
      # appended to the base `on_create` command. See the description of `on_apply` at the type or the property
      #
      #
    end

    on_modify do
      #
      # on_modify
      # =========
      # When Puppet signals it needs to modify the resource, it will call this method. The return value of 
      # this method is appended to the command given in set_command
      #
      # Example:
      # --------
      # 
      # do_command(:sql)
      #
      # on_modify do
      #    "alter user #{self[:name]}"
      # end
      #
      # Explanation
      # ------------
      # When Puppet needs to modify the resource, the sql method is called with parameter "alter user username"
      # The `on_modify` method will be called in the context of a `provider`. This means you can reference the current
      # resource through `self`.
      #
      # Property information
      # ---------------------
      # If you have defined an `on_apply` method in any property or parameter, it's return value will be 
      # appended to the base `on_modify` command. See the description of `on_apply` at the type or the property
      #
      #
    end

    on_destroy do
      #
      # on_destroy
      # ==========
      # When Puppet signals it needs to destroy the resource, it will call this method. The return value of 
      # this method is appended to the command given in set_command
      #
      # Example:
      # --------
      # 
      # do_command(:sql)
      #
      # on_destroy do
      #    "drop user #{self[:name]}"
      # end
      #
      # Explanation
      # -----------
      # When Puppet needs to destroy the resource, the sql method is called with parameter "drop user username"
      # The `on_dstroy` method will be called in the context of a `provider`. This means you can reference the current
      # resource through `self`.
      #
      # Property information
      # --------------------
      # in contrast to `on_create` and `on_modify`, no `on_apply` methods are called on any of the types
      #
      #
    end

    newparam(:name) do
      include EasyType
      #
      # newparam
      # ==========
      # To define a parameter, use the regular newparam(:parameter_name). 
      #
      # Mungers
      # =======
      # If your parameter needs munging, you can include the necessary mungers. Check the documentation 
      # of the Mungers to see which mungers are available
      #
      # Example:
      # --------
      #
      # include EasyType::Mungers::Upcase   # Check easy_type/validators for available mungers
      # 
      # Explanation
      # -----------
      # This will include the Upcase munger. This will change any input in the Puppet Manifest
      # to an Uppercase value before comparing it with the actual value.
      #
      # Validators
      # ==========
      # If your parameter needs validation, you can include the necessary validator. Check the documentation 
      # of the Validators to see which validators are available
      #
      # Example:
      # --------
      #
      # include EasyType::Validators::Name  # Check easy_type/validators for available validators
      # 
      # Explanation
      # -----------
      # This will check if the content of the parameter is a valid name.


      desc "Give you desciption of the parameter"

      isnamevar

      to_translate_to_resource do | raw_resource|
        #raw_resource.column_data('FILL_YOUR_PARAMETER_KEY_HERE')
      end

      #
      # to_translate_to_resource
      # ========================
      # Use this method to pick a part for the raw_resource hash. and translate it to the real resource hash
      # If you have used the `convert_csv_data_to_hash` method to create the Hash, you can use the 
      # `column_data` method to pick the right element from the Hash. `column_data` will show an error 
      # when the data is not available in the Hash.
      #
      # Example:
      # --------
      #
      # to_translate_to_resource do | raw_resource|
      #   raw_resource.column_data('USERNAME').upcase
      # end
      #
      # Explanation:
      # ------------
      #
      # This will extract the `USERNAME` from the `raw_resource` Hash and translate it to an uppercase 
      # value. let's say, the Hash contains {'USERNAME` => 'micky_mouse`}. This would lead to the 
      # following Puppet Resource
      #
      # example_type{micky_mouse: ensure => present,}
      #
      # Tricky stuff
      # ------------
      # Check what the keys of the Hash are. There **is** a difference between 'key', 'KEY', :KEY and :key 
      #
    end


    #
    # parameter
    # ==========
    # You can also use a shortcut:
    #
    # Example:
    # --------
    #   parameter :parameter_name
    #
    # Explanation
    # -----------
    # In this case, easy_type looks for a file 
    # `module_name\lib\puppet\type\type_name\parameter_name.rb`
    # This file *MUST* contain the full parameter definition
    #
    parameter :just_an_other_parameter

    newproperty(:your_property) do
      include EasyType
      #
      # newproperty
      # ==========
      # To define a property, use the regular newproperty(:property_name). 
      #
      #
      # Mungers
      # =======
      # If your property needs munging, you can include the necessary mungers. Check the documentation 
      # of the Mungers to see which mungers are available
      #
      # Example:
      # --------
      #
      # include EasyType::Mungers::Upcase   # Check easy_type/validators for available mungers
      # 
      # Explanation
      # -----------
      # This will include the Upcase munger. This will change any input in the Puppet Manifest
      # to an Uppercase value before comparing it with the actual value.
      #
      # Validators
      # ==========
      # If your property needs validation, you can include the necessary validator. Check the documentation 
      # of the Validators to see which validators are available
      #
      # Example:
      # --------
      #
      # include EasyType::Validators::Name  # Check easy_type/validators for available validators
      # 
      # Explanation
      # -----------
      # This will check if the content of the property is a valid name.

      desc "Give your desciption of the property"

      #
      # to_translate_to_resource
      # ========================
      # Use this method to pick a part for the raw_resource hash. and translate it to the real resource hash
      # If you have used the `convert_csv_data_to_hash` method to create the Hash, you can use the 
      # `column_data` method to pick the right element from the Hash. `column_data` will show an error 
      # when the data is not available in the Hash.
      #
      # Example:
      # --------
      #
      # to_translate_to_resource do | raw_resource|
      #   raw_resource.column_data('USERNAME').upcase
      # end
      #
      # Explanation:
      # ------------
      #
      # This will extract the `USERNAME` from the `raw_resource` Hash and translate it to an uppercase 
      # value. let's say, the Hash contains {'USERNAME` => 'micky_mouse`}. This would lead to the 
      # follwoing Puppet Resource
      #
      # example_type{micky_mouse: ensure => present,}
      #
      # Tricky stuff
      # ------------
      # Check what the keys of the Hash are. There **is** a difference between 'key', 'KEY', :KEY and :key 
      #
      to_translate_to_resource do | raw_resource|
        #raw_resource.column_data('FILL_YOUR_PROPERTY_KEY_HERE')
      end


      #
      # on_apply
      # ========
      # When Puppet signals it needs to create or modify the resource, it will call this method for every modified
      # property. It will then append the return value to the existing string from ether `on_create` or `on_modify`
      #
      # Example:
      # --------
      # in the type
      # 
      #  on_command(:data_source)
      #
      #  on_modify do
      #    "alter #{self[:name]}"
      #  end
      #    
      # 
      #  in the property
      #
      #  on_apply do | command_builder |
      #    "set destination #{resource[:destination]} "
      #  end
      #
      # Explanation
      # ------------
      # When Puppet needs to modify the resource, the `data_source` method is called with parameter "alter my_name"
      # The `on_apply` method will append the text "set destination /dev/null" to the string.
      #
      # While the `on_create`, `on_destroy` and `on_modify` methods are called in the context of the
      # provider, the `on_apply` method is called in the context of the property.
      #
      on_apply do
        "add_your_on_apply_information_for_this_property"
      end

    end

    property :just_an_other_property
    # property
    # ==========
    # You can also use a shortcut:
    #
    # Example:
    # --------
    #   property :property_name
    #
    # Explanation
    # -----------
    # In this case, easy_type looks for a file 
    # `module_name\lib\puppet\type\type_name\property_name.rb`
    # This file *MUST* contain the full property definition
    #

    group(:group_name) do
      # property :first_in_group
      # property :second_in_group
    end
    #
    # group
    # =====
    #
    # Sometimes to get your modify statement correct for a resource, you need to include more parts
    # of the resource. If you define a group of properties, you tell easy_type. To always include all
    # properties in the group if any of them is changed.
    #
    # Example:
    # --------
    # group(:group_name) do
    #   property :first_in_group
    #   property :second_in_group
    # end
    #
    # Explanation:
    # ------------
    # if Puppet signals property `first_in_group` is modified, the command will include the return values of 
    # the `on_apply` for both properties. 
    #
    # Tricky stuff
    # ------------
    # When you want to use a group, you **CANNOT** use inline (e.g. newproperty do), but you **MUST** 
    # use the property definition in the fiCheck what the keys of the Hash are. There **is** a difference between 'key', 'KEY', :KEY and :key 
    #
    #
  end
end

Spread the word

If you like easy_type, You can spread the word by adding a badge to the README.md file of your newly created type.

[![Powered By EasyType](https://raw.github.com/hajee/easy_type/master/powered_by_easy_type.png)](https://github.com/hajee/easy_type)

This will look like this:

Powered By EasyType

License

MIT License

Contact

Bert Hajee hajee@moretIA.com

Support

Please log tickets and issues at our Projects site

Bitdeli Badge

About

Build a complex puppet custom type and provider, the easy way

Resources

License

Stars

Watchers

Forks

Packages

No packages published