puppet 2.7.17 → 2.7.18
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- data/CHANGELOG +16 -0
- data/conf/redhat/puppet.spec +4 -1
- data/lib/puppet.rb +1 -1
- data/lib/puppet/application/master.rb +26 -8
- data/lib/puppet/defaults.rb +4 -4
- data/lib/puppet/face/file/download.rb +1 -1
- data/lib/puppet/file_bucket/file.rb +6 -7
- data/lib/puppet/file_serving/configuration.rb +2 -1
- data/lib/puppet/file_serving/content.rb +1 -2
- data/lib/puppet/file_serving/metadata.rb +1 -2
- data/lib/puppet/file_serving/mount/modules.rb +4 -5
- data/lib/puppet/file_serving/{indirection_hooks.rb → terminus_selector.rb} +2 -5
- data/lib/puppet/indirector/file_bucket_file/selector.rb +49 -0
- data/lib/puppet/indirector/file_content/selector.rb +30 -0
- data/lib/puppet/indirector/file_metadata/selector.rb +30 -0
- data/lib/puppet/network/authstore.rb +10 -2
- data/lib/puppet/reports/store.rb +13 -6
- data/lib/puppet/ssl/base.rb +8 -0
- data/lib/puppet/ssl/certificate_authority.rb +10 -0
- data/lib/puppet/ssl/certificate_authority/interface.rb +4 -2
- data/lib/puppet/ssl/host.rb +1 -0
- data/spec/integration/file_bucket/file_spec.rb +44 -0
- data/spec/integration/file_serving/content_spec.rb +3 -8
- data/spec/integration/file_serving/metadata_spec.rb +4 -9
- data/spec/integration/network/rest_authconfig_spec.rb +22 -3
- data/spec/shared_behaviours/file_serving.rb +70 -58
- data/spec/shared_behaviours/file_serving_model.rb +73 -0
- data/spec/unit/file_serving/configuration_spec.rb +37 -29
- data/spec/unit/file_serving/content_spec.rb +1 -5
- data/spec/unit/file_serving/metadata_spec.rb +1 -5
- data/spec/unit/file_serving/mount/modules_spec.rb +8 -0
- data/spec/unit/file_serving/{indirection_hooks_spec.rb → terminus_selector_spec.rb} +10 -11
- data/spec/unit/indirector/file_bucket_file/selector_spec.rb +29 -0
- data/spec/unit/indirector/file_content/selector_spec.rb +10 -0
- data/spec/unit/indirector/file_metadata/selector_spec.rb +11 -0
- data/spec/unit/network/handler/ca_spec.rb +1 -0
- data/spec/unit/reports/store_spec.rb +28 -0
- data/spec/unit/ssl/certificate_authority/interface_spec.rb +17 -17
- data/spec/unit/ssl/certificate_authority_spec.rb +76 -11
- metadata +15 -8
- data/lib/puppet/file_bucket/file/indirection_hooks.rb +0 -9
data/CHANGELOG
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
2.7.18
|
2
|
+
===
|
3
|
+
d804782 Reject directory traversal in store report processor
|
4
|
+
fd44bf5 Tighten permissions on classfile, resourcefile, lastrunfile, and lastrunreport.
|
5
|
+
4d7c9fd Use "inspect" when listing certificates
|
6
|
+
bd2820e Don't allow the creation of SSL objects with invalid certnames
|
7
|
+
f341962 Validate CSR CN and provided certname before signing
|
8
|
+
38c5a4e Add specs for selector terminuses of file_{content,metadata}
|
9
|
+
9e920a8 Fix whitespace inside parentheses
|
10
|
+
2d01c2b Use head method to determine if file is in file bucket
|
11
|
+
40ee670 Always use the local file_bucket on master
|
12
|
+
d881b4b Fail more gracefully when finding module files if no file is specified
|
13
|
+
20ab0e9 Reject file requests containing ..
|
14
|
+
10f6cb8 Add Selector terminus for file_content/file_metadata
|
15
|
+
ab9150b Deprecate IP-based authentication
|
16
|
+
|
1
17
|
2.7.17
|
2
18
|
===
|
3
19
|
30d89d3 (maint) Add symlink stub to gentoo service provider spec
|
data/conf/redhat/puppet.spec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
%global confdir conf/redhat
|
6
6
|
|
7
7
|
Name: puppet
|
8
|
-
Version: 2.7.
|
8
|
+
Version: 2.7.18
|
9
9
|
#Release: 0.1rc1.2%{?dist}
|
10
10
|
Release: 1%{?dist}
|
11
11
|
Summary: A network tool for managing many disparate systems
|
@@ -289,6 +289,9 @@ fi
|
|
289
289
|
rm -rf %{buildroot}
|
290
290
|
|
291
291
|
%changelog
|
292
|
+
* Mon Jul 9 2012 Moses Mendoza <moses@puppetlabs.com> - 2.7.18-1
|
293
|
+
- Update for 2.7.18
|
294
|
+
|
292
295
|
* Tue Jun 19 2012 Matthaus Litteken <matthaus@puppetlabs.com> - 2.7.17-1
|
293
296
|
- Update for 2.7.17
|
294
297
|
|
data/lib/puppet.rb
CHANGED
@@ -163,8 +163,6 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
|
|
163
163
|
|
164
164
|
def main
|
165
165
|
require 'etc'
|
166
|
-
require 'puppet/file_serving/content'
|
167
|
-
require 'puppet/file_serving/metadata'
|
168
166
|
|
169
167
|
xmlrpc_handlers = [:Status, :FileServer, :Master, :Report, :Filebucket]
|
170
168
|
|
@@ -205,9 +203,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
|
|
205
203
|
end
|
206
204
|
end
|
207
205
|
|
208
|
-
def
|
209
|
-
raise Puppet::Error.new("Puppet master is not supported on Microsoft Windows") if Puppet.features.microsoft_windows?
|
210
|
-
|
206
|
+
def setup_logs
|
211
207
|
# Handle the logging settings.
|
212
208
|
if options[:debug] or options[:verbose]
|
213
209
|
if options[:debug]
|
@@ -223,14 +219,22 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
|
|
223
219
|
end
|
224
220
|
|
225
221
|
Puppet::Util::Log.newdestination(:syslog) unless options[:setdest]
|
222
|
+
end
|
226
223
|
|
227
|
-
|
228
|
-
|
229
|
-
|
224
|
+
def setup_terminuses
|
225
|
+
require 'puppet/file_serving/content'
|
226
|
+
require 'puppet/file_serving/metadata'
|
230
227
|
|
231
228
|
# Cache our nodes in yaml. Currently not configurable.
|
232
229
|
Puppet::Node.indirection.cache_class = :yaml
|
233
230
|
|
231
|
+
Puppet::FileServing::Content.indirection.terminus_class = :file_server
|
232
|
+
Puppet::FileServing::Metadata.indirection.terminus_class = :file_server
|
233
|
+
|
234
|
+
Puppet::FileBucket::File.indirection.terminus_class = :file
|
235
|
+
end
|
236
|
+
|
237
|
+
def setup_ssl
|
234
238
|
# Configure all of the SSL stuff.
|
235
239
|
if Puppet::SSL::CertificateAuthority.ca?
|
236
240
|
Puppet::SSL::Host.ca_location = :local
|
@@ -240,4 +244,18 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
|
|
240
244
|
Puppet::SSL::Host.ca_location = :none
|
241
245
|
end
|
242
246
|
end
|
247
|
+
|
248
|
+
def setup
|
249
|
+
raise Puppet::Error.new("Puppet master is not supported on Microsoft Windows") if Puppet.features.microsoft_windows?
|
250
|
+
|
251
|
+
setup_logs
|
252
|
+
|
253
|
+
exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?
|
254
|
+
|
255
|
+
Puppet.settings.use :main, :master, :ssl, :metrics
|
256
|
+
|
257
|
+
setup_terminuses
|
258
|
+
|
259
|
+
setup_ssl
|
260
|
+
end
|
243
261
|
end
|
data/lib/puppet/defaults.rb
CHANGED
@@ -599,14 +599,14 @@ EOT
|
|
599
599
|
:client_datadir => {:default => "$vardir/client_data", :mode => "750", :desc => "The directory in which serialized data is stored on the client."},
|
600
600
|
:classfile => { :default => "$statedir/classes.txt",
|
601
601
|
:owner => "root",
|
602
|
-
:mode =>
|
602
|
+
:mode => 0640,
|
603
603
|
:desc => "The file in which puppet agent stores a list of the classes
|
604
604
|
associated with the retrieved configuration. Can be loaded in
|
605
605
|
the separate `puppet` executable using the `--loadclasses`
|
606
606
|
option."},
|
607
607
|
:resourcefile => { :default => "$statedir/resources.txt",
|
608
608
|
:owner => "root",
|
609
|
-
:mode =>
|
609
|
+
:mode => 0640,
|
610
610
|
:desc => "The file in which puppet agent stores a list of the resources
|
611
611
|
associated with the retrieved configuration." },
|
612
612
|
:puppetdlog => { :default => "$logdir/puppetd.log",
|
@@ -713,11 +713,11 @@ EOT
|
|
713
713
|
"Whether to send reports after every transaction."
|
714
714
|
],
|
715
715
|
:lastrunfile => { :default => "$statedir/last_run_summary.yaml",
|
716
|
-
:mode =>
|
716
|
+
:mode => 0640,
|
717
717
|
:desc => "Where puppet agent stores the last run report summary in yaml format."
|
718
718
|
},
|
719
719
|
:lastrunreport => { :default => "$statedir/last_run_report.yaml",
|
720
|
-
:mode =>
|
720
|
+
:mode => 0640,
|
721
721
|
:desc => "Where puppet agent stores the last run report in yaml format."
|
722
722
|
},
|
723
723
|
:graph => [false, "Whether to create dot graph files for the different
|
@@ -35,7 +35,7 @@ Puppet::Face.define(:file, '0.0.1') do
|
|
35
35
|
key = "#{type}/#{sumdata}"
|
36
36
|
|
37
37
|
Puppet::FileBucket::File.indirection.terminus_class = :file
|
38
|
-
if Puppet::FileBucket::File.indirection.
|
38
|
+
if Puppet::FileBucket::File.indirection.head(key)
|
39
39
|
Puppet.info "Content for '#{sum}' already exists"
|
40
40
|
return
|
41
41
|
end
|
@@ -8,13 +8,12 @@ class Puppet::FileBucket::File
|
|
8
8
|
# There are mechanisms to save and load this file locally and remotely in puppet/indirector/filebucketfile/*
|
9
9
|
# There is a compatibility class that emulates pre-indirector filebuckets in Puppet::FileBucket::Dipper
|
10
10
|
extend Puppet::Indirector
|
11
|
-
|
12
|
-
indirects :file_bucket_file, :terminus_class => :file, :extend => Puppet::FileBucket::File::IndirectionHooks
|
11
|
+
indirects :file_bucket_file, :terminus_class => :selector
|
13
12
|
|
14
13
|
attr :contents
|
15
14
|
attr :bucket_path
|
16
15
|
|
17
|
-
def initialize(
|
16
|
+
def initialize(contents, options = {})
|
18
17
|
raise ArgumentError.new("contents must be a String, got a #{contents.class}") unless contents.is_a?(String)
|
19
18
|
@contents = contents
|
20
19
|
|
@@ -42,15 +41,15 @@ class Puppet::FileBucket::File
|
|
42
41
|
"#{checksum_type}/#{checksum_data}"
|
43
42
|
end
|
44
43
|
|
45
|
-
def self.from_s(
|
46
|
-
self.new(
|
44
|
+
def self.from_s(contents)
|
45
|
+
self.new(contents)
|
47
46
|
end
|
48
47
|
|
49
48
|
def to_pson
|
50
49
|
{ "contents" => contents }.to_pson
|
51
50
|
end
|
52
51
|
|
53
|
-
def self.from_pson(
|
54
|
-
self.new(
|
52
|
+
def self.from_pson(pson)
|
53
|
+
self.new(pson["contents"])
|
55
54
|
end
|
56
55
|
end
|
@@ -64,7 +64,8 @@ class Puppet::FileServing::Configuration
|
|
64
64
|
|
65
65
|
mount_name, path = request.key.split(File::Separator, 2)
|
66
66
|
|
67
|
-
raise(ArgumentError, "Cannot find file: Invalid
|
67
|
+
raise(ArgumentError, "Cannot find file: Invalid mount '#{mount_name}'") unless mount_name =~ %r{^[-\w]+$}
|
68
|
+
raise(ArgumentError, "Cannot find file: Invalid relative path '#{path}'") if path and path.split('/').include?('..')
|
68
69
|
|
69
70
|
return nil unless mount = find_mount(mount_name, request.environment)
|
70
71
|
if mount.name == "modules" and mount_name != "modules"
|
@@ -1,14 +1,13 @@
|
|
1
1
|
require 'puppet/indirector'
|
2
2
|
require 'puppet/file_serving'
|
3
3
|
require 'puppet/file_serving/base'
|
4
|
-
require 'puppet/file_serving/indirection_hooks'
|
5
4
|
|
6
5
|
# A class that handles retrieving file contents.
|
7
6
|
# It only reads the file when its content is specifically
|
8
7
|
# asked for.
|
9
8
|
class Puppet::FileServing::Content < Puppet::FileServing::Base
|
10
9
|
extend Puppet::Indirector
|
11
|
-
indirects :file_content, :
|
10
|
+
indirects :file_content, :terminus_class => :selector
|
12
11
|
|
13
12
|
attr_writer :content
|
14
13
|
|
@@ -3,7 +3,6 @@ require 'puppet/indirector'
|
|
3
3
|
require 'puppet/file_serving'
|
4
4
|
require 'puppet/file_serving/base'
|
5
5
|
require 'puppet/util/checksums'
|
6
|
-
require 'puppet/file_serving/indirection_hooks'
|
7
6
|
|
8
7
|
# A class that handles retrieving file metadata.
|
9
8
|
class Puppet::FileServing::Metadata < Puppet::FileServing::Base
|
@@ -11,7 +10,7 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base
|
|
11
10
|
include Puppet::Util::Checksums
|
12
11
|
|
13
12
|
extend Puppet::Indirector
|
14
|
-
indirects :file_metadata, :
|
13
|
+
indirects :file_metadata, :terminus_class => :selector
|
15
14
|
|
16
15
|
attr_reader :path, :owner, :group, :mode, :checksum_type, :checksum, :ftype, :destination
|
17
16
|
|
@@ -5,6 +5,7 @@ require 'puppet/file_serving/mount'
|
|
5
5
|
class Puppet::FileServing::Mount::Modules < Puppet::FileServing::Mount
|
6
6
|
# Return an instance of the appropriate class.
|
7
7
|
def find(path, request)
|
8
|
+
raise "No module specified" if path.to_s.empty?
|
8
9
|
module_name, relative_path = path.split("/", 2)
|
9
10
|
return nil unless mod = request.environment.module(module_name)
|
10
11
|
|
@@ -12,11 +13,9 @@ class Puppet::FileServing::Mount::Modules < Puppet::FileServing::Mount
|
|
12
13
|
end
|
13
14
|
|
14
15
|
def search(path, request)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
return nil unless path = mod.file(relative_path)
|
19
|
-
[path]
|
16
|
+
if result = find(path, request)
|
17
|
+
[result]
|
18
|
+
end
|
20
19
|
end
|
21
20
|
|
22
21
|
def valid?
|
@@ -1,15 +1,12 @@
|
|
1
|
-
require 'uri'
|
2
1
|
require 'puppet/file_serving'
|
3
|
-
require 'puppet/util'
|
4
2
|
|
5
3
|
# This module is used to pick the appropriate terminus
|
6
4
|
# in file-serving indirections. This is necessary because
|
7
5
|
# the terminus varies based on the URI asked for.
|
8
|
-
module Puppet::FileServing::
|
6
|
+
module Puppet::FileServing::TerminusSelector
|
9
7
|
PROTOCOL_MAP = {"puppet" => :rest, "file" => :file}
|
10
8
|
|
11
|
-
|
12
|
-
def select_terminus(request)
|
9
|
+
def select(request)
|
13
10
|
# We rely on the request's parsing of the URI.
|
14
11
|
|
15
12
|
# Short-circuit to :file if it's a fully-qualified path or specifies a 'file' protocol.
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'puppet/indirector/code'
|
2
|
+
|
3
|
+
module Puppet::FileBucketFile
|
4
|
+
class Selector < Puppet::Indirector::Code
|
5
|
+
desc "Select the terminus based on the request"
|
6
|
+
|
7
|
+
def select(request)
|
8
|
+
if request.protocol == 'https'
|
9
|
+
:rest
|
10
|
+
else
|
11
|
+
:file
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_terminus(request)
|
16
|
+
indirection.terminus(select(request))
|
17
|
+
end
|
18
|
+
|
19
|
+
def head(request)
|
20
|
+
get_terminus(request).head(request)
|
21
|
+
end
|
22
|
+
|
23
|
+
def find(request)
|
24
|
+
get_terminus(request).find(request)
|
25
|
+
end
|
26
|
+
|
27
|
+
def save(request)
|
28
|
+
get_terminus(request).save(request)
|
29
|
+
end
|
30
|
+
|
31
|
+
def search(request)
|
32
|
+
get_terminus(request).search(request)
|
33
|
+
end
|
34
|
+
|
35
|
+
def destroy(request)
|
36
|
+
get_terminus(request).destroy(request)
|
37
|
+
end
|
38
|
+
|
39
|
+
def authorized?(request)
|
40
|
+
terminus = get_terminus(request)
|
41
|
+
if terminus.respond_to?(:authorized?)
|
42
|
+
terminus.authorized?(request)
|
43
|
+
else
|
44
|
+
true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'puppet/file_serving/content'
|
2
|
+
require 'puppet/indirector/file_content'
|
3
|
+
require 'puppet/indirector/code'
|
4
|
+
require 'puppet/file_serving/terminus_selector'
|
5
|
+
|
6
|
+
class Puppet::Indirector::FileContent::Selector < Puppet::Indirector::Code
|
7
|
+
desc "Select the terminus based on the request"
|
8
|
+
include Puppet::FileServing::TerminusSelector
|
9
|
+
|
10
|
+
def get_terminus(request)
|
11
|
+
indirection.terminus(select(request))
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(request)
|
15
|
+
get_terminus(request).find(request)
|
16
|
+
end
|
17
|
+
|
18
|
+
def search(request)
|
19
|
+
get_terminus(request).search(request)
|
20
|
+
end
|
21
|
+
|
22
|
+
def authorized?(request)
|
23
|
+
terminus = get_terminus(request)
|
24
|
+
if terminus.respond_to?(:authorized?)
|
25
|
+
terminus.authorized?(request)
|
26
|
+
else
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'puppet/file_serving/metadata'
|
2
|
+
require 'puppet/indirector/file_metadata'
|
3
|
+
require 'puppet/indirector/code'
|
4
|
+
require 'puppet/file_serving/terminus_selector'
|
5
|
+
|
6
|
+
class Puppet::Indirector::FileMetadata::Selector < Puppet::Indirector::Code
|
7
|
+
desc "Select the terminus based on the request"
|
8
|
+
include Puppet::FileServing::TerminusSelector
|
9
|
+
|
10
|
+
def get_terminus(request)
|
11
|
+
indirection.terminus(select(request))
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(request)
|
15
|
+
get_terminus(request).find(request)
|
16
|
+
end
|
17
|
+
|
18
|
+
def search(request)
|
19
|
+
get_terminus(request).search(request)
|
20
|
+
end
|
21
|
+
|
22
|
+
def authorized?(request)
|
23
|
+
terminus = get_terminus(request)
|
24
|
+
if terminus.respond_to?(:authorized?)
|
25
|
+
terminus.authorized?(request)
|
26
|
+
else
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -150,7 +150,16 @@ module Puppet
|
|
150
150
|
|
151
151
|
# Does this declaration match the name/ip combo?
|
152
152
|
def match?(name, ip)
|
153
|
-
|
153
|
+
if ip?
|
154
|
+
if pattern.include?(IPAddr.new(ip))
|
155
|
+
Puppet.deprecation_warning "Authentication based on IP address is deprecated; please use certname-based rules instead"
|
156
|
+
true
|
157
|
+
else
|
158
|
+
false
|
159
|
+
end
|
160
|
+
else
|
161
|
+
matchname?(name)
|
162
|
+
end
|
154
163
|
end
|
155
164
|
|
156
165
|
# Set the pattern appropriately. Also sets the name and length.
|
@@ -212,7 +221,6 @@ module Puppet
|
|
212
221
|
|
213
222
|
# Convert the name to a common pattern.
|
214
223
|
def munge_name(name)
|
215
|
-
# LAK:NOTE http://snurl.com/21zf8 [groups_google_com]
|
216
224
|
# Change to name.downcase.split(".",-1).reverse for FQDN support
|
217
225
|
name.downcase.split(".").reverse
|
218
226
|
end
|
data/lib/puppet/reports/store.rb
CHANGED
@@ -2,6 +2,8 @@ require 'puppet'
|
|
2
2
|
require 'fileutils'
|
3
3
|
require 'tempfile'
|
4
4
|
|
5
|
+
SEPARATOR = [Regexp.escape(File::SEPARATOR.to_s), Regexp.escape(File::ALT_SEPARATOR.to_s)].join
|
6
|
+
|
5
7
|
Puppet::Reports.register_report(:store) do
|
6
8
|
desc "Store the yaml report on disk. Each host sends its report as a YAML dump
|
7
9
|
and this just stores the file on disk, in the `reportdir` directory.
|
@@ -13,9 +15,11 @@ Puppet::Reports.register_report(:store) do
|
|
13
15
|
def process
|
14
16
|
# We don't want any tracking back in the fs. Unlikely, but there
|
15
17
|
# you go.
|
16
|
-
|
18
|
+
if host =~ Regexp.union(/[#{SEPARATOR}]/, /\A\.\.?\Z/)
|
19
|
+
raise ArgumentError, "Invalid node name #{host.inspect}"
|
20
|
+
end
|
17
21
|
|
18
|
-
dir = File.join(Puppet[:reportdir],
|
22
|
+
dir = File.join(Puppet[:reportdir], host)
|
19
23
|
|
20
24
|
if ! FileTest.exists?(dir)
|
21
25
|
FileUtils.mkdir_p(dir)
|
@@ -42,17 +46,20 @@ Puppet::Reports.register_report(:store) do
|
|
42
46
|
FileUtils.mv(f.path, file)
|
43
47
|
rescue => detail
|
44
48
|
puts detail.backtrace if Puppet[:trace]
|
45
|
-
Puppet.warning "Could not write report for #{
|
49
|
+
Puppet.warning "Could not write report for #{host} at #{file}: #{detail}"
|
46
50
|
end
|
47
51
|
|
48
52
|
# Only testing cares about the return value
|
49
53
|
file
|
50
54
|
end
|
51
55
|
|
52
|
-
# removes all reports for a given host
|
56
|
+
# removes all reports for a given host?
|
53
57
|
def self.destroy(host)
|
54
|
-
|
55
|
-
|
58
|
+
if host =~ Regexp.union(/[#{SEPARATOR}]/, /\A\.\.?\Z/)
|
59
|
+
raise ArgumentError, "Invalid node name #{host.inspect}"
|
60
|
+
end
|
61
|
+
|
62
|
+
dir = File.join(Puppet[:reportdir], host)
|
56
63
|
|
57
64
|
if File.exists?(dir)
|
58
65
|
Dir.entries(dir).each do |file|
|