X-Git-Url: https://git.adam-barratt.org.uk/?p=mirror%2Fdsa-puppet.git;a=blobdiff_plain;f=3rdparty%2Fmodules%2Fconcat%2Flib%2Fpuppet%2Ftype%2Fconcat_file.rb;h=6dd068ec1bf4f1d33d361756ad99eca9acebb2b9;hp=359c96341e1da6fa1f26a8908cce393610a97bf3;hb=30caaa85aed7015ca0d77216bff175eebd917eb7;hpb=6f656bd4265e3dab13b9af2bf96e9044322e9d8f diff --git a/3rdparty/modules/concat/lib/puppet/type/concat_file.rb b/3rdparty/modules/concat/lib/puppet/type/concat_file.rb index 359c96341..6dd068ec1 100644 --- a/3rdparty/modules/concat/lib/puppet/type/concat_file.rb +++ b/3rdparty/modules/concat/lib/puppet/type/concat_file.rb @@ -4,14 +4,15 @@ 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. + @doc = <<-DOC + @summary + Generates a file with content from fragments sharing a common unique tag. - example: + @example Concat_fragment <<| tag == 'unique_tag' |>> concat_file { '/tmp/file': - tag => 'unique_tag', # Mandatory + tag => 'unique_tag', # Optional. Default to undef path => '/tmp/file', # Optional. If given it overrides the resource name owner => 'root', # Optional. Default to undef group => 'root', # Optional. Default to undef @@ -19,9 +20,14 @@ Puppet::Type.newtype(:concat_file) do order => 'numeric' # Optional, Default to 'numeric' ensure_newline => false # Optional, Defaults to false } - " + DOC ensurable do + desc <<-DOC + Specifies whether the destination file should exist. Setting to 'absent' tells Puppet to delete the destination file if it exists, and + negates the effect of any other parameters. + DOC + defaultvalues defaultto { :present } @@ -32,33 +38,46 @@ Puppet::Type.newtype(:concat_file) do end newparam(:tag) do - desc "Tag reference to collect all concat_fragment's with the same tag" + desc 'Required. Specifies a unique tag reference to collect all concat_fragments with the same tag.' end - newparam(:path, :namevar => true) do - desc "The output file" + newparam(:path, namevar: true) do + desc <<-DOC + Specifies a destination file for the combined fragments. Valid options: a string containing an absolute path. Default value: the + title of your declared resource. + DOC 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}'" + unless Puppet::Util.absolute_path?(value, :posix) || Puppet::Util.absolute_path?(value, :windows) + raise ArgumentError, _("File paths must be fully qualified, not '%{_value}'") % { _value: value } end end end - newparam(:owner, :parent => Puppet::Type::File::Owner) do - desc "Desired file owner." + newparam(:owner, parent: Puppet::Type::File::Owner) do + desc <<-DOC + Specifies the owner of the destination file. Valid options: a string containing a username or integer containing a uid. + DOC end - newparam(:group, :parent => Puppet::Type::File::Group) do - desc "Desired file group." + newparam(:group, parent: Puppet::Type::File::Group) do + desc <<-DOC + Specifies a permissions group for the destination file. Valid options: a string containing a group name or integer containing a + gid. + DOC end - newparam(:mode, :parent => Puppet::Type::File::Mode) do - desc "Desired file mode." + newparam(:mode, parent: Puppet::Type::File::Mode) do + desc <<-DOC + Specifies the permissions mode of the destination file. Valid options: a string containing a permission mode value in octal notation. + DOC end newparam(:order) do - desc "Controls the ordering of fragments. Can be set to alpha or numeric." + desc <<-DOC + Specifies a method for sorting your fragments by name within the destination file. You can override this setting for individual + fragments by adjusting the order parameter in their concat::fragment declarations. + DOC newvalues(:alpha, :numeric) @@ -66,82 +85,215 @@ Puppet::Type.newtype(:concat_file) do end newparam(:backup) do - desc "Controls the filebucketing behavior of the final file and see File type reference for its use." - defaultto 'puppet' + desc <<-DOC + Specifies whether (and how) to back up the destination file before overwriting it. Your value gets passed on to Puppet's native file + resource for execution. Valid options: true, false, or a string representing either a target filebucket or a filename extension + beginning with ".".' + DOC + + validate do |value| + unless [TrueClass, FalseClass, String].include?(value.class) + raise ArgumentError, _('Backup must be a Boolean or String') + end + end 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 + newparam(:replace, boolean: true, parent: Puppet::Parameter::Boolean) do + desc 'Specifies whether to overwrite the destination file if it already exists.' + defaultto true end newparam(:validate_cmd) do - desc "Validates file." + desc <<-DOC + Specifies a validation command to apply to the destination file. Requires Puppet version 3.5 or newer. Valid options: a string to + be passed to a file resource. + DOC + + validate do |value| + unless value.is_a?(String) + raise ArgumentError, _('Validate_cmd must be a String') + end + end + end + + newparam(:ensure_newline, boolean: true, parent: Puppet::Parameter::Boolean) do + desc "Specifies whether to add a line break at the end of each fragment that doesn't already end in one." + defaultto false + end + + newparam(:format) do + desc <<-DOC + Specify what data type to merge the fragments as. Valid options: 'plain', 'yaml', 'json', 'json-array', 'json-pretty', 'json-array-pretty'. + DOC + + newvalues(:plain, :yaml, :json, :'json-array', :'json-pretty', :'json-array-pretty') + + defaultto :plain end - newparam(:ensure_newline, :boolean => true, :parent => Puppet::Parameter::Boolean) do - desc "Whether to ensure there is a newline after each fragment." - defaultto :false + newparam(:force, boolean: true, parent: Puppet::Parameter::Boolean) do + desc 'Specifies whether to merge data structures, keeping the values with higher order.' + + defaultto false end - # Inherit File parameters - newparam(:selinux_ignore_defaults) do + newparam(:selinux_ignore_defaults, boolean: true, parent: Puppet::Parameter::Boolean) do + desc <<-DOC + See the file type's selinux_ignore_defaults documentention: + https://docs.puppetlabs.com/references/latest/type.html#file-attribute-selinux_ignore_defaults. + DOC end newparam(:selrange) do + desc "See the file type's selrange documentation: https://docs.puppetlabs.com/references/latest/type.html#file-attribute-selrange" + validate do |value| + raise ArgumentError, _('Selrange must be a String') unless value.is_a?(String) + end end newparam(:selrole) do + desc "See the file type's selrole documentation: https://docs.puppetlabs.com/references/latest/type.html#file-attribute-selrole" + validate do |value| + raise ArgumentError, _('Selrole must be a String') unless value.is_a?(String) + end end newparam(:seltype) do + desc "See the file type's seltype documentation: https://docs.puppetlabs.com/references/latest/type.html#file-attribute-seltype" + validate do |value| + raise ArgumentError, _('Seltype must be a String') unless value.is_a?(String) + end end newparam(:seluser) do + desc "See the file type's seluser documentation: https://docs.puppetlabs.com/references/latest/type.html#file-attribute-seluser" + validate do |value| + raise ArgumentError, _('Seluser must be a String') unless value.is_a?(String) + end end - newparam(:show_diff) do + newparam(:show_diff, boolean: true, parent: Puppet::Parameter::Boolean) do + desc <<-DOC + Specifies whether to set the show_diff parameter for the file resource. Useful for hiding secrets stored in hiera from insecure + reporting methods. + DOC end - # End file parameters # Autorequire the file we are generating below + # Why is this necessary ? autorequire(:file) do [self[:path]] end + def fragments + # Collect fragments that target this resource by path, title or tag. + @fragments ||= catalog.resources.map { |resource| + next unless resource.is_a?(Puppet::Type.type(:concat_fragment)) + + if resource[:target] == self[:path] || resource[:target] == title || + (resource[:tag] && resource[:tag] == self[:tag]) + resource + end + }.compact + end + + def decompound(d) + d.split('___', 2).map { |v| (v =~ %r{^\d+$}) ? v.to_i : v } + end + def should_content return @generated_content if @generated_content - @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| + fragments.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 } + sorted = if self[:order] == :numeric + content_fragments.sort do |a, b| + decompound(a[0]) <=> decompound(b[0]) + end + else + content_fragments.sort_by do |a| + a_order, a_name = a[0].split('__', 2) + [a_order, a_name] + end + end + + case self[:format] + when :plain + @generated_content = sorted.map { |cf| cf[1] }.join + when :yaml + content_array = sorted.map do |cf| + YAML.safe_load(cf[1]) + end + content_hash = content_array.reduce({}) do |memo, current| + nested_merge(memo, current) + end + @generated_content = content_hash.to_yaml + when :json, :'json-array', :'json-pretty', :'json-array-pretty' + content_array = sorted.map do |cf| + JSON.parse(cf[1]) + end + + if [:json, :'json-pretty'].include?(self[:format]) + content_hash = content_array.reduce({}) do |memo, current| + nested_merge(memo, current) 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] + @generated_content = + if self[:format] == :json + content_hash.to_json + else + JSON.pretty_generate(content_hash) + end + else + @generated_content = + if self[:format] == :'json-array' + content_array.to_json + else + JSON.pretty_generate(content_array) + end end end - @generated_content = sorted.map { |cf| cf[1] }.join - @generated_content end + def nested_merge(hash1, hash2) + # If a hash is empty, simply return the other + return hash1 if hash2.empty? + return hash2 if hash1.empty? + + # Unique merge for arrays + if hash1.is_a?(Array) && hash2.is_a?(Array) + return (hash1 + hash2).uniq + end + + # Deep-merge Hashes; higher order value is kept + hash1.merge(hash2) do |k, v1, v2| + if v1.is_a?(Hash) && v2.is_a?(Hash) + nested_merge(v1, v2) + elsif v1.is_a?(Array) && v2.is_a?(Array) + nested_merge(v1, v2) + else + # Fail if there are duplicate keys without force + unless v1 == v2 + unless self[:force] + err_message = [ + "Duplicate key '#{k}' found with values '#{v1}' and #{v2}'.", + "Use 'force' attribute to merge keys.", + ] + raise(_(err_message.join(' '))) + end + Puppet.debug("Key '#{k}': replacing '#{v2}' with '#{v1}'.") + end + v1 + end + end + end + def fragment_content(r) if r[:content].nil? == false fragment_content = r[:content] @@ -149,17 +301,18 @@ Puppet::Type.newtype(:concat_file) do @source = nil Array(r[:source]).each do |source| if Puppet::FileServing::Metadata.indirection.find(source) - @source = source + @source = source break end end - self.fail "Could not retrieve source(s) #{r[:source].join(", ")}" unless @source + raise _('Could not retrieve source(s) %{_array}') % { _array: Array(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$/ + newline = Puppet::Util::Platform.windows? ? "\r\n" : "\n" + fragment_content << newline unless fragment_content =~ %r{#{newline}$} end fragment_content @@ -167,7 +320,7 @@ Puppet::Type.newtype(:concat_file) do def generate file_opts = { - :ensure => self[:ensure] == :absent ? :absent : :file, + ensure: (self[:ensure] == :absent) ? :absent : :file, } [:path, @@ -183,18 +336,16 @@ Puppet::Type.newtype(:concat_file) do :seluser, :validate_cmd, :show_diff].each do |param| - unless self[param].nil? - file_opts[param] = self[param] - end + file_opts[param] = self[param] unless self[param].nil? end metaparams = Puppet::Type.metaparams - excluded_metaparams = [ :before, :notify, :require, :subscribe, :tag ] + 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] + file_opts[metaparam] = self[metaparam] unless self[metaparam].nil? end [Puppet::Type.type(:file).new(file_opts)] @@ -203,10 +354,10 @@ Puppet::Type.newtype(:concat_file) do def eval_generate content = should_content - if !content.nil? and !content.empty? + if !content.nil? && !content.empty? catalog.resource("File[#{self[:path]}]")[:content] = content end - [ catalog.resource("File[#{self[:path]}]") ] + [catalog.resource("File[#{self[:path]}]")] end end