puppet 0.9.2
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 +0 -0
- data/COPYING +340 -0
- data/LICENSE +17 -0
- data/README +24 -0
- data/Rakefile +294 -0
- data/TODO +4 -0
- data/bin/cf2puppet +186 -0
- data/bin/puppet +176 -0
- data/bin/puppetca +213 -0
- data/bin/puppetd +246 -0
- data/bin/puppetdoc +184 -0
- data/bin/puppetmasterd +258 -0
- data/examples/code/allatonce +13 -0
- data/examples/code/assignments +11 -0
- data/examples/code/classing +35 -0
- data/examples/code/components +73 -0
- data/examples/code/execs +16 -0
- data/examples/code/failers/badclassnoparam +10 -0
- data/examples/code/failers/badclassparam +10 -0
- data/examples/code/failers/badcompnoparam +9 -0
- data/examples/code/failers/badcompparam +9 -0
- data/examples/code/failers/badtypeparam +3 -0
- data/examples/code/file.bl +11 -0
- data/examples/code/filedefaults +10 -0
- data/examples/code/fileparsing +116 -0
- data/examples/code/filerecursion +15 -0
- data/examples/code/functions +3 -0
- data/examples/code/groups +7 -0
- data/examples/code/head +30 -0
- data/examples/code/importing +8 -0
- data/examples/code/nodes +20 -0
- data/examples/code/one +8 -0
- data/examples/code/relationships +34 -0
- data/examples/code/selectors +28 -0
- data/examples/code/simpletests +11 -0
- data/examples/code/snippets/argumentdefaults +14 -0
- data/examples/code/snippets/casestatement +39 -0
- data/examples/code/snippets/classheirarchy.pp +15 -0
- data/examples/code/snippets/classincludes.pp +17 -0
- data/examples/code/snippets/classpathtest +11 -0
- data/examples/code/snippets/dirchmod +19 -0
- data/examples/code/snippets/failmissingexecpath.pp +13 -0
- data/examples/code/snippets/falsevalues.pp +3 -0
- data/examples/code/snippets/filecreate +11 -0
- data/examples/code/snippets/implicititeration +15 -0
- data/examples/code/snippets/multipleinstances +7 -0
- data/examples/code/snippets/namevartest +9 -0
- data/examples/code/snippets/scopetest +13 -0
- data/examples/code/snippets/selectorvalues.pp +22 -0
- data/examples/code/snippets/simpledefaults +5 -0
- data/examples/code/snippets/simpleselector +38 -0
- data/examples/code/svncommit +13 -0
- data/examples/root/bin/sleeper +69 -0
- data/examples/root/etc/configfile +0 -0
- data/examples/root/etc/debian-passwd +29 -0
- data/examples/root/etc/debian-syslog.conf +71 -0
- data/examples/root/etc/init.d/sleeper +65 -0
- data/examples/root/etc/otherfile +0 -0
- data/examples/root/etc/puppet/fileserver.conf +3 -0
- data/examples/root/etc/puppet/puppetmasterd.conf +10 -0
- data/ext/module:puppet +195 -0
- data/install.rb +270 -0
- data/lib/puppet.rb +249 -0
- data/lib/puppet/base64.rb +19 -0
- data/lib/puppet/client.rb +519 -0
- data/lib/puppet/config.rb +49 -0
- data/lib/puppet/daemon.rb +208 -0
- data/lib/puppet/element.rb +71 -0
- data/lib/puppet/event.rb +259 -0
- data/lib/puppet/log.rb +321 -0
- data/lib/puppet/metric.rb +250 -0
- data/lib/puppet/parsedfile.rb +38 -0
- data/lib/puppet/parser/ast.rb +1560 -0
- data/lib/puppet/parser/interpreter.rb +150 -0
- data/lib/puppet/parser/lexer.rb +226 -0
- data/lib/puppet/parser/parser.rb +1354 -0
- data/lib/puppet/parser/scope.rb +755 -0
- data/lib/puppet/server.rb +170 -0
- data/lib/puppet/server/authstore.rb +227 -0
- data/lib/puppet/server/ca.rb +140 -0
- data/lib/puppet/server/filebucket.rb +147 -0
- data/lib/puppet/server/fileserver.rb +477 -0
- data/lib/puppet/server/logger.rb +43 -0
- data/lib/puppet/server/master.rb +103 -0
- data/lib/puppet/server/servlet.rb +247 -0
- data/lib/puppet/sslcertificates.rb +737 -0
- data/lib/puppet/statechange.rb +150 -0
- data/lib/puppet/storage.rb +95 -0
- data/lib/puppet/transaction.rb +179 -0
- data/lib/puppet/transportable.rb +151 -0
- data/lib/puppet/type.rb +1354 -0
- data/lib/puppet/type/component.rb +141 -0
- data/lib/puppet/type/cron.rb +543 -0
- data/lib/puppet/type/exec.rb +316 -0
- data/lib/puppet/type/group.rb +152 -0
- data/lib/puppet/type/nameservice.rb +3 -0
- data/lib/puppet/type/nameservice/netinfo.rb +173 -0
- data/lib/puppet/type/nameservice/objectadd.rb +146 -0
- data/lib/puppet/type/nameservice/posix.rb +200 -0
- data/lib/puppet/type/package.rb +420 -0
- data/lib/puppet/type/package/apt.rb +70 -0
- data/lib/puppet/type/package/dpkg.rb +108 -0
- data/lib/puppet/type/package/rpm.rb +81 -0
- data/lib/puppet/type/package/sun.rb +117 -0
- data/lib/puppet/type/package/yum.rb +58 -0
- data/lib/puppet/type/pfile.rb +569 -0
- data/lib/puppet/type/pfile/checksum.rb +219 -0
- data/lib/puppet/type/pfile/create.rb +108 -0
- data/lib/puppet/type/pfile/group.rb +129 -0
- data/lib/puppet/type/pfile/mode.rb +131 -0
- data/lib/puppet/type/pfile/source.rb +264 -0
- data/lib/puppet/type/pfile/type.rb +31 -0
- data/lib/puppet/type/pfile/uid.rb +166 -0
- data/lib/puppet/type/pfilebucket.rb +80 -0
- data/lib/puppet/type/pprocess.rb +97 -0
- data/lib/puppet/type/service.rb +347 -0
- data/lib/puppet/type/service/base.rb +17 -0
- data/lib/puppet/type/service/debian.rb +50 -0
- data/lib/puppet/type/service/init.rb +145 -0
- data/lib/puppet/type/service/smf.rb +29 -0
- data/lib/puppet/type/state.rb +182 -0
- data/lib/puppet/type/symlink.rb +183 -0
- data/lib/puppet/type/tidy.rb +183 -0
- data/lib/puppet/type/typegen.rb +149 -0
- data/lib/puppet/type/typegen/filerecord.rb +243 -0
- data/lib/puppet/type/typegen/filetype.rb +316 -0
- data/lib/puppet/type/user.rb +290 -0
- data/lib/puppet/util.rb +138 -0
- data/test/certmgr/certmgr.rb +265 -0
- data/test/client/client.rb +203 -0
- data/test/executables/puppetbin.rb +53 -0
- data/test/executables/puppetca.rb +79 -0
- data/test/executables/puppetd.rb +71 -0
- data/test/executables/puppetmasterd.rb +153 -0
- data/test/executables/puppetmodule.rb +60 -0
- data/test/language/ast.rb +412 -0
- data/test/language/interpreter.rb +71 -0
- data/test/language/scope.rb +412 -0
- data/test/language/snippets.rb +445 -0
- data/test/other/events.rb +111 -0
- data/test/other/log.rb +195 -0
- data/test/other/metrics.rb +92 -0
- data/test/other/overrides.rb +115 -0
- data/test/other/parsedfile.rb +31 -0
- data/test/other/relationships.rb +113 -0
- data/test/other/state.rb +106 -0
- data/test/other/storage.rb +39 -0
- data/test/other/transactions.rb +235 -0
- data/test/parser/lexer.rb +120 -0
- data/test/parser/parser.rb +180 -0
- data/test/puppet/conffiles.rb +104 -0
- data/test/puppet/defaults.rb +100 -0
- data/test/puppet/error.rb +23 -0
- data/test/puppet/utiltest.rb +120 -0
- data/test/puppettest.rb +774 -0
- data/test/server/authstore.rb +209 -0
- data/test/server/bucket.rb +227 -0
- data/test/server/ca.rb +201 -0
- data/test/server/fileserver.rb +710 -0
- data/test/server/logger.rb +175 -0
- data/test/server/master.rb +150 -0
- data/test/server/server.rb +130 -0
- data/test/tagging/tagging.rb +80 -0
- data/test/test +51 -0
- data/test/types/basic.rb +119 -0
- data/test/types/component.rb +272 -0
- data/test/types/cron.rb +261 -0
- data/test/types/exec.rb +273 -0
- data/test/types/file.rb +616 -0
- data/test/types/filebucket.rb +167 -0
- data/test/types/fileignoresource.rb +287 -0
- data/test/types/filesources.rb +587 -0
- data/test/types/filetype.rb +162 -0
- data/test/types/group.rb +271 -0
- data/test/types/package.rb +205 -0
- data/test/types/query.rb +101 -0
- data/test/types/service.rb +100 -0
- data/test/types/symlink.rb +93 -0
- data/test/types/tidy.rb +124 -0
- data/test/types/type.rb +135 -0
- data/test/types/user.rb +371 -0
- metadata +243 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
module Puppet
|
2
|
+
module PackagingType
|
3
|
+
# The packaging system for Debian systems.
|
4
|
+
module DPKG
|
5
|
+
def query
|
6
|
+
packages = []
|
7
|
+
|
8
|
+
# dpkg only prints as many columns as you have available
|
9
|
+
# which means we don't get all of the info
|
10
|
+
# stupid stupid
|
11
|
+
oldcol = ENV["COLUMNS"]
|
12
|
+
ENV["COLUMNS"] = "500"
|
13
|
+
fields = [:desired, :status, :error, :name, :version, :description]
|
14
|
+
|
15
|
+
hash = {}
|
16
|
+
# list out our specific package
|
17
|
+
open("| dpkg -l %s 2>/dev/null" % self.name) { |process|
|
18
|
+
# our regex for matching dpkg output
|
19
|
+
regex = %r{^(.)(.)(.)\s(\S+)\s+(\S+)\s+(.+)$}
|
20
|
+
|
21
|
+
# we only want the last line
|
22
|
+
lines = process.readlines
|
23
|
+
# we've got four header lines, so we should expect all of those
|
24
|
+
# plus our output
|
25
|
+
if lines.length < 5
|
26
|
+
return nil
|
27
|
+
end
|
28
|
+
|
29
|
+
line = lines[-1]
|
30
|
+
|
31
|
+
if match = regex.match(line)
|
32
|
+
fields.zip(match.captures) { |field,value|
|
33
|
+
hash[field] = value
|
34
|
+
}
|
35
|
+
#packages.push Puppet::Type::Package.installedpkg(hash)
|
36
|
+
else
|
37
|
+
raise Puppet::DevError,
|
38
|
+
"failed to match dpkg line %s" % line
|
39
|
+
end
|
40
|
+
}
|
41
|
+
ENV["COLUMNS"] = oldcol
|
42
|
+
|
43
|
+
if hash[:error] != " "
|
44
|
+
raise Puppet::PackageError.new(
|
45
|
+
"Package %s, version %s is in error state: %s" %
|
46
|
+
[hash[:name], hash[:install], hash[:error]]
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
if hash[:status] == "i"
|
51
|
+
hash[:install] = hash[:version]
|
52
|
+
else
|
53
|
+
hash[:install] = :notinstalled
|
54
|
+
end
|
55
|
+
|
56
|
+
return hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def list
|
60
|
+
packages = []
|
61
|
+
|
62
|
+
# dpkg only prints as many columns as you have available
|
63
|
+
# which means we don't get all of the info
|
64
|
+
# stupid stupid
|
65
|
+
oldcol = ENV["COLUMNS"]
|
66
|
+
ENV["COLUMNS"] = "500"
|
67
|
+
|
68
|
+
# list out all of the packages
|
69
|
+
open("| dpkg -l") { |process|
|
70
|
+
# our regex for matching dpkg output
|
71
|
+
regex = %r{^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$}
|
72
|
+
fields = [:status, :name, :install, :description]
|
73
|
+
hash = {}
|
74
|
+
|
75
|
+
5.times { process.gets } # throw away the header
|
76
|
+
|
77
|
+
# now turn each returned line into a package object
|
78
|
+
process.each { |line|
|
79
|
+
if match = regex.match(line)
|
80
|
+
hash.clear
|
81
|
+
|
82
|
+
fields.zip(match.captures) { |field,value|
|
83
|
+
hash[field] = value
|
84
|
+
}
|
85
|
+
packages.push Puppet::Type::Package.installedpkg(hash)
|
86
|
+
else
|
87
|
+
raise Puppet::DevError,
|
88
|
+
"Failed to match dpkg line %s" % line
|
89
|
+
end
|
90
|
+
}
|
91
|
+
}
|
92
|
+
ENV["COLUMNS"] = oldcol
|
93
|
+
|
94
|
+
return packages
|
95
|
+
end
|
96
|
+
|
97
|
+
def remove
|
98
|
+
cmd = "dpkg -r %s" % self.name
|
99
|
+
output = %x{#{cmd} 2>&1}
|
100
|
+
if $? != 0
|
101
|
+
raise Puppet::PackageError.new(output)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# $Id: dpkg.rb 707 2005-09-27 19:59:36Z luke $
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Puppet
|
2
|
+
module PackagingType
|
3
|
+
module RPM
|
4
|
+
def query
|
5
|
+
fields = {
|
6
|
+
:name => "NAME",
|
7
|
+
:install => "VERSION",
|
8
|
+
:description => "DESCRIPTION"
|
9
|
+
}
|
10
|
+
|
11
|
+
cmd = "rpm -q #{self.name} --qf '%s\n'" %
|
12
|
+
"%{NAME} %{VERSION}-%{RELEASE}"
|
13
|
+
|
14
|
+
# list out all of the packages
|
15
|
+
output = %x{#{cmd} 2>/dev/null}.chomp
|
16
|
+
|
17
|
+
if $? != 0
|
18
|
+
return nil
|
19
|
+
end
|
20
|
+
|
21
|
+
regex = %r{^(\S+)\s+(\S+)}
|
22
|
+
#fields = [:name, :install, :description]
|
23
|
+
fields = [:name, :install]
|
24
|
+
hash = {}
|
25
|
+
if match = regex.match(output)
|
26
|
+
fields.zip(match.captures) { |field,value|
|
27
|
+
hash[field] = value
|
28
|
+
}
|
29
|
+
else
|
30
|
+
raise Puppet::DevError,
|
31
|
+
"Failed to match rpm output '%s'" %
|
32
|
+
output
|
33
|
+
end
|
34
|
+
|
35
|
+
return hash
|
36
|
+
end
|
37
|
+
|
38
|
+
def list
|
39
|
+
packages = []
|
40
|
+
|
41
|
+
# list out all of the packages
|
42
|
+
open("| rpm -q -a --qf '%{NAME} %{VERSION}\n'") { |process|
|
43
|
+
# our regex for matching dpkg output
|
44
|
+
regex = %r{^(\S+)\s+(\S+)}
|
45
|
+
fields = [:name, :install]
|
46
|
+
hash = {}
|
47
|
+
|
48
|
+
# now turn each returned line into a package object
|
49
|
+
process.each { |line|
|
50
|
+
if match = regex.match(line)
|
51
|
+
hash.clear
|
52
|
+
|
53
|
+
fields.zip(match.captures) { |field,value|
|
54
|
+
hash[field] = value
|
55
|
+
}
|
56
|
+
packages.push Puppet::Type::Package.installedpkg(hash)
|
57
|
+
else
|
58
|
+
raise "failed to match rpm line %s" % line
|
59
|
+
end
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
return packages
|
64
|
+
end
|
65
|
+
|
66
|
+
# we need package retrieval mechanisms before we can have package
|
67
|
+
# installation mechanisms...
|
68
|
+
#type.install = proc { |pkg|
|
69
|
+
# raise "installation not implemented yet"
|
70
|
+
#}
|
71
|
+
|
72
|
+
def remove
|
73
|
+
cmd = "rpm -e %s" % self.name
|
74
|
+
output = %x{#{cmd}}
|
75
|
+
if $? != 0
|
76
|
+
raise output
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Puppet
|
2
|
+
module PackagingType
|
3
|
+
module Sun
|
4
|
+
def query
|
5
|
+
names = {
|
6
|
+
"PKGINST" => :name,
|
7
|
+
"NAME" => nil,
|
8
|
+
"CATEGORY" => :category,
|
9
|
+
"ARCH" => :platform,
|
10
|
+
"VERSION" => :install,
|
11
|
+
"BASEDIR" => :root,
|
12
|
+
"HOTLINE" => nil,
|
13
|
+
"EMAIL" => nil,
|
14
|
+
"VENDOR" => :vendor,
|
15
|
+
"DESC" => :description,
|
16
|
+
"PSTAMP" => nil,
|
17
|
+
"INSTDATE" => nil,
|
18
|
+
"STATUS" => nil,
|
19
|
+
"FILES" => nil
|
20
|
+
}
|
21
|
+
|
22
|
+
hash = {}
|
23
|
+
|
24
|
+
# list out all of the packages
|
25
|
+
open("| pkginfo -l %s 2>/dev/null" % self.name) { |process|
|
26
|
+
# we're using the long listing, so each line is a separate
|
27
|
+
# piece of information
|
28
|
+
process.each { |line|
|
29
|
+
case line
|
30
|
+
when /^$/: # ignore
|
31
|
+
when /\s*([A-Z]+):\s+(.+)/:
|
32
|
+
name = $1
|
33
|
+
value = $2
|
34
|
+
if names.include?(name)
|
35
|
+
unless names[name].nil?
|
36
|
+
hash[names[name]] = value
|
37
|
+
end
|
38
|
+
else
|
39
|
+
self.err "'pkginfo' returned invalid name %s" %
|
40
|
+
name
|
41
|
+
end
|
42
|
+
when /\s+\d+.+/:
|
43
|
+
# nothing; we're ignoring the FILES info
|
44
|
+
end
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
if hash.empty?
|
49
|
+
return nil
|
50
|
+
else
|
51
|
+
return hash
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def list
|
56
|
+
packages = []
|
57
|
+
hash = {}
|
58
|
+
names = {
|
59
|
+
"PKGINST" => :name,
|
60
|
+
"NAME" => nil,
|
61
|
+
"CATEGORY" => :category,
|
62
|
+
"ARCH" => :platform,
|
63
|
+
"VERSION" => :install,
|
64
|
+
"BASEDIR" => :root,
|
65
|
+
"HOTLINE" => nil,
|
66
|
+
"EMAIL" => nil,
|
67
|
+
"VENDOR" => :vendor,
|
68
|
+
"DESC" => :description,
|
69
|
+
"PSTAMP" => nil,
|
70
|
+
"INSTDATE" => nil,
|
71
|
+
"STATUS" => nil,
|
72
|
+
"FILES" => nil
|
73
|
+
}
|
74
|
+
|
75
|
+
# list out all of the packages
|
76
|
+
open("| pkginfo -l") { |process|
|
77
|
+
# we're using the long listing, so each line is a separate
|
78
|
+
# piece of information
|
79
|
+
process.each { |line|
|
80
|
+
case line
|
81
|
+
when /^$/:
|
82
|
+
packages.push Puppet::Type::Package.installedpkg(hash)
|
83
|
+
hash.clear
|
84
|
+
when /\s*(\w+):\s+(.+)/:
|
85
|
+
name = $1
|
86
|
+
value = $2
|
87
|
+
if names.include?(name)
|
88
|
+
unless names[name].nil?
|
89
|
+
hash[names[name]] = value
|
90
|
+
end
|
91
|
+
else
|
92
|
+
raise "Could not find %s" % name
|
93
|
+
end
|
94
|
+
when /\s+\d+.+/:
|
95
|
+
# nothing; we're ignoring the FILES info
|
96
|
+
end
|
97
|
+
}
|
98
|
+
}
|
99
|
+
return packages
|
100
|
+
end
|
101
|
+
|
102
|
+
# we need package retrieval mechanisms before we can have package
|
103
|
+
# installation mechanisms...
|
104
|
+
#type.install = proc { |pkg|
|
105
|
+
# raise "installation not implemented yet"
|
106
|
+
#}
|
107
|
+
|
108
|
+
def remove
|
109
|
+
cmd = "pkgrm -n %s" % self.name
|
110
|
+
output = %x{#{cmd}}
|
111
|
+
if $? != 0
|
112
|
+
raise output
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Puppet
|
2
|
+
module PackagingType
|
3
|
+
# A derivative of DPKG; this is how most people actually manage
|
4
|
+
# Debian boxes, and the only thing that differs is that it can
|
5
|
+
# install packages from remote sites.
|
6
|
+
module Yum
|
7
|
+
include RPM
|
8
|
+
|
9
|
+
# Install a package using 'apt-get'.
|
10
|
+
def install
|
11
|
+
cmd = "yum -y install %s" % self.name
|
12
|
+
|
13
|
+
self.info "Executing %s" % cmd.inspect
|
14
|
+
output = %x{#{cmd} 2>&1}
|
15
|
+
|
16
|
+
unless $? == 0
|
17
|
+
raise Puppet::PackageError.new(output)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# What's the latest package version available?
|
22
|
+
def latest
|
23
|
+
cmd = "yum list %s" % self.name
|
24
|
+
output = %x{#{cmd} 2>&1}
|
25
|
+
|
26
|
+
unless $? == 0
|
27
|
+
raise Puppet::PackageError.new(output)
|
28
|
+
end
|
29
|
+
|
30
|
+
if output =~ /#{self.name}\S+\s+(\S+)\s/
|
31
|
+
return $1
|
32
|
+
else
|
33
|
+
self.debug "No version"
|
34
|
+
if Puppet[:debug]
|
35
|
+
print output
|
36
|
+
end
|
37
|
+
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def update
|
43
|
+
cmd = "yum -y update %s" % self.name
|
44
|
+
|
45
|
+
self.info "Executing %s" % cmd.inspect
|
46
|
+
output = %x{#{cmd} 2>&1}
|
47
|
+
|
48
|
+
unless $? == 0
|
49
|
+
raise Puppet::PackageError.new(output)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def versionable?
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,569 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'cgi'
|
3
|
+
require 'etc'
|
4
|
+
require 'uri'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'puppet/type/state'
|
7
|
+
require 'puppet/server/fileserver'
|
8
|
+
|
9
|
+
# We put all of the states in separate files, because there are so many
|
10
|
+
# of them.
|
11
|
+
require 'puppet/type/pfile/type'
|
12
|
+
require 'puppet/type/pfile/create'
|
13
|
+
require 'puppet/type/pfile/checksum'
|
14
|
+
require 'puppet/type/pfile/uid'
|
15
|
+
require 'puppet/type/pfile/mode'
|
16
|
+
require 'puppet/type/pfile/group'
|
17
|
+
require 'puppet/type/pfile/source'
|
18
|
+
|
19
|
+
module Puppet
|
20
|
+
class Type
|
21
|
+
class PFile < Type
|
22
|
+
@doc = "Manages local files, including setting ownership and
|
23
|
+
permissions, and allowing creation of both files and directories."
|
24
|
+
|
25
|
+
@states = [
|
26
|
+
Puppet::State::PFileCreate,
|
27
|
+
Puppet::State::PFileChecksum,
|
28
|
+
Puppet::State::PFileSource,
|
29
|
+
Puppet::State::PFileUID,
|
30
|
+
Puppet::State::PFileGroup,
|
31
|
+
Puppet::State::PFileMode,
|
32
|
+
Puppet::State::PFileType
|
33
|
+
]
|
34
|
+
|
35
|
+
@parameters = [
|
36
|
+
:path,
|
37
|
+
:backup,
|
38
|
+
:linkmaker,
|
39
|
+
:recurse,
|
40
|
+
:ignore
|
41
|
+
]
|
42
|
+
|
43
|
+
@paramdoc[:path] = "The path to the file to manage. Must be fully
|
44
|
+
qualified."
|
45
|
+
|
46
|
+
@paramdoc[:backup] = "Whether files should be backed up before
|
47
|
+
being replaced. If a ``filebucket`` is specified, files will be
|
48
|
+
backed up there; else, they will be backed up in the same directory
|
49
|
+
with a ``.puppet-bak`` extension."
|
50
|
+
|
51
|
+
@paramdoc[:linkmaker] = "An internal parameter used by the *symlink*
|
52
|
+
type to do recursive link creation."
|
53
|
+
|
54
|
+
@paramdoc[:recurse] = "Whether and how deeply to do recursive
|
55
|
+
management. **false**/*true*/*inf*/*number*"
|
56
|
+
|
57
|
+
@paramdoc[:ignore] = "A parameter which omits action on files matching
|
58
|
+
specified patterns during recursion. Uses Ruby's builtin globbing
|
59
|
+
engine, so shell metacharacters are fully supported, e.g. ``[a-z]*``.
|
60
|
+
Matches that would descend into the directory structure are ignored,
|
61
|
+
e.g., ``*/*``."
|
62
|
+
|
63
|
+
#no longer a parameter
|
64
|
+
# @paramdoc[:source] = "Where to retrieve the contents of the files.
|
65
|
+
# Currently only supports local copying, but will eventually
|
66
|
+
# support multiple protocols for copying. Arguments are specified
|
67
|
+
# using either a full local path or using a URI (currently only
|
68
|
+
# *file* is supported as a protocol)."
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
@name = :file
|
73
|
+
@namevar = :path
|
74
|
+
|
75
|
+
@depthfirst = false
|
76
|
+
|
77
|
+
PINPARAMS = [:mode, :type, :owner, :group, :checksum]
|
78
|
+
|
79
|
+
|
80
|
+
def argument?(arg)
|
81
|
+
@arghash.include?(arg)
|
82
|
+
end
|
83
|
+
|
84
|
+
def handlebackup(file = nil)
|
85
|
+
# let the path be specified
|
86
|
+
file ||= self[:path]
|
87
|
+
# if they specifically don't want a backup, then just say
|
88
|
+
# we're good
|
89
|
+
unless FileTest.exists?(file)
|
90
|
+
return true
|
91
|
+
end
|
92
|
+
|
93
|
+
unless self[:backup]
|
94
|
+
return true
|
95
|
+
end
|
96
|
+
|
97
|
+
case File.stat(file).ftype
|
98
|
+
when "directory":
|
99
|
+
# we don't need to backup directories
|
100
|
+
return true
|
101
|
+
when "file":
|
102
|
+
backup = self[:backup]
|
103
|
+
case backup
|
104
|
+
when Puppet::Client::Dipper:
|
105
|
+
sum = backup.backup(file)
|
106
|
+
self.info "Filebucketed %s with sum %s" %
|
107
|
+
[file, sum]
|
108
|
+
return true
|
109
|
+
when String:
|
110
|
+
newfile = file + backup
|
111
|
+
if FileTest.exists?(newfile)
|
112
|
+
begin
|
113
|
+
File.unlink(newfile)
|
114
|
+
rescue => detail
|
115
|
+
self.err "Could not remove old backup: %s" %
|
116
|
+
detail
|
117
|
+
return false
|
118
|
+
end
|
119
|
+
end
|
120
|
+
begin
|
121
|
+
FileUtils.cp(file,
|
122
|
+
file + backup)
|
123
|
+
return true
|
124
|
+
rescue => detail
|
125
|
+
# since they said they want a backup, let's error out
|
126
|
+
# if we couldn't make one
|
127
|
+
raise Puppet::Error.new("Could not back %s up: %s" %
|
128
|
+
[file, detail.message])
|
129
|
+
end
|
130
|
+
else
|
131
|
+
self.err "Invalid backup type %s" % backup
|
132
|
+
return false
|
133
|
+
end
|
134
|
+
else
|
135
|
+
self.notice "Cannot backup files of type %s" %
|
136
|
+
File.stat(file).ftype
|
137
|
+
return false
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def handleignore(children)
|
142
|
+
@parameters[:ignore].each { |ignore|
|
143
|
+
ignored = []
|
144
|
+
Dir.glob(File.join(self.name,ignore), File::FNM_DOTMATCH) { |match|
|
145
|
+
ignored.push(File.basename(match))
|
146
|
+
}
|
147
|
+
children = children - ignored
|
148
|
+
}
|
149
|
+
return children
|
150
|
+
end
|
151
|
+
|
152
|
+
def initialize(hash)
|
153
|
+
# clean out as many references to any file paths as possible
|
154
|
+
# this was the source of many, many bugs
|
155
|
+
|
156
|
+
@arghash = self.argclean(hash)
|
157
|
+
@arghash.delete(self.class.namevar)
|
158
|
+
|
159
|
+
if @arghash.include?(:source)
|
160
|
+
@arghash.delete(:source)
|
161
|
+
end
|
162
|
+
|
163
|
+
@stat = nil
|
164
|
+
@parameters = Hash.new(false)
|
165
|
+
|
166
|
+
# default to true
|
167
|
+
self[:backup] = true
|
168
|
+
|
169
|
+
# Used for caching clients
|
170
|
+
@clients = {}
|
171
|
+
|
172
|
+
super
|
173
|
+
end
|
174
|
+
|
175
|
+
def parambackup=(value)
|
176
|
+
case value
|
177
|
+
when false, "false":
|
178
|
+
@parameters[:backup] = false
|
179
|
+
when true, "true":
|
180
|
+
@parameters[:backup] = ".puppet-bak"
|
181
|
+
when Array:
|
182
|
+
case value[0]
|
183
|
+
when "filebucket":
|
184
|
+
if bucket = Puppet::Type::PFileBucket.bucket(value[1])
|
185
|
+
@parameters[:backup] = bucket
|
186
|
+
else
|
187
|
+
@parameters[:backup] = ".puppet-bak"
|
188
|
+
raise Puppet::Error,
|
189
|
+
"Could not retrieve filebucket %s" %
|
190
|
+
value[1]
|
191
|
+
end
|
192
|
+
else
|
193
|
+
raise Puppet::Error, "Invalid backup object type %s" %
|
194
|
+
value[0].inspect
|
195
|
+
end
|
196
|
+
else
|
197
|
+
raise Puppet::Error, "Invalid backup type %s" %
|
198
|
+
value.inspect
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def paramignore=(value)
|
203
|
+
|
204
|
+
#Make sure the value of ignore is in correct type
|
205
|
+
unless value.is_a?(Array) or value.is_a?(String)
|
206
|
+
raise Puppet::DevError.new("Ignore must be a string or an Array")
|
207
|
+
end
|
208
|
+
|
209
|
+
@parameters[:ignore] = value
|
210
|
+
end
|
211
|
+
|
212
|
+
def newchild(path, hash = {})
|
213
|
+
# make local copy of arguments
|
214
|
+
args = @arghash.dup
|
215
|
+
|
216
|
+
if path =~ %r{^#{File::SEPARATOR}}
|
217
|
+
raise Puppet::DevError.new(
|
218
|
+
"Must pass relative paths to PFile#newchild()"
|
219
|
+
)
|
220
|
+
else
|
221
|
+
path = File.join(self.name, path)
|
222
|
+
end
|
223
|
+
|
224
|
+
args[:path] = path
|
225
|
+
|
226
|
+
unless hash.include?(:recurse)
|
227
|
+
if args.include?(:recurse)
|
228
|
+
if args[:recurse].is_a?(Integer)
|
229
|
+
self.notice "Decrementing recurse on %s" % path
|
230
|
+
args[:recurse] -= 1 # reduce the level of recursion
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
hash.each { |key,value|
|
237
|
+
args[key] = value
|
238
|
+
}
|
239
|
+
|
240
|
+
child = nil
|
241
|
+
klass = nil
|
242
|
+
if @parameters[:linkmaker] and args.include?(:source) and
|
243
|
+
! FileTest.directory?(args[:source])
|
244
|
+
klass = Puppet::Type::Symlink
|
245
|
+
|
246
|
+
self.debug "%s is a link" % path
|
247
|
+
# clean up the args a lot for links
|
248
|
+
old = args.dup
|
249
|
+
args = {
|
250
|
+
:target => old[:source],
|
251
|
+
:path => path
|
252
|
+
}
|
253
|
+
else
|
254
|
+
klass = self.class
|
255
|
+
end
|
256
|
+
|
257
|
+
# The child might already exist because 'localrecurse' runs
|
258
|
+
# before 'sourcerecurse'. I could push the override stuff into
|
259
|
+
# a separate method or something, but the work is the same other
|
260
|
+
# than this last bit, so it doesn't really make sense.
|
261
|
+
if child = klass[path]
|
262
|
+
unless @children.include?(child)
|
263
|
+
self.notice "Not managing more explicit file %s" %
|
264
|
+
path
|
265
|
+
return nil
|
266
|
+
end
|
267
|
+
args.each { |var,value|
|
268
|
+
next if var == :path
|
269
|
+
next if var == :name
|
270
|
+
# behave idempotently
|
271
|
+
unless child.should(var) == value
|
272
|
+
child[var] = value
|
273
|
+
end
|
274
|
+
}
|
275
|
+
else # create it anew
|
276
|
+
#notice "Creating new file with args %s" % args.inspect
|
277
|
+
args[:parent] = self
|
278
|
+
begin
|
279
|
+
child = klass.implicitcreate(args)
|
280
|
+
|
281
|
+
# implicit creation can return nil
|
282
|
+
if child.nil?
|
283
|
+
return nil
|
284
|
+
end
|
285
|
+
@children << child
|
286
|
+
rescue Puppet::Error => detail
|
287
|
+
self.notice(
|
288
|
+
"Cannot manage: %s" %
|
289
|
+
[detail.message]
|
290
|
+
)
|
291
|
+
self.debug args.inspect
|
292
|
+
child = nil
|
293
|
+
rescue => detail
|
294
|
+
self.notice(
|
295
|
+
"Cannot manage: %s" %
|
296
|
+
[detail]
|
297
|
+
)
|
298
|
+
self.debug args.inspect
|
299
|
+
child = nil
|
300
|
+
end
|
301
|
+
end
|
302
|
+
return child
|
303
|
+
end
|
304
|
+
|
305
|
+
# Paths are special for files, because we don't actually want to show
|
306
|
+
# the parent's full path.
|
307
|
+
def path
|
308
|
+
unless defined? @path
|
309
|
+
if defined? @parent
|
310
|
+
# We only need to behave specially when our parent is also
|
311
|
+
# a file
|
312
|
+
if @parent.is_a?(self.class)
|
313
|
+
# Remove the parent file name
|
314
|
+
ppath = @parent.path.sub(/\/?file=.+/, '')
|
315
|
+
@path = []
|
316
|
+
if ppath != "/" and ppath != ""
|
317
|
+
@path << ppath
|
318
|
+
end
|
319
|
+
@path << self.class.name.to_s + "=" + self.name
|
320
|
+
else
|
321
|
+
super
|
322
|
+
end
|
323
|
+
else
|
324
|
+
# The top-level name is always puppet[top], so we don't
|
325
|
+
# bother with that. And we don't add the hostname
|
326
|
+
# here, it gets added in the log server thingy.
|
327
|
+
if self.name == "puppet[top]"
|
328
|
+
@path = ["/"]
|
329
|
+
else
|
330
|
+
# We assume that if we don't have a parent that we
|
331
|
+
# should not cache the path
|
332
|
+
@path = [self.class.name.to_s + "=" + self.name]
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
return @path.join("/")
|
338
|
+
end
|
339
|
+
|
340
|
+
# Recurse into the directory. This basically just calls 'localrecurse'
|
341
|
+
# and maybe 'sourcerecurse'.
|
342
|
+
def recurse
|
343
|
+
recurse = @parameters[:recurse]
|
344
|
+
# we might have a string, rather than a number
|
345
|
+
if recurse.is_a?(String)
|
346
|
+
if recurse =~ /^[0-9]+$/
|
347
|
+
recurse = Integer(recurse)
|
348
|
+
#elsif recurse =~ /^inf/ # infinite recursion
|
349
|
+
else # anything else is infinite recursion
|
350
|
+
recurse = true
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# are we at the end of the recursion?
|
355
|
+
if recurse == 0
|
356
|
+
self.info "finished recursing"
|
357
|
+
return
|
358
|
+
end
|
359
|
+
|
360
|
+
if recurse.is_a?(Integer)
|
361
|
+
recurse -= 1
|
362
|
+
end
|
363
|
+
|
364
|
+
self.localrecurse(recurse)
|
365
|
+
if @states.include?(:source)
|
366
|
+
self.sourcerecurse(recurse)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def localrecurse(recurse)
|
371
|
+
unless FileTest.exist?(self.name) and self.stat.directory?
|
372
|
+
#self.info "%s is not a directory; not recursing" %
|
373
|
+
# self.name
|
374
|
+
return
|
375
|
+
end
|
376
|
+
|
377
|
+
unless FileTest.directory? self.name
|
378
|
+
raise Puppet::Error.new(
|
379
|
+
"Uh, somehow trying to manage non-dir %s" % self.name
|
380
|
+
)
|
381
|
+
end
|
382
|
+
unless FileTest.readable? self.name
|
383
|
+
self.notice "Cannot manage %s: permission denied" % self.name
|
384
|
+
return
|
385
|
+
end
|
386
|
+
|
387
|
+
children = Dir.entries(self.name)
|
388
|
+
|
389
|
+
#Get rid of ignored children
|
390
|
+
if @parameters.include?(:ignore)
|
391
|
+
children = handleignore(children)
|
392
|
+
end
|
393
|
+
|
394
|
+
added = []
|
395
|
+
children.each { |file|
|
396
|
+
file = File.basename(file)
|
397
|
+
next if file =~ /^\.\.?$/ # skip . and ..
|
398
|
+
if child = self.newchild(file, :recurse => recurse)
|
399
|
+
unless @children.include?(child)
|
400
|
+
self.push child
|
401
|
+
added.push file
|
402
|
+
|
403
|
+
end
|
404
|
+
child.retrieve
|
405
|
+
end
|
406
|
+
}
|
407
|
+
end
|
408
|
+
|
409
|
+
# This recurses against the remote source and makes sure the local
|
410
|
+
# and remote structures match. It's run after 'localrecurse'.
|
411
|
+
def sourcerecurse(recurse)
|
412
|
+
# FIXME sourcerecurse should support purging non-remote files
|
413
|
+
source = @states[:source].source
|
414
|
+
|
415
|
+
sourceobj, path = uri2obj(source)
|
416
|
+
|
417
|
+
# we'll set this manually as necessary
|
418
|
+
if @arghash.include?(:create)
|
419
|
+
@arghash.delete(:create)
|
420
|
+
end
|
421
|
+
|
422
|
+
# okay, we've got our source object; now we need to
|
423
|
+
# build up a local file structure to match the remote
|
424
|
+
# one
|
425
|
+
|
426
|
+
server = sourceobj.server
|
427
|
+
sum = "md5"
|
428
|
+
if state = self.state(:checksum)
|
429
|
+
sum = state.checktype
|
430
|
+
end
|
431
|
+
r = false
|
432
|
+
if recurse
|
433
|
+
unless recurse == 0
|
434
|
+
r = 1
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
ignore = @parameters[:ignore]
|
439
|
+
|
440
|
+
#self.warning "Listing path %s" % path.inspect
|
441
|
+
desc = server.list(path, r, ignore)
|
442
|
+
|
443
|
+
desc.split("\n").each { |line|
|
444
|
+
file, type = line.split("\t")
|
445
|
+
next if file == "/"
|
446
|
+
name = file.sub(/^\//, '')
|
447
|
+
#self.warning "child name is %s" % name
|
448
|
+
args = {:source => source + file}
|
449
|
+
if type == file
|
450
|
+
args[:recurse] = nil
|
451
|
+
end
|
452
|
+
self.newchild(name, args)
|
453
|
+
#self.newchild(hash, source, recurse)
|
454
|
+
#hash2child(hash, source, recurse)
|
455
|
+
}
|
456
|
+
end
|
457
|
+
|
458
|
+
# a wrapper method to make sure the file exists before doing anything
|
459
|
+
def retrieve
|
460
|
+
if @states.include?(:source)
|
461
|
+
# This probably isn't the best place for it, but we need
|
462
|
+
# to make sure that we have a corresponding checksum state.
|
463
|
+
unless @states.include?(:checksum)
|
464
|
+
self[:checksum] = "md5"
|
465
|
+
end
|
466
|
+
@states[:source].retrieve
|
467
|
+
end
|
468
|
+
|
469
|
+
if @parameters.include?(:recurse)
|
470
|
+
self.recurse
|
471
|
+
end
|
472
|
+
|
473
|
+
unless stat = self.stat(true)
|
474
|
+
self.debug "File does not exist"
|
475
|
+
@states.each { |name,state|
|
476
|
+
# We've already retreived the source, and we don't
|
477
|
+
# want to overwrite whatever it did. This is a bit
|
478
|
+
# of a hack, but oh well, source is definitely special.
|
479
|
+
next if name == :source
|
480
|
+
state.is = :notfound
|
481
|
+
}
|
482
|
+
return
|
483
|
+
end
|
484
|
+
|
485
|
+
super
|
486
|
+
end
|
487
|
+
|
488
|
+
def stat(refresh = false)
|
489
|
+
if @stat.nil? or refresh == true
|
490
|
+
begin
|
491
|
+
@stat = File.lstat(self.name)
|
492
|
+
rescue Errno::ENOENT => error
|
493
|
+
@stat = nil
|
494
|
+
rescue => error
|
495
|
+
self.debug "Failed to stat %s: %s" %
|
496
|
+
[self.name,error]
|
497
|
+
@stat = nil
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
return @stat
|
502
|
+
end
|
503
|
+
|
504
|
+
def uri2obj(source)
|
505
|
+
sourceobj = FileSource.new
|
506
|
+
path = nil
|
507
|
+
if source =~ /^\//
|
508
|
+
source = "file://localhost/%s" % source
|
509
|
+
sourceobj.mount = "localhost"
|
510
|
+
sourceobj.local = true
|
511
|
+
end
|
512
|
+
begin
|
513
|
+
uri = URI.parse(source)
|
514
|
+
rescue => detail
|
515
|
+
raise Puppet::Error, "Could not understand source %s: %s" %
|
516
|
+
[source, detail.to_s]
|
517
|
+
end
|
518
|
+
|
519
|
+
case uri.scheme
|
520
|
+
when "file":
|
521
|
+
unless defined? @@localfileserver
|
522
|
+
@@localfileserver = Puppet::Server::FileServer.new(
|
523
|
+
:Local => true,
|
524
|
+
:Mount => { "/" => "localhost" },
|
525
|
+
:Config => false
|
526
|
+
)
|
527
|
+
#@@localfileserver.mount("/", "localhost")
|
528
|
+
end
|
529
|
+
sourceobj.server = @@localfileserver
|
530
|
+
path = "/localhost" + uri.path
|
531
|
+
when "puppet":
|
532
|
+
args = { :Server => uri.host }
|
533
|
+
if uri.port
|
534
|
+
args[:Port] = uri.port
|
535
|
+
end
|
536
|
+
# FIXME We should cache a copy of this server
|
537
|
+
#sourceobj.server = Puppet::NetworkClient.new(args)
|
538
|
+
unless @clients.include?(source)
|
539
|
+
@clients[source] = Puppet::Client::FileClient.new(args)
|
540
|
+
end
|
541
|
+
sourceobj.server = @clients[source]
|
542
|
+
|
543
|
+
tmp = uri.path
|
544
|
+
if tmp =~ %r{^/(\w+)}
|
545
|
+
sourceobj.mount = $1
|
546
|
+
path = tmp
|
547
|
+
#path = tmp.sub(%r{^/\w+},'') || "/"
|
548
|
+
else
|
549
|
+
raise Puppet::Error, "Invalid source path %s" % tmp
|
550
|
+
end
|
551
|
+
else
|
552
|
+
raise Puppet::Error,
|
553
|
+
"Got other recursive file proto %s from %s" %
|
554
|
+
[uri.scheme, source]
|
555
|
+
end
|
556
|
+
|
557
|
+
return [sourceobj, path.sub(/\/\//, '/')]
|
558
|
+
end
|
559
|
+
end # Puppet::Type::PFile
|
560
|
+
end # Puppet::Type
|
561
|
+
|
562
|
+
# the filesource class can't include the path, because the path
|
563
|
+
# changes for every file instance
|
564
|
+
class FileSource
|
565
|
+
attr_accessor :mount, :root, :server, :local
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
# $Id$
|