1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
# frozen_string_literal: true
require 'net/http'
require 'uri'
require 'tempfile'
require_relative '../../../puppet/util/checksums'
require_relative '../../../puppet/type/file/data_sync'
module Puppet
Puppet::Type.type(:file).newproperty(:content) do
include Puppet::Util::Checksums
include Puppet::DataSync
attr_reader :actual_content
desc <<-'EOT'
The desired contents of a file, as a string. This attribute is mutually
exclusive with `source` and `target`.
Newlines and tabs can be specified in double-quoted strings using
standard escaped syntax --- \n for a newline, and \t for a tab.
With very small files, you can construct content strings directly in
the manifest...
define resolve($nameserver1, $nameserver2, $domain, $search) {
$str = "search ${search}
domain ${domain}
nameserver ${nameserver1}
nameserver ${nameserver2}
"
file { '/etc/resolv.conf':
content => $str,
}
}
...but for larger files, this attribute is more useful when combined with the
[template](https://puppet.com/docs/puppet/latest/function.html#template)
or [file](https://puppet.com/docs/puppet/latest/function.html#file)
function.
EOT
# Store a checksum as the value, rather than the actual content.
# Simplifies everything.
munge do |value|
if value == :absent
value
elsif value.is_a?(String) && checksum?(value)
# XXX This is potentially dangerous because it means users can't write a file whose
# entire contents are a plain checksum unless it is a Binary content.
Puppet.puppet_deprecation_warning([
# TRANSLATORS "content" is an attribute and should not be translated
_('Using a checksum in a file\'s "content" property is deprecated.'),
# TRANSLATORS "filebucket" is a resource type and should not be translated. The quoted occurrence of "content" is an attribute and should not be translated.
_('The ability to use a checksum to retrieve content from the filebucket using the "content" property will be removed in a future release.'),
# TRANSLATORS "content" is an attribute and should not be translated.
_('The literal value of the "content" property will be written to the file.'),
# TRANSLATORS "static catalogs" should not be translated.
_('The checksum retrieval functionality is being replaced by the use of static catalogs.'),
_('See https://puppet.com/docs/puppet/latest/static_catalogs.html for more information.')
].join(" "),
:file => @resource.file,
:line => @resource.line) if !@actual_content && !resource.parameter(:source)
value
else
@actual_content = value.is_a?(Puppet::Pops::Types::PBinaryType::Binary) ? value.binary_buffer : value
resource.parameter(:checksum).sum(@actual_content)
end
end
# Checksums need to invert how changes are printed.
def change_to_s(currentvalue, newvalue)
# Our "new" checksum value is provided by the source.
source = resource.parameter(:source)
tmp = source.checksum if source
if tmp
newvalue = tmp
end
if currentvalue == :absent
"defined content as '#{newvalue}'"
elsif newvalue == :absent
"undefined content from '#{currentvalue}'"
else
"content changed '#{currentvalue}' to '#{newvalue}'"
end
end
def length
(actual_content and actual_content.length) || 0
end
def content
should
end
# Override this method to provide diffs if asked for.
# Also, fix #872: when content is used, and replace is true, the file
# should be insync when it exists
def insync?(is)
if resource[:source] && resource[:checksum_value]
# Asserts that nothing has changed since validate ran.
devfail "content property should not exist if source and checksum_value are specified"
end
contents_prop = resource.parameter(:source) || self
checksum_insync?(contents_prop, is, !resource[:content].nil?) { |inner| super(inner) }
end
def property_matches?(current, desired)
# If checksum_value is specified, it overrides comparing the content field.
checksum_type = resource.parameter(:checksum).value
checksum_value = resource.parameter(:checksum_value)
if checksum_value
desired = "{#{checksum_type}}#{checksum_value.value}"
end
# The inherited equality is always accepted, so use it if valid.
return true if super(current, desired)
date_matches?(checksum_type, current, desired)
end
def retrieve
retrieve_checksum(resource)
end
# Make sure we're also managing the checksum property.
def should=(value)
# treat the value as a bytestring
value = value.b if value.is_a?(String)
@resource.newattr(:checksum) unless @resource.parameter(:checksum)
super
end
# Just write our content out to disk.
def sync
contents_sync(resource.parameter(:source) || self)
end
def write(file)
resource.parameter(:checksum).sum_stream { |sum|
each_chunk_from { |chunk|
sum << chunk
file.print chunk
}
}
end
private
# the content is munged so if it's a checksum source_or_content is nil
# unless the checksum indirectly comes from source
def each_chunk_from
if actual_content.is_a?(String)
yield actual_content
elsif content_is_really_a_checksum? && actual_content.nil?
yield read_file_from_filebucket
elsif actual_content.nil?
yield ''
end
end
def content_is_really_a_checksum?
checksum?(should)
end
def read_file_from_filebucket
dipper = resource.bucket
raise "Could not get filebucket from file" unless dipper
sum = should.sub(/\{\w+\}/, '')
dipper.getfile(sum)
rescue => detail
self.fail Puppet::Error, "Could not retrieve content for #{should} from filebucket: #{detail}", detail
end
end
end
|