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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
|
require 'net/http'
require 'uri'
require 'tempfile'
require 'puppet/util/checksums'
require 'puppet/network/http/api/v1'
require 'puppet/network/http/compression'
module Puppet
Puppet::Type.type(:file).newproperty(:content) do
include Puppet::Util::Diff
include Puppet::Util::Checksums
include Puppet::Network::HTTP::API::V1
include Puppet::Network::HTTP::Compression.module
attr_reader :actual_content
desc "Specify the contents of a file as a string. Newlines, tabs, and
spaces can be specified using the escaped syntax (e.g., \\n for a
newline). The primary purpose of this parameter is to provide a
kind of limited templating::
define resolve(nameserver1, nameserver2, domain, search) {
$str = \"search $search
domain $domain
nameserver $nameserver1
nameserver $nameserver2
\"
file { \"/etc/resolv.conf\":
content => $str
}
}
This attribute is especially useful when used with
`PuppetTemplating templating`:trac:."
# Store a checksum as the value, rather than the actual content.
# Simplifies everything.
munge do |value|
if value == :absent
value
elsif checksum?(value)
# XXX This is potentially dangerous because it means users can't write a file whose
# entire contents are a plain checksum
value
else
@actual_content = value
resource.parameter(:checksum).sum(value)
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.
if source = resource.parameter(:source) and tmp = source.checksum
newvalue = tmp
end
if currentvalue == :absent
return "defined content as '%s'" % [newvalue]
elsif newvalue == :absent
return "undefined content from '%s'" % [currentvalue]
else
return "content changed '%s' to '%s'" % [currentvalue, newvalue]
end
end
def checksum_type
if source = resource.parameter(:source)
result = source.checksum
else checksum = resource.parameter(:checksum)
result = resource[:checksum]
end
if result =~ /^\{(\w+)\}.+/
return $1.to_sym
else
return result
end
end
def length
(actual_content and actual_content.length) || 0
end
def content
self.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.should_be_file?
return false if is == :absent
else
return true
end
return true if ! @resource.replace?
return true unless self.should
result = super
if ! result and Puppet[:show_diff]
write_temporarily do |path|
print diff(@resource[:path], path)
end
end
return result
end
def retrieve
return :absent unless stat = @resource.stat
ftype = stat.ftype
# Don't even try to manage the content on directories or links
return nil if ["directory","link"].include?(ftype)
begin
resource.parameter(:checksum).sum_file(resource[:path])
rescue => detail
raise Puppet::Error, "Could not read #{ftype} #{@resource.title}: #{detail}"
end
end
# Make sure we're also managing the checksum property.
def should=(value)
@resource.newattr(:checksum) unless @resource.parameter(:checksum)
super
end
# Just write our content out to disk.
def sync
return_event = @resource.stat ? :file_changed : :file_created
# We're safe not testing for the 'source' if there's no 'should'
# because we wouldn't have gotten this far if there weren't at least
# one valid value somewhere.
@resource.write(:content)
return return_event
end
def write_temporarily
tempfile = Tempfile.new("puppet-file")
tempfile.open
write(tempfile)
tempfile.close
yield tempfile.path
tempfile.delete
end
def write(file)
resource.parameter(:checksum).sum_stream { |sum|
each_chunk_from(actual_content || resource.parameter(:source)) { |chunk|
sum << chunk
file.print chunk
}
}
end
def each_chunk_from(source_or_content)
if source_or_content.is_a?(String)
yield source_or_content
elsif source_or_content.nil?
yield read_file_from_filebucket
elsif source_or_content.local?
chunk_file_from_disk(source_or_content) { |chunk| yield chunk }
else
chunk_file_from_source(source_or_content) { |chunk| yield chunk }
end
end
private
def chunk_file_from_disk(source_or_content)
File.open(source_or_content.full_path, "r") do |src|
while chunk = src.read(8192)
yield chunk
end
end
end
def chunk_file_from_source(source_or_content)
request = Puppet::Indirector::Request.new(:file_content, :find, source_or_content.full_path)
connection = Puppet::Network::HttpPool.http_instance(source_or_content.server, source_or_content.port)
connection.request_get(indirection2uri(request), add_accept_encoding({"Accept" => "raw"})) do |response|
case response.code
when "404"; nil
when /^2/; uncompress(response) { |uncompressor| response.read_body { |chunk| yield uncompressor.uncompress(chunk) } }
else
# Raise the http error if we didn't get a 'success' of some kind.
message = "Error %s on SERVER: %s" % [response.code, (response.body||'').empty? ? response.message : uncompress_body(response)]
raise Net::HTTPError.new(message, response)
end
end
end
def read_file_from_filebucket
raise "Could not get filebucket from file" unless dipper = resource.bucket
sum = should.sub(/\{\w+\}/, '')
dipper.getfile(sum)
rescue => detail
fail "Could not retrieve content for #{should} from filebucket: #{detail}"
end
end
end
|