+require 'puppet/type/file/owner'
+require 'puppet/type/file/group'
+require 'puppet/type/file/mode'
+require 'puppet/util/checksums'
+
+Puppet::Type.newtype(:concat_file) do
+ @doc = "Gets all the file fragments and puts these into the target file.
+ This will mostly be used with exported resources.
+
+ example:
+ Concat_fragment <<| tag == 'unique_tag' |>>
+
+ concat_file { '/tmp/file':
+ tag => 'unique_tag', # Mandatory
+ path => '/tmp/file', # Optional. If given it overrides the resource name
+ owner => 'root', # Optional. Default to undef
+ group => 'root', # Optional. Default to undef
+ mode => '0644' # Optional. Default to undef
+ order => 'numeric' # Optional, Default to 'numeric'
+ ensure_newline => false # Optional, Defaults to false
+ }
+ "
+
+ ensurable do
+ defaultvalues
+
+ defaultto { :present }
+ end
+
+ def exists?
+ self[:ensure] == :present
+ end
+
+ newparam(:tag) do
+ desc "Tag reference to collect all concat_fragment's with the same tag"
+ end
+
+ newparam(:path, :namevar => true) do
+ desc "The output file"
+
+ validate do |value|
+ unless (Puppet::Util.absolute_path?(value, :posix) or Puppet::Util.absolute_path?(value, :windows))
+ raise ArgumentError, "File paths must be fully qualified, not '#{value}'"
+ end
+ end
+ end
+
+ newparam(:owner, :parent => Puppet::Type::File::Owner) do
+ desc "Desired file owner."
+ end
+
+ newparam(:group, :parent => Puppet::Type::File::Group) do
+ desc "Desired file group."
+ end
+
+ newparam(:mode, :parent => Puppet::Type::File::Mode) do
+ desc "Desired file mode."
+ end
+
+ newparam(:order) do
+ desc "Controls the ordering of fragments. Can be set to alpha or numeric."
+
+ newvalues(:alpha, :numeric)
+
+ defaultto :numeric
+ end
+
+ newparam(:backup) do
+ desc "Controls the filebucketing behavior of the final file and see File type reference for its use."
+ defaultto 'puppet'
+ end
+
+ newparam(:replace, :boolean => true, :parent => Puppet::Parameter::Boolean) do
+ desc "Whether to replace a file that already exists on the local system."
+ defaultto :true
+ end
+
+ newparam(:validate_cmd) do
+ desc "Validates file."
+ end
+
+ newparam(:ensure_newline, :boolean => true, :parent => Puppet::Parameter::Boolean) do
+ desc "Whether to ensure there is a newline after each fragment."
+ defaultto :false
+ end
+
+ # Inherit File parameters
+ newparam(:selinux_ignore_defaults) do
+ end
+
+ newparam(:selrange) do
+ end
+
+ newparam(:selrole) do
+ end
+
+ newparam(:seltype) do
+ end
+
+ newparam(:seluser) do
+ end
+
+ newparam(:show_diff) do
+ end
+ # End file parameters
+
+ # Autorequire the file we are generating below
+ autorequire(:file) do
+ [self[:path]]
+ end
+
+ def should_content
+ return @generated_content if @generated_content
+ @generated_content = ""
+ content_fragments = []
+
+ resources = catalog.resources.select do |r|
+ r.is_a?(Puppet::Type.type(:concat_fragment)) && r[:tag] == self[:tag]
+ end
+
+ resources.each do |r|
+ content_fragments << ["#{r[:order]}___#{r[:name]}", fragment_content(r)]
+ end
+
+ if self[:order] == :numeric
+ sorted = content_fragments.sort do |a, b|
+ def decompound(d)
+ d.split('___', 2).map { |v| v =~ /^\d+$/ ? v.to_i : v }
+ end
+
+ decompound(a[0]) <=> decompound(b[0])
+ end
+ else
+ sorted = content_fragments.sort_by do |a|
+ a_order, a_name = a[0].split('__', 2)
+ [a_order, a_name]
+ end
+ end
+
+ @generated_content = sorted.map { |cf| cf[1] }.join
+
+ @generated_content
+ end
+
+ def fragment_content(r)
+ if r[:content].nil? == false
+ fragment_content = r[:content]
+ elsif r[:source].nil? == false
+ @source = nil
+ Array(r[:source]).each do |source|
+ if Puppet::FileServing::Metadata.indirection.find(source)
+ @source = source
+ break
+ end
+ end
+ self.fail "Could not retrieve source(s) #{r[:source].join(", ")}" unless @source
+ tmp = Puppet::FileServing::Content.indirection.find(@source)
+ fragment_content = tmp.content unless tmp.nil?
+ end
+
+ if self[:ensure_newline]
+ fragment_content<<"\n" unless fragment_content =~ /\n$/
+ end
+
+ fragment_content
+ end
+
+ def generate
+ file_opts = {
+ :ensure => self[:ensure] == :absent ? :absent : :file,
+ }
+
+ [:path,
+ :owner,
+ :group,
+ :mode,
+ :replace,
+ :backup,
+ :selinux_ignore_defaults,
+ :selrange,
+ :selrole,
+ :seltype,
+ :seluser,
+ :validate_cmd,
+ :show_diff].each do |param|
+ unless self[param].nil?
+ file_opts[param] = self[param]
+ end
+ end
+
+ metaparams = Puppet::Type.metaparams
+ excluded_metaparams = [ :before, :notify, :require, :subscribe, :tag ]
+
+ metaparams.reject! { |param| excluded_metaparams.include? param }
+
+ metaparams.each do |metaparam|
+ file_opts[metaparam] = self[metaparam] if self[metaparam]
+ end
+
+ [Puppet::Type.type(:file).new(file_opts)]
+ end
+
+ def eval_generate
+ content = should_content
+
+ if !content.nil? and !content.empty?
+ catalog.resource("File[#{self[:path]}]")[:content] = content
+ end
+
+ [ catalog.resource("File[#{self[:path]}]") ]
+ end
+end