Merge branch '0.24.x'
Conflicts: bin/puppetca lib/puppet/type/group.rb lib/puppet/type/tidy.rb lib/puppet/util/settings.rb Also edited the following files so tests will pass: lib/puppet/type/component.rb spec/unit/ssl/certificate_request.rb spec/unit/type/computer.rb spec/unit/type/mcx.rb spec/unit/type/resources.rb spec/unit/util/settings.rb spec/unit/util/storage.rb test/ral/type/zone.rb
This commit is contained in:
Коммит
99a9b5a045
38
CHANGELOG
38
CHANGELOG
|
@ -8,6 +8,44 @@
|
|||
set file paths to 'false' to disable the CRL.
|
||||
|
||||
0.24.x
|
||||
Fixed #1695 - Solaris 10 zone provider doesn't properly handle unknown zone attributes in newer releases
|
||||
|
||||
Fixed #1776 - Trivial fix for gentoo service provider
|
||||
|
||||
Added Rake :ci namespace and CI tasks
|
||||
|
||||
Fixed #1767 - Minor fix to emacs mode
|
||||
|
||||
Fixed #1711 - fileserver test fails due to incorrect mocking
|
||||
|
||||
Fixed #1751 - Mac OS X DirectoryService nameservice provider support for
|
||||
plist output and password hash fil
|
||||
|
||||
Fixed #1752 - Add an optional argument to Puppet::Util.execute to determine
|
||||
whether stderr and stdout are combined in the output
|
||||
|
||||
Added versionable feature to the RPM provider
|
||||
|
||||
Fixed #1668 - puppetca can't clean unsigned certs
|
||||
|
||||
Moved RRD feature from util/metric.rb to feature/base.rb
|
||||
|
||||
Fixed #1735 and #1747 - Fixes to confine system
|
||||
|
||||
Fixed #1681 - Add filesystem type check to test for per-file SELinux context support
|
||||
|
||||
Fixed #1746 - Sync SELinux file attributes after file contents created/modified
|
||||
|
||||
Replaced SELInux calls to binaries with Ruby SELinux bindings
|
||||
|
||||
Fixed #1748 - Include spec directory in packages
|
||||
|
||||
Fixes #1672 - unsafe crontab handling in Solaris
|
||||
|
||||
Fixed #1718 - Added preseed to apt uninstall and purge
|
||||
|
||||
Fixed #1739 - Added uninstall functionality to yum provider
|
||||
|
||||
Fixed #1710 - Spurious output in test run
|
||||
|
||||
Fixed #1667 - Documentation should specify natural language regexs, not Regexp objects
|
||||
|
|
26
Rakefile
26
Rakefile
|
@ -24,6 +24,7 @@ project = Rake::RedLabProject.new("puppet") do |p|
|
|||
'lib/puppet/**/*.rb',
|
||||
'lib/puppet/**/*.py',
|
||||
'test/**/*',
|
||||
'spec/**/*',
|
||||
'bin/**/*',
|
||||
'ext/**/*',
|
||||
'examples/**/*',
|
||||
|
@ -136,7 +137,7 @@ desc "Run the specs under spec/"
|
|||
task :spec do
|
||||
require 'spec'
|
||||
require 'spec/rake/spectask'
|
||||
require 'rcov'
|
||||
# require 'rcov'
|
||||
Spec::Rake::SpecTask.new do |t|
|
||||
# t.rcov = true
|
||||
t.spec_opts = ['--format','s', '--loadby','mtime']
|
||||
|
@ -149,6 +150,29 @@ task :unit do
|
|||
sh "cd test; rake"
|
||||
end
|
||||
|
||||
namespace :ci do
|
||||
|
||||
desc "Run the CI prep tasks"
|
||||
task :prep do
|
||||
require 'rubygems'
|
||||
gem 'ci_reporter'
|
||||
require 'ci/reporter/rake/rspec'
|
||||
require 'ci/reporter/rake/test_unit'
|
||||
ENV['CI_REPORTS'] = 'results'
|
||||
end
|
||||
|
||||
desc "Run CI Unit tests"
|
||||
task :unit => [:prep, 'ci:setup:testunit'] do
|
||||
sh "cd test; rake test; exit 0"
|
||||
end
|
||||
|
||||
desc "Run CI RSpec tests"
|
||||
task :spec => [:prep, 'ci:setup:rspec'] do
|
||||
sh "cd spec; rake all; exit 0"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
desc "Send patch information to the puppet-dev list"
|
||||
task :mail_patches do
|
||||
if Dir.glob("00*.patch").length > 0
|
||||
|
|
|
@ -42,19 +42,10 @@ Autotest.add_hook :initialize do |at|
|
|||
}
|
||||
end
|
||||
|
||||
# a place for overrides when necessary.
|
||||
class Autotest::PuppetRspec < Autotest::Rspec
|
||||
# Autotest will look for spec commands in the following
|
||||
# locations, in this order:
|
||||
#
|
||||
# * bin/spec
|
||||
# * default spec bin/loader installed in Rubygems
|
||||
# * our local vendor/gems/rspec/bin/spec
|
||||
def spec_commands
|
||||
[
|
||||
File.join('vendor', 'gems', 'rspec', 'bin', 'spec') ,
|
||||
File.join('bin', 'spec'),
|
||||
File.join(Config::CONFIG['bindir'], 'spec')
|
||||
]
|
||||
end
|
||||
|
||||
def spec_commands
|
||||
ENV["AUTOTEST"] = "true"
|
||||
ENV["PATH"].split(":").collect { |dir| File.join(dir, "spec") }
|
||||
end
|
||||
end
|
||||
|
|
124
bin/puppetdoc
124
bin/puppetdoc
|
@ -8,25 +8,36 @@
|
|||
#
|
||||
# = Usage
|
||||
#
|
||||
# puppetdoc [-a|--all] [-h|--help] [-m|--mode <text|pdf|trac> [-r|--reference <[type]|configuration|..>]
|
||||
# puppetdoc [-a|--all] [-h|--help] [-o|--outputdir <rdoc outputdir>] [-m|--mode <text|pdf|trac|rdoc>]
|
||||
# [-r|--reference <[type]|configuration|..>] [manifest-file]
|
||||
#
|
||||
# = Description
|
||||
#
|
||||
# This command generates a restructured-text document describing all installed
|
||||
# If mode is not 'rdoc', then this command generates a restructured-text document describing all installed
|
||||
# Puppet types or all allowable arguments to puppet executables. It is largely
|
||||
# meant for internal use and is used to generate the reference document
|
||||
# available on the Reductive Labs web site.
|
||||
#
|
||||
# In 'rdoc' mode, this command generates an html RDoc hierarchy describing the manifests that
|
||||
# are in 'manifestdir' and 'modulepath' configuration directives.
|
||||
# The generated documentation directory is doc by default but can be changed with the 'outputdir' option.
|
||||
#
|
||||
# If the command is started with 'manifest-file' command-line arguments, puppetdoc generate a single
|
||||
# manifest documentation that is output on stdout.
|
||||
#
|
||||
# = Options
|
||||
#
|
||||
# all::
|
||||
# Output the docs for all of the reference types.
|
||||
# Output the docs for all of the reference types. In 'rdoc' modes, this also outputs documentation for all resources
|
||||
#
|
||||
# help::
|
||||
# Print this help message
|
||||
#
|
||||
# outputdir::
|
||||
# Specifies the directory where to output the rdoc documentation in 'rdoc' mode.
|
||||
#
|
||||
# mode::
|
||||
# Determine the output mode. Valid modes are 'text', 'trac', and 'pdf'. Note that 'trac' mode only works on Reductive Labs servers. The default mode is 'text'.
|
||||
# Determine the output mode. Valid modes are 'text', 'trac', 'pdf' and 'rdoc'. Note that 'trac' mode only works on Reductive Labs servers. The default mode is 'text'. In 'rdoc' mode you must provide 'manifests-path'
|
||||
#
|
||||
# reference::
|
||||
# Build a particular reference. Get a list of references by running +puppetdoc --list+.
|
||||
|
@ -34,6 +45,10 @@
|
|||
# = Example
|
||||
#
|
||||
# $ puppetdoc -r type > /tmp/type_reference.rst
|
||||
# or
|
||||
# $ puppetdoc --outputdir /tmp/rdoc --mode rdoc /path/to/manifests
|
||||
# or
|
||||
# $ puppetdoc /etc/puppet/manifests/site.pp
|
||||
#
|
||||
# = Author
|
||||
#
|
||||
|
@ -47,16 +62,24 @@
|
|||
require 'puppet'
|
||||
require 'puppet/util/reference'
|
||||
require 'puppet/network/handler'
|
||||
require 'puppet/util/rdoc'
|
||||
require 'getoptlong'
|
||||
|
||||
result = GetoptLong.new(
|
||||
[ "--all", "-a", GetoptLong::NO_ARGUMENT ],
|
||||
[ "--list", "-l", GetoptLong::NO_ARGUMENT ],
|
||||
[ "--format", "-f", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--mode", "-m", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--reference", "-r", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--help", "-h", GetoptLong::NO_ARGUMENT ]
|
||||
)
|
||||
options = [
|
||||
[ "--all", "-a", GetoptLong::NO_ARGUMENT ],
|
||||
[ "--list", "-l", GetoptLong::NO_ARGUMENT ],
|
||||
[ "--format", "-f", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--mode", "-m", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--reference", "-r", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--help", "-h", GetoptLong::NO_ARGUMENT ],
|
||||
[ "--outputdir", "-o", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--verbose", "-v", GetoptLong::NO_ARGUMENT ],
|
||||
[ "--debug", "-d", GetoptLong::NO_ARGUMENT ]
|
||||
]
|
||||
|
||||
# Add all of the config parameters as valid options.
|
||||
Puppet.settings.addargs(options)
|
||||
result = GetoptLong.new(*options)
|
||||
|
||||
debug = false
|
||||
|
||||
|
@ -66,8 +89,11 @@ options = {:references => [], :mode => :text, :format => :to_rest}
|
|||
Reference = Puppet::Util::Reference
|
||||
|
||||
begin
|
||||
unknown_args = []
|
||||
result.each { |opt,arg|
|
||||
case opt
|
||||
when "--outputdir"
|
||||
options[:outputdir] = arg
|
||||
when "--all"
|
||||
options[:all] = true
|
||||
when "--format"
|
||||
|
@ -78,7 +104,7 @@ begin
|
|||
raise "Invalid output format %s" % arg
|
||||
end
|
||||
when "--mode"
|
||||
if Reference.modes.include?(arg)
|
||||
if Reference.modes.include?(arg) or arg.intern==:rdoc
|
||||
options[:mode] = arg.intern
|
||||
else
|
||||
raise "Invalid output mode %s" % arg
|
||||
|
@ -88,6 +114,10 @@ begin
|
|||
exit(0)
|
||||
when "--reference"
|
||||
options[:references] << arg.intern
|
||||
when "--verbose"
|
||||
options[:verbose] = true
|
||||
when "--debug"
|
||||
options[:debug] = true
|
||||
when "--help"
|
||||
if Puppet.features.usage?
|
||||
RDoc::usage && exit
|
||||
|
@ -95,14 +125,52 @@ begin
|
|||
puts "No help available unless you have RDoc::usage installed"
|
||||
exit
|
||||
end
|
||||
else
|
||||
unknown_args << {:opt => opt, :arg => arg }
|
||||
end
|
||||
}
|
||||
|
||||
# sole manifest documentation
|
||||
if ARGV.size > 0
|
||||
options[:mode] = :rdoc
|
||||
manifest = true
|
||||
end
|
||||
|
||||
# consume the remaining unknown options
|
||||
# and feed them as settings, but only for rdoc mode
|
||||
if options[:mode] == :rdoc and unknown_args.size > 0
|
||||
unknown_args.each do |option|
|
||||
# force absolute path for modulepath when passed on commandline
|
||||
if option[:opt]=="--modulepath" or option[:opt] == "--manifestdir"
|
||||
option[:arg] = option[:arg].split(':').collect { |p| File.expand_path(p) }.join(':')
|
||||
end
|
||||
Puppet.settings.handlearg(option[:opt], option[:arg])
|
||||
end
|
||||
end
|
||||
rescue GetoptLong::InvalidOption => detail
|
||||
$stderr.puts "Try '#{$0} --help'"
|
||||
exit(1)
|
||||
end
|
||||
|
||||
if options[:all]
|
||||
if options[:mode] == :rdoc # rdoc mode
|
||||
# hack to get access to puppetmasterd modulepath and manifestdir
|
||||
Puppet[:name] = "puppetmasterd"
|
||||
# Now parse the config
|
||||
Puppet.parse_config
|
||||
|
||||
# Handle the logging settings.
|
||||
if options[:debug] or options[:verbose]
|
||||
if options[:debug]
|
||||
Puppet::Util::Log.level = :debug
|
||||
else
|
||||
Puppet::Util::Log.level = :info
|
||||
end
|
||||
|
||||
Puppet::Util::Log.newdestination(:console)
|
||||
end
|
||||
end
|
||||
|
||||
if options[:all] and options[:mode] != :rdoc
|
||||
# Don't add dynamic references to the "all" list.
|
||||
options[:references] = Reference.references.reject do |ref|
|
||||
Reference.reference(ref).dynamic?
|
||||
|
@ -114,6 +182,33 @@ if options[:references].empty?
|
|||
end
|
||||
|
||||
case options[:mode]
|
||||
when :rdoc # rdoc or sole manifest mode
|
||||
exit_code = 0
|
||||
files = []
|
||||
unless manifest
|
||||
files += Puppet[:modulepath].split(':').collect { |p| File.expand_path(p) }
|
||||
files += Puppet[:manifestdir].split(':').collect { |p| File.expand_path(p) }
|
||||
end
|
||||
files += ARGV
|
||||
Puppet.info "scanning: %s" % files.inspect
|
||||
Puppet.settings.setdefaults("puppetdoc",
|
||||
"document_all" => [false, "Document all resources"]
|
||||
)
|
||||
Puppet.settings[:document_all] = options[:all] || false
|
||||
begin
|
||||
if manifest
|
||||
Puppet::Util::RDoc.manifestdoc(files)
|
||||
else
|
||||
Puppet::Util::RDoc.rdoc(options[:outputdir], files)
|
||||
end
|
||||
rescue => detail
|
||||
if Puppet[:trace]
|
||||
puts detail.backtrace
|
||||
end
|
||||
$stderr.puts "Could not generate documentation: %s" % detail
|
||||
exit_code = 1
|
||||
end
|
||||
exit exit_code
|
||||
when :trac
|
||||
options[:references].each do |name|
|
||||
section = Puppet::Util::Reference.reference(name) or raise "Could not find section %s" % name
|
||||
|
@ -159,4 +254,3 @@ else
|
|||
exit exit_code
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
mcx { '/Groups/mcx_dock':
|
||||
ensure => 'absent',
|
||||
content => 'absent'
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
mcx { '/Groups/mcx_dock':
|
||||
ensure => 'present',
|
||||
content => '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.dock</key>
|
||||
<dict>
|
||||
<key>AppItems-Raw</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>upk</key>
|
||||
<dict>
|
||||
<key>mcx_input_key_names</key>
|
||||
<array>
|
||||
<string>AppItems-Raw</string>
|
||||
</array>
|
||||
<key>mcx_output_key_name</key>
|
||||
<string>static-apps</string>
|
||||
<key>mcx_remove_duplicates</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>value</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>mcx_typehint</key>
|
||||
<integer>1</integer>
|
||||
<key>tile-data</key>
|
||||
<dict>
|
||||
<key>file-data</key>
|
||||
<dict>
|
||||
<key>_CFURLString</key>
|
||||
<string>/Applications/Mail.app</string>
|
||||
<key>_CFURLStringType</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>file-label</key>
|
||||
<string>Mail</string>
|
||||
</dict>
|
||||
<key>tile-type</key>
|
||||
<string>file-tile</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>mcx_typehint</key>
|
||||
<integer>1</integer>
|
||||
<key>tile-data</key>
|
||||
<dict>
|
||||
<key>file-data</key>
|
||||
<dict>
|
||||
<key>_CFURLString</key>
|
||||
<string>/Applications/Safari.app</string>
|
||||
<key>_CFURLStringType</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>file-label</key>
|
||||
<string>Safari</string>
|
||||
</dict>
|
||||
<key>tile-type</key>
|
||||
<string>file-tile</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>DocItems-Raw</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>upk</key>
|
||||
<dict>
|
||||
<key>mcx_input_key_names</key>
|
||||
<array>
|
||||
<string>DocItems-Raw</string>
|
||||
</array>
|
||||
<key>mcx_output_key_name</key>
|
||||
<string>static-others</string>
|
||||
<key>mcx_remove_duplicates</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>value</key>
|
||||
<array/>
|
||||
</dict>
|
||||
<key>MCXDockSpecialFolders-Raw</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>upk</key>
|
||||
<dict>
|
||||
<key>mcx_input_key_names</key>
|
||||
<array>
|
||||
<string>MCXDockSpecialFolders-Raw</string>
|
||||
</array>
|
||||
<key>mcx_output_key_name</key>
|
||||
<string>MCXDockSpecialFolders</string>
|
||||
<key>mcx_remove_duplicates</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>value</key>
|
||||
<array/>
|
||||
</dict>
|
||||
<key>contents-immutable</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>value</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>static-only</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>value</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
'
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
# Mac MCX Test
|
||||
|
||||
computer { "localhost": }
|
||||
|
||||
mcx {
|
||||
"mcx_dock":
|
||||
ensure => "present",
|
||||
ds_type => "group",
|
||||
ds_name => "mcx_dock",
|
||||
content => '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.dock</key>
|
||||
<dict>
|
||||
<key>AppItems-Raw</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>upk</key>
|
||||
<dict>
|
||||
<key>mcx_input_key_names</key>
|
||||
<array>
|
||||
<string>AppItems-Raw</string>
|
||||
</array>
|
||||
<key>mcx_output_key_name</key>
|
||||
<string>static-apps</string>
|
||||
<key>mcx_remove_duplicates</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>value</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>mcx_typehint</key>
|
||||
<integer>1</integer>
|
||||
<key>tile-data</key>
|
||||
<dict>
|
||||
<key>file-data</key>
|
||||
<dict>
|
||||
<key>_CFURLString</key>
|
||||
<string>/Applications/Mail.app</string>
|
||||
<key>_CFURLStringType</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>file-label</key>
|
||||
<string>Mail</string>
|
||||
</dict>
|
||||
<key>tile-type</key>
|
||||
<string>file-tile</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>mcx_typehint</key>
|
||||
<integer>1</integer>
|
||||
<key>tile-data</key>
|
||||
<dict>
|
||||
<key>file-data</key>
|
||||
<dict>
|
||||
<key>_CFURLString</key>
|
||||
<string>/Applications/Safari.app</string>
|
||||
<key>_CFURLStringType</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>file-label</key>
|
||||
<string>Safari</string>
|
||||
</dict>
|
||||
<key>tile-type</key>
|
||||
<string>file-tile</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>DocItems-Raw</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>upk</key>
|
||||
<dict>
|
||||
<key>mcx_input_key_names</key>
|
||||
<array>
|
||||
<string>DocItems-Raw</string>
|
||||
</array>
|
||||
<key>mcx_output_key_name</key>
|
||||
<string>static-others</string>
|
||||
<key>mcx_remove_duplicates</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>value</key>
|
||||
<array/>
|
||||
</dict>
|
||||
<key>MCXDockSpecialFolders-Raw</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>upk</key>
|
||||
<dict>
|
||||
<key>mcx_input_key_names</key>
|
||||
<array>
|
||||
<string>MCXDockSpecialFolders-Raw</string>
|
||||
</array>
|
||||
<key>mcx_output_key_name</key>
|
||||
<string>MCXDockSpecialFolders</string>
|
||||
<key>mcx_remove_duplicates</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>value</key>
|
||||
<array/>
|
||||
</dict>
|
||||
<key>contents-immutable</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>value</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>static-only</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>value</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
'
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
# Mac MCX Test
|
||||
|
||||
computer { "localhost": }
|
||||
|
||||
mcx {
|
||||
"/Groups/mcx_dock":
|
||||
ensure => "present",
|
||||
content => 'invalid plist'
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
mcx { '/Groups/doesnotexist':
|
||||
ensure => 'present',
|
||||
content => '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.dock</key>
|
||||
<dict>
|
||||
<key>AppItems-Raw</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>upk</key>
|
||||
<dict>
|
||||
<key>mcx_input_key_names</key>
|
||||
<array>
|
||||
<string>AppItems-Raw</string>
|
||||
</array>
|
||||
<key>mcx_output_key_name</key>
|
||||
<string>static-apps</string>
|
||||
<key>mcx_remove_duplicates</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>value</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>mcx_typehint</key>
|
||||
<integer>1</integer>
|
||||
<key>tile-data</key>
|
||||
<dict>
|
||||
<key>file-data</key>
|
||||
<dict>
|
||||
<key>_CFURLString</key>
|
||||
<string>/Applications/Mail.app</string>
|
||||
<key>_CFURLStringType</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>file-label</key>
|
||||
<string>Mail</string>
|
||||
</dict>
|
||||
<key>tile-type</key>
|
||||
<string>file-tile</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>mcx_typehint</key>
|
||||
<integer>1</integer>
|
||||
<key>tile-data</key>
|
||||
<dict>
|
||||
<key>file-data</key>
|
||||
<dict>
|
||||
<key>_CFURLString</key>
|
||||
<string>/Applications/Safari.app</string>
|
||||
<key>_CFURLStringType</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>file-label</key>
|
||||
<string>Safari</string>
|
||||
</dict>
|
||||
<key>tile-type</key>
|
||||
<string>file-tile</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>DocItems-Raw</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>upk</key>
|
||||
<dict>
|
||||
<key>mcx_input_key_names</key>
|
||||
<array>
|
||||
<string>DocItems-Raw</string>
|
||||
</array>
|
||||
<key>mcx_output_key_name</key>
|
||||
<string>static-others</string>
|
||||
<key>mcx_remove_duplicates</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>value</key>
|
||||
<array/>
|
||||
</dict>
|
||||
<key>MCXDockSpecialFolders-Raw</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>upk</key>
|
||||
<dict>
|
||||
<key>mcx_input_key_names</key>
|
||||
<array>
|
||||
<string>MCXDockSpecialFolders-Raw</string>
|
||||
</array>
|
||||
<key>mcx_output_key_name</key>
|
||||
<string>MCXDockSpecialFolders</string>
|
||||
<key>mcx_remove_duplicates</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>value</key>
|
||||
<array/>
|
||||
</dict>
|
||||
<key>contents-immutable</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>value</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>static-only</key>
|
||||
<dict>
|
||||
<key>state</key>
|
||||
<string>always</string>
|
||||
<key>value</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
'
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
mcx { '/Groups/foobar':
|
||||
ensure => 'absent',
|
||||
content => 'absent'
|
||||
}
|
|
@ -226,7 +226,7 @@ of the initial include plus puppet-include-indent."
|
|||
;; Semicolon ends a block for a resource when multiple resources
|
||||
;; are defined in the same block, but try not to get the case of
|
||||
;; a complete resource on a single line wrong.
|
||||
((looking-at "^\\([^'\":\n]\\|\"[^\n\"]*\"\\|'[^\n']'\\)**;\\s-*$")
|
||||
((looking-at "^\\([^'\":\n]\\|\"[^\n\"]*\"\\|'[^\n']*'\\)*;\\s-*$")
|
||||
(setq cur-indent (- (current-indentation) puppet-indent-level))
|
||||
(setq not-indented nil))
|
||||
|
||||
|
|
|
@ -320,7 +320,7 @@ module Puppet
|
|||
a proxy in front of the process or processes, since Mongrel cannot
|
||||
speak SSL.",
|
||||
:call_on_define => true, # Call our hook with the default value, so we always get the correct bind address set.
|
||||
:hook => proc { |value| value == "webrick" ? parent[:bindaddress] = "0.0.0.0" : parent[:bindaddress] = "127.0.0.1" if parent[:bindaddress] == "" }
|
||||
:hook => proc { |value| value == "webrick" ? Puppet.settings[:bindaddress] = "0.0.0.0" : Puppet.settings[:bindaddress] = "127.0.0.1" if Puppet.settings[:bindaddress] == "" }
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -25,3 +25,6 @@ Puppet.features.add :diff, :libs => %w{diff/lcs diff/lcs/hunk}
|
|||
|
||||
# We have augeas
|
||||
Puppet.features.add(:augeas, :libs => ["augeas"])
|
||||
|
||||
# We have RRD available
|
||||
Puppet.features.add(:rrd, :libs => ["RRDtool"])
|
||||
|
|
|
@ -24,7 +24,7 @@ class Puppet::Module
|
|||
def self.templatepath(environment = nil)
|
||||
dirs = Puppet.settings.value(:templatedir, environment).split(":")
|
||||
dirs.select do |p|
|
||||
p =~ /^#{File::SEPARATOR}/ && File::directory?(p)
|
||||
File::directory?(p)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -487,7 +487,7 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
|
|||
return unless Puppet[:splay]
|
||||
return if splayed?
|
||||
|
||||
time = rand(Integer(Puppet[:splaylimit]))
|
||||
time = rand(Integer(Puppet[:splaylimit]) + 1)
|
||||
Puppet.info "Sleeping for %s seconds (splay is enabled)" % time
|
||||
sleep(time)
|
||||
@splayed = true
|
||||
|
|
|
@ -73,7 +73,9 @@ module Puppet::Network
|
|||
rescue Timeout::Error => detail
|
||||
Puppet.err "Connection timeout calling %s.%s: %s" %
|
||||
[namespace, method, detail.to_s]
|
||||
raise XMLRPCClientError.new("Connection Timeout").set_backtrace(detail.backtrace)
|
||||
error = XMLRPCClientError.new("Connection Timeout")
|
||||
error.set_backtrace(detail.backtrace)
|
||||
raise error
|
||||
rescue => detail
|
||||
if detail.message =~ /^Wrong size\. Was \d+, should be \d+$/
|
||||
Puppet.warning "XMLRPC returned wrong size. Retrying."
|
||||
|
|
|
@ -12,8 +12,23 @@ class Puppet::Parser::AST
|
|||
|
||||
include Puppet::Util::Errors
|
||||
include Puppet::Util::MethodHelper
|
||||
include Puppet::Util::Docs
|
||||
|
||||
attr_accessor :line, :file, :parent, :scope
|
||||
|
||||
# don't fetch lexer comment by default
|
||||
def use_docs
|
||||
self.class.use_docs
|
||||
end
|
||||
|
||||
# allow our subclass to specify they want documentation
|
||||
class << self
|
||||
attr_accessor :use_docs
|
||||
def associates_doc
|
||||
self.use_docs = true
|
||||
end
|
||||
end
|
||||
|
||||
# Does this ast object set something? If so, it gets evaluated first.
|
||||
def self.settor?
|
||||
if defined? @settor
|
||||
|
|
|
@ -6,6 +6,8 @@ class Puppet::Parser::AST
|
|||
class CaseStatement < AST::Branch
|
||||
attr_accessor :test, :options, :default
|
||||
|
||||
associates_doc
|
||||
|
||||
# Short-curcuit evaluation. Return the value of the statements for
|
||||
# the first option that matches.
|
||||
def evaluate(scope)
|
||||
|
|
|
@ -8,6 +8,8 @@ class Puppet::Parser::AST
|
|||
class Collection < AST::Branch
|
||||
attr_accessor :type, :query, :form
|
||||
|
||||
associates_doc
|
||||
|
||||
# We return an object that does a late-binding evaluation.
|
||||
def evaluate(scope)
|
||||
if self.query
|
||||
|
|
|
@ -18,6 +18,10 @@ class Puppet::Parser::AST
|
|||
lval = @lval.safeevaluate(scope)
|
||||
rval = @rval.safeevaluate(scope)
|
||||
|
||||
# convert to number if operands are number
|
||||
lval = Puppet::Parser::Scope.number?(lval) || lval
|
||||
rval = Puppet::Parser::Scope.number?(rval) || rval
|
||||
|
||||
# return result
|
||||
unless @operator == '!='
|
||||
lval.send(@operator,rval)
|
||||
|
|
|
@ -10,6 +10,8 @@ class Puppet::Parser::AST::Definition < Puppet::Parser::AST::Branch
|
|||
attr_accessor :name
|
||||
end
|
||||
|
||||
associates_doc
|
||||
|
||||
# The class name
|
||||
@name = :definition
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ class Puppet::Parser::AST
|
|||
# A separate ElseIf statement; can function as an 'else' if there's no
|
||||
# test.
|
||||
class Else < AST::Branch
|
||||
|
||||
associates_doc
|
||||
|
||||
attr_accessor :statements
|
||||
|
||||
def each
|
||||
|
|
|
@ -3,25 +3,17 @@ require 'puppet/parser/ast/branch'
|
|||
class Puppet::Parser::AST
|
||||
# An AST object to call a function.
|
||||
class Function < AST::Branch
|
||||
|
||||
associates_doc
|
||||
|
||||
attr_accessor :name, :arguments
|
||||
|
||||
@settor = true
|
||||
|
||||
def evaluate(scope)
|
||||
# We don't need to evaluate the name, because it's plaintext
|
||||
args = @arguments.safeevaluate(scope)
|
||||
|
||||
return scope.send("function_" + @name, args)
|
||||
end
|
||||
|
||||
def initialize(hash)
|
||||
@ftype = hash[:ftype] || :rvalue
|
||||
hash.delete(:ftype) if hash.include? :ftype
|
||||
|
||||
super(hash)
|
||||
|
||||
# Make sure it's a defined function
|
||||
unless @fname = Puppet::Parser::Functions.function(@name)
|
||||
unless @fname
|
||||
raise Puppet::ParseError, "Unknown function %s" % @name
|
||||
end
|
||||
|
||||
|
@ -42,6 +34,21 @@ class Puppet::Parser::AST
|
|||
raise Puppet::DevError, "Invalid function type %s" % @ftype.inspect
|
||||
end
|
||||
|
||||
|
||||
|
||||
# We don't need to evaluate the name, because it's plaintext
|
||||
args = @arguments.safeevaluate(scope)
|
||||
|
||||
return scope.send("function_" + @name, args)
|
||||
end
|
||||
|
||||
def initialize(hash)
|
||||
@ftype = hash[:ftype] || :rvalue
|
||||
hash.delete(:ftype) if hash.include? :ftype
|
||||
|
||||
super(hash)
|
||||
|
||||
@fname = Puppet::Parser::Functions.function(@name)
|
||||
# Lastly, check the parity
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,9 @@ require 'puppet/parser/ast/definition'
|
|||
# in that each class is a singleton -- only one will exist for a given
|
||||
# node.
|
||||
class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition
|
||||
|
||||
associates_doc
|
||||
|
||||
@name = :class
|
||||
|
||||
# Are we a child of the passed class? Do a recursive search up our
|
||||
|
|
|
@ -3,6 +3,9 @@ require 'puppet/parser/ast/branch'
|
|||
class Puppet::Parser::AST
|
||||
# A basic 'if/elsif/else' statement.
|
||||
class IfStatement < AST::Branch
|
||||
|
||||
associates_doc
|
||||
|
||||
attr_accessor :test, :else, :statements
|
||||
|
||||
def each
|
||||
|
|
|
@ -3,6 +3,9 @@ require 'puppet/parser/ast/hostclass'
|
|||
# The specific code associated with a host. Nodes are annoyingly unlike
|
||||
# other objects. That's just the way it is, at least for now.
|
||||
class Puppet::Parser::AST::Node < Puppet::Parser::AST::HostClass
|
||||
|
||||
associates_doc
|
||||
|
||||
@name = :node
|
||||
|
||||
def initialize(options)
|
||||
|
|
|
@ -4,6 +4,9 @@ require 'puppet/parser/ast/resource_reference'
|
|||
# builtin type.
|
||||
class Puppet::Parser::AST
|
||||
class Resource < AST::ResourceReference
|
||||
|
||||
associates_doc
|
||||
|
||||
attr_accessor :title, :type, :exported, :virtual
|
||||
attr_reader :params
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ class Puppet::Parser::AST
|
|||
class ResourceDefaults < AST::Branch
|
||||
attr_accessor :type, :params
|
||||
|
||||
associates_doc
|
||||
|
||||
# As opposed to ResourceDef, this stores each default for the given
|
||||
# object type.
|
||||
def evaluate(scope)
|
||||
|
|
|
@ -4,6 +4,9 @@ class Puppet::Parser::AST
|
|||
# Set a parameter on a resource specification created somewhere else in the
|
||||
# configuration. The object is responsible for verifying that this is allowed.
|
||||
class ResourceOverride < Resource
|
||||
|
||||
associates_doc
|
||||
|
||||
attr_accessor :object
|
||||
attr_reader :params
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@ require 'puppet/parser/ast/branch'
|
|||
class Puppet::Parser::AST
|
||||
# Define a variable. Stores the value in the current scope.
|
||||
class VarDef < AST::Branch
|
||||
|
||||
associates_doc
|
||||
|
||||
attr_accessor :name, :value, :append
|
||||
|
||||
@settor = true
|
||||
|
|
|
@ -54,6 +54,20 @@ module Functions
|
|||
end
|
||||
end
|
||||
|
||||
# Remove a function added by newfunction
|
||||
def self.rmfunction(name)
|
||||
name = symbolize(name)
|
||||
|
||||
unless @functions.include? name
|
||||
raise Puppet::DevError, "Function %s is not defined" % name
|
||||
end
|
||||
|
||||
@functions.delete(name)
|
||||
|
||||
fname = "function_" + name.to_s
|
||||
Puppet::Parser::Scope.send(:remove_method, fname)
|
||||
end
|
||||
|
||||
# Determine if a given name is a function
|
||||
def self.function(name)
|
||||
name = symbolize(name)
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
Puppet::Parser::Functions::newfunction(:inline_template, :type => :rvalue, :doc =>
|
||||
"Evaluate a template string and return its value. See `the templating docs
|
||||
</trac/puppet/wiki/PuppetTemplating>`_ for more information. Note that
|
||||
if multiple template strings are specified, their output is all concatenated
|
||||
and returned as the output of the function.") do |vals|
|
||||
require 'erb'
|
||||
|
||||
vals.collect do |string|
|
||||
# Use a wrapper, so the template can't get access to the full
|
||||
# Scope object.
|
||||
|
||||
wrapper = Puppet::Parser::TemplateWrapper.new(self)
|
||||
begin
|
||||
wrapper.result(string)
|
||||
rescue => detail
|
||||
raise Puppet::ParseError,
|
||||
"Failed to parse inline template: %s" %
|
||||
[detail]
|
||||
end
|
||||
end.join("")
|
||||
end
|
|
@ -9,10 +9,11 @@ Puppet::Parser::Functions::newfunction(:template, :type => :rvalue, :doc =>
|
|||
# Use a wrapper, so the template can't get access to the full
|
||||
# Scope object.
|
||||
debug "Retrieving template %s" % file
|
||||
wrapper = Puppet::Parser::TemplateWrapper.new(self, file)
|
||||
|
||||
wrapper = Puppet::Parser::TemplateWrapper.new(self)
|
||||
wrapper.file = file
|
||||
begin
|
||||
wrapper.result()
|
||||
wrapper.result
|
||||
rescue => detail
|
||||
raise Puppet::ParseError,
|
||||
"Failed to parse template %s: %s" %
|
||||
|
|
|
@ -130,6 +130,7 @@ namestring: name
|
|||
}
|
||||
|
||||
resource: classname LBRACE resourceinstances endsemi RBRACE {
|
||||
@lexer.commentpop
|
||||
array = val[2]
|
||||
if array.instance_of?(AST::ResourceInstance)
|
||||
array = [array]
|
||||
|
@ -158,6 +159,7 @@ resource: classname LBRACE resourceinstances endsemi RBRACE {
|
|||
|
||||
# Override a value set elsewhere in the configuration.
|
||||
resourceoverride: resourceref LBRACE anyparams endcomma RBRACE {
|
||||
@lexer.commentpop
|
||||
result = ast AST::ResourceOverride, :object => val[0], :params => val[2]
|
||||
}
|
||||
|
||||
|
@ -198,7 +200,7 @@ collection: classref collectrhand {
|
|||
Puppet.warning addcontext("Collection names must now be capitalized")
|
||||
end
|
||||
type = val[0].downcase
|
||||
args = {:type => type}
|
||||
args = {:type => type }
|
||||
|
||||
if val[1].is_a?(AST::CollExpr)
|
||||
args[:query] = val[1]
|
||||
|
@ -410,6 +412,7 @@ resourceref: NAME LBRACK rvalues RBRACK {
|
|||
}
|
||||
|
||||
ifstatement: IF expression LBRACE statements RBRACE else {
|
||||
@lexer.commentpop
|
||||
args = {
|
||||
:test => val[1],
|
||||
:statements => val[3]
|
||||
|
@ -422,6 +425,7 @@ ifstatement: IF expression LBRACE statements RBRACE else {
|
|||
result = ast AST::IfStatement, args
|
||||
}
|
||||
| IF expression LBRACE RBRACE else {
|
||||
@lexer.commentpop
|
||||
args = {
|
||||
:test => val[1],
|
||||
:statements => ast(AST::Nop)
|
||||
|
@ -436,9 +440,11 @@ ifstatement: IF expression LBRACE statements RBRACE else {
|
|||
|
||||
else: # nothing
|
||||
| ELSE LBRACE statements RBRACE {
|
||||
@lexer.commentpop
|
||||
result = ast AST::Else, :statements => val[2]
|
||||
}
|
||||
| ELSE LBRACE RBRACE {
|
||||
@lexer.commentpop
|
||||
result = ast AST::Else, :statements => ast(AST::Nop)
|
||||
}
|
||||
|
||||
|
@ -508,6 +514,7 @@ expression: rvalue
|
|||
}
|
||||
|
||||
casestatement: CASE rvalue LBRACE caseopts RBRACE {
|
||||
@lexer.commentpop
|
||||
options = val[3]
|
||||
unless options.instance_of?(AST::ASTArray)
|
||||
options = ast AST::ASTArray, :children => [val[3]]
|
||||
|
@ -526,8 +533,10 @@ caseopts: caseopt
|
|||
}
|
||||
|
||||
caseopt: casevalues COLON LBRACE statements RBRACE {
|
||||
@lexer.commentpop
|
||||
result = ast AST::CaseOpt, :value => val[0], :statements => val[3]
|
||||
} | casevalues COLON LBRACE RBRACE {
|
||||
@lexer.commentpop
|
||||
result = ast(AST::CaseOpt,
|
||||
:value => val[0],
|
||||
:statements => ast(AST::ASTArray)
|
||||
|
@ -549,7 +558,10 @@ selector: selectlhand QMARK svalues {
|
|||
}
|
||||
|
||||
svalues: selectval
|
||||
| LBRACE sintvalues endcomma RBRACE { result = val[1] }
|
||||
| LBRACE sintvalues endcomma RBRACE {
|
||||
@lexer.commentpop
|
||||
result = val[1]
|
||||
}
|
||||
|
||||
sintvalues: selectval
|
||||
| sintvalues comma selectval {
|
||||
|
@ -593,12 +605,14 @@ import: IMPORT qtexts {
|
|||
# Disable definition inheritance for now. 8/27/06, luke
|
||||
#definition: DEFINE NAME argumentlist parent LBRACE statements RBRACE {
|
||||
definition: DEFINE classname argumentlist LBRACE statements RBRACE {
|
||||
@lexer.commentpop
|
||||
newdefine classname(val[1]), :arguments => val[2], :code => val[4]
|
||||
@lexer.indefine = false
|
||||
result = nil
|
||||
|
||||
#} | DEFINE NAME argumentlist parent LBRACE RBRACE {
|
||||
} | DEFINE classname argumentlist LBRACE RBRACE {
|
||||
@lexer.commentpop
|
||||
newdefine classname(val[1]), :arguments => val[2]
|
||||
@lexer.indefine = false
|
||||
result = nil
|
||||
|
@ -606,11 +620,13 @@ definition: DEFINE classname argumentlist LBRACE statements RBRACE {
|
|||
|
||||
#hostclass: CLASS NAME argumentlist parent LBRACE statements RBRACE {
|
||||
hostclass: CLASS classname classparent LBRACE statements RBRACE {
|
||||
@lexer.commentpop
|
||||
# Our class gets defined in the parent namespace, not our own.
|
||||
@lexer.namepop
|
||||
newclass classname(val[1]), :code => val[4], :parent => val[2]
|
||||
result = nil
|
||||
} | CLASS classname classparent LBRACE RBRACE {
|
||||
@lexer.commentpop
|
||||
# Our class gets defined in the parent namespace, not our own.
|
||||
@lexer.namepop
|
||||
newclass classname(val[1]), :parent => val[2]
|
||||
|
@ -618,9 +634,11 @@ hostclass: CLASS classname classparent LBRACE statements RBRACE {
|
|||
}
|
||||
|
||||
nodedef: NODE hostnames nodeparent LBRACE statements RBRACE {
|
||||
@lexer.commentpop
|
||||
newnode val[1], :parent => val[2], :code => val[4]
|
||||
result = nil
|
||||
} | NODE hostnames nodeparent LBRACE RBRACE {
|
||||
@lexer.commentpop
|
||||
newnode val[1], :parent => val[2]
|
||||
result = nil
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ class Puppet::Parser::Lexer
|
|||
|
||||
# Our base token class.
|
||||
class Token
|
||||
attr_accessor :regex, :name, :string, :skip, :incr_line, :skip_text
|
||||
attr_accessor :regex, :name, :string, :skip, :incr_line, :skip_text, :accumulate
|
||||
|
||||
def initialize(regex, name)
|
||||
if regex.is_a?(String)
|
||||
|
@ -28,8 +28,10 @@ class Puppet::Parser::Lexer
|
|||
end
|
||||
end
|
||||
|
||||
def skip?
|
||||
self.skip
|
||||
%w{skip accumulate}.each do |method|
|
||||
define_method(method+"?") do
|
||||
self.send(method)
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
|
@ -132,7 +134,7 @@ class Puppet::Parser::Lexer
|
|||
'*' => :TIMES,
|
||||
'<<' => :LSHIFT,
|
||||
'>>' => :RSHIFT,
|
||||
%r{([a-z][-\w]*::)+[a-z][-\w]*} => :CLASSNAME,
|
||||
%r{([a-z][-\w]*)?(::[a-z][-\w]*)+} => :CLASSNAME, # Require '::' in the class name, else we'd compete with NAME
|
||||
%r{((::){0,1}[A-Z][-\w]*)+} => :CLASSREF
|
||||
)
|
||||
|
||||
|
@ -155,11 +157,16 @@ class Puppet::Parser::Lexer
|
|||
[string_token, value]
|
||||
end
|
||||
|
||||
TOKENS.add_token :COMMENT, %r{#.*}, :skip => true
|
||||
TOKENS.add_token :COMMENT, %r{#.*}, :accumulate => true, :skip => true do |lexer,value|
|
||||
value.sub!(/# ?/,'')
|
||||
[self, value]
|
||||
end
|
||||
|
||||
TOKENS.add_token :MLCOMMENT, %r{/\*(.*?)\*/}m do |lexer, value|
|
||||
TOKENS.add_token :MLCOMMENT, %r{/\*(.*?)\*/}m, :accumulate => true, :skip => true do |lexer, value|
|
||||
lexer.line += value.count("\n")
|
||||
[nil,nil]
|
||||
value.sub!(/^\/\* ?/,'')
|
||||
value.sub!(/ ?\*\/$/,'')
|
||||
[self,value]
|
||||
end
|
||||
|
||||
TOKENS.add_token :RETURN, "\n", :skip => true, :incr_line => true, :skip_text => true
|
||||
|
@ -325,6 +332,7 @@ class Puppet::Parser::Lexer
|
|||
@namestack = []
|
||||
@indefine = false
|
||||
@expected = []
|
||||
@commentstack = ['']
|
||||
end
|
||||
|
||||
# Make any necessary changes to the token and/or value.
|
||||
|
@ -333,12 +341,18 @@ class Puppet::Parser::Lexer
|
|||
|
||||
skip() if token.skip_text
|
||||
|
||||
return if token.skip
|
||||
return if token.skip and not token.accumulate?
|
||||
|
||||
token, value = token.convert(self, value) if token.respond_to?(:convert)
|
||||
|
||||
return unless token
|
||||
|
||||
if token.accumulate?
|
||||
@commentstack.last << value + "\n"
|
||||
end
|
||||
|
||||
return if token.skip
|
||||
|
||||
return token, value
|
||||
end
|
||||
|
||||
|
@ -389,6 +403,18 @@ class Puppet::Parser::Lexer
|
|||
raise "Could not match '%s'" % nword
|
||||
end
|
||||
|
||||
if matched_token.name == :RETURN
|
||||
# this matches a blank line
|
||||
if @last_return
|
||||
# eat the previously accumulated comments
|
||||
getcomment
|
||||
end
|
||||
# since :RETURN skips, we won't survive to munge_token
|
||||
@last_return = true
|
||||
else
|
||||
@last_return = false
|
||||
end
|
||||
|
||||
final_token, value = munge_token(matched_token, value)
|
||||
|
||||
next unless final_token
|
||||
|
@ -399,6 +425,10 @@ class Puppet::Parser::Lexer
|
|||
@expected.pop
|
||||
end
|
||||
|
||||
if final_token.name == :LBRACE
|
||||
commentpush
|
||||
end
|
||||
|
||||
yield [final_token.name, value]
|
||||
|
||||
if @previous_token
|
||||
|
@ -414,7 +444,6 @@ class Puppet::Parser::Lexer
|
|||
@indefine = value
|
||||
end
|
||||
end
|
||||
|
||||
@previous_token = final_token
|
||||
skip()
|
||||
end
|
||||
|
@ -453,4 +482,19 @@ class Puppet::Parser::Lexer
|
|||
def string=(string)
|
||||
@scanner = StringScanner.new(string)
|
||||
end
|
||||
|
||||
# returns the content of the currently accumulated content cache
|
||||
def commentpop
|
||||
return @commentstack.pop
|
||||
end
|
||||
|
||||
def getcomment
|
||||
comment = @commentstack.pop
|
||||
@commentstack.push('')
|
||||
return comment
|
||||
end
|
||||
|
||||
def commentpush
|
||||
@commentstack.push('')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,7 +29,7 @@ module Puppet
|
|||
|
||||
class Parser < Racc::Parser
|
||||
|
||||
module_eval <<'..end grammar.ra modeval..id5cb4445525', 'grammar.ra', 741
|
||||
module_eval <<'..end grammar.ra modeval..id987bcfd032', 'grammar.ra', 759
|
||||
|
||||
# It got too annoying having code in a file that needs to be compiled.
|
||||
require 'puppet/parser/parser_support'
|
||||
|
@ -41,7 +41,7 @@ require 'puppet/parser/parser_support'
|
|||
|
||||
# $Id$
|
||||
|
||||
..end grammar.ra modeval..id5cb4445525
|
||||
..end grammar.ra modeval..id987bcfd032
|
||||
|
||||
##### racc 1.4.5 generates ###
|
||||
|
||||
|
@ -1103,8 +1103,9 @@ module_eval <<'.,.,', 'grammar.ra', 130
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 151
|
||||
module_eval <<'.,.,', 'grammar.ra', 152
|
||||
def _reduce_34( val, _values, result )
|
||||
@lexer.commentpop
|
||||
array = val[2]
|
||||
if array.instance_of?(AST::ResourceInstance)
|
||||
array = [array]
|
||||
|
@ -1127,7 +1128,7 @@ module_eval <<'.,.,', 'grammar.ra', 151
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 154
|
||||
module_eval <<'.,.,', 'grammar.ra', 155
|
||||
def _reduce_35( val, _values, result )
|
||||
# This is a deprecated syntax.
|
||||
error "All resource specifications require names"
|
||||
|
@ -1135,7 +1136,7 @@ module_eval <<'.,.,', 'grammar.ra', 154
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 157
|
||||
module_eval <<'.,.,', 'grammar.ra', 158
|
||||
def _reduce_36( val, _values, result )
|
||||
# a defaults setting for a type
|
||||
result = ast(AST::ResourceDefaults, :type => val[0], :params => val[2])
|
||||
|
@ -1143,14 +1144,15 @@ module_eval <<'.,.,', 'grammar.ra', 157
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 162
|
||||
module_eval <<'.,.,', 'grammar.ra', 164
|
||||
def _reduce_37( val, _values, result )
|
||||
@lexer.commentpop
|
||||
result = ast AST::ResourceOverride, :object => val[0], :params => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 189
|
||||
module_eval <<'.,.,', 'grammar.ra', 191
|
||||
def _reduce_38( val, _values, result )
|
||||
type = val[0]
|
||||
|
||||
|
@ -1178,27 +1180,27 @@ module_eval <<'.,.,', 'grammar.ra', 189
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 190
|
||||
module_eval <<'.,.,', 'grammar.ra', 192
|
||||
def _reduce_39( val, _values, result )
|
||||
result = :virtual
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 191
|
||||
module_eval <<'.,.,', 'grammar.ra', 193
|
||||
def _reduce_40( val, _values, result )
|
||||
result = :exported
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 214
|
||||
module_eval <<'.,.,', 'grammar.ra', 216
|
||||
def _reduce_41( val, _values, result )
|
||||
if val[0] =~ /^[a-z]/
|
||||
Puppet.warning addcontext("Collection names must now be capitalized")
|
||||
end
|
||||
type = val[0].downcase
|
||||
args = {:type => type}
|
||||
args = {:type => type }
|
||||
|
||||
if val[1].is_a?(AST::CollExpr)
|
||||
args[:query] = val[1]
|
||||
|
@ -1215,7 +1217,7 @@ module_eval <<'.,.,', 'grammar.ra', 214
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 224
|
||||
module_eval <<'.,.,', 'grammar.ra', 226
|
||||
def _reduce_42( val, _values, result )
|
||||
if val[1]
|
||||
result = val[1]
|
||||
|
@ -1227,7 +1229,7 @@ module_eval <<'.,.,', 'grammar.ra', 224
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 232
|
||||
module_eval <<'.,.,', 'grammar.ra', 234
|
||||
def _reduce_43( val, _values, result )
|
||||
if val[1]
|
||||
result = val[1]
|
||||
|
@ -1243,7 +1245,7 @@ module_eval <<'.,.,', 'grammar.ra', 232
|
|||
|
||||
# reduce 45 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 240
|
||||
module_eval <<'.,.,', 'grammar.ra', 242
|
||||
def _reduce_46( val, _values, result )
|
||||
result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
|
||||
result
|
||||
|
@ -1252,7 +1254,7 @@ module_eval <<'.,.,', 'grammar.ra', 240
|
|||
|
||||
# reduce 47 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 246
|
||||
module_eval <<'.,.,', 'grammar.ra', 248
|
||||
def _reduce_48( val, _values, result )
|
||||
result = val[1]
|
||||
result.parens = true
|
||||
|
@ -1264,7 +1266,7 @@ module_eval <<'.,.,', 'grammar.ra', 246
|
|||
|
||||
# reduce 50 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 254
|
||||
module_eval <<'.,.,', 'grammar.ra', 256
|
||||
def _reduce_51( val, _values, result )
|
||||
result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
|
||||
#result = ast AST::CollExpr
|
||||
|
@ -1273,7 +1275,7 @@ module_eval <<'.,.,', 'grammar.ra', 254
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 259
|
||||
module_eval <<'.,.,', 'grammar.ra', 261
|
||||
def _reduce_52( val, _values, result )
|
||||
result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
|
||||
#result = ast AST::CollExpr
|
||||
|
@ -1286,7 +1288,7 @@ module_eval <<'.,.,', 'grammar.ra', 259
|
|||
|
||||
# reduce 54 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 266
|
||||
module_eval <<'.,.,', 'grammar.ra', 268
|
||||
def _reduce_55( val, _values, result )
|
||||
result = ast AST::ResourceInstance, :children => [val[0],val[2]]
|
||||
result
|
||||
|
@ -1295,7 +1297,7 @@ module_eval <<'.,.,', 'grammar.ra', 266
|
|||
|
||||
# reduce 56 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 276
|
||||
module_eval <<'.,.,', 'grammar.ra', 278
|
||||
def _reduce_57( val, _values, result )
|
||||
if val[0].instance_of?(AST::ResourceInstance)
|
||||
result = ast AST::ASTArray, :children => [val[0],val[2]]
|
||||
|
@ -1311,21 +1313,21 @@ module_eval <<'.,.,', 'grammar.ra', 276
|
|||
|
||||
# reduce 59 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 283
|
||||
module_eval <<'.,.,', 'grammar.ra', 285
|
||||
def _reduce_60( val, _values, result )
|
||||
result = ast AST::Undef, :value => :undef
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 287
|
||||
module_eval <<'.,.,', 'grammar.ra', 289
|
||||
def _reduce_61( val, _values, result )
|
||||
result = ast AST::Name, :value => val[0]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 291
|
||||
module_eval <<'.,.,', 'grammar.ra', 293
|
||||
def _reduce_62( val, _values, result )
|
||||
result = ast AST::Type, :value => val[0]
|
||||
result
|
||||
|
@ -1344,7 +1346,7 @@ module_eval <<'.,.,', 'grammar.ra', 291
|
|||
|
||||
# reduce 68 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 307
|
||||
module_eval <<'.,.,', 'grammar.ra', 309
|
||||
def _reduce_69( val, _values, result )
|
||||
if val[0] =~ /::/
|
||||
raise Puppet::ParseError, "Cannot assign to variables in other namespaces"
|
||||
|
@ -1356,7 +1358,7 @@ module_eval <<'.,.,', 'grammar.ra', 307
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 312
|
||||
module_eval <<'.,.,', 'grammar.ra', 314
|
||||
def _reduce_70( val, _values, result )
|
||||
variable = ast AST::Name, :value => val[0]
|
||||
result = ast AST::VarDef, :name => variable, :value => val[2], :append => true
|
||||
|
@ -1364,21 +1366,21 @@ module_eval <<'.,.,', 'grammar.ra', 312
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 317
|
||||
module_eval <<'.,.,', 'grammar.ra', 319
|
||||
def _reduce_71( val, _values, result )
|
||||
result = ast AST::ASTArray
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 317
|
||||
module_eval <<'.,.,', 'grammar.ra', 319
|
||||
def _reduce_72( val, _values, result )
|
||||
result = val[0]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 326
|
||||
module_eval <<'.,.,', 'grammar.ra', 328
|
||||
def _reduce_73( val, _values, result )
|
||||
if val[0].instance_of?(AST::ASTArray)
|
||||
val[0].push(val[2])
|
||||
|
@ -1390,14 +1392,14 @@ module_eval <<'.,.,', 'grammar.ra', 326
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 330
|
||||
module_eval <<'.,.,', 'grammar.ra', 332
|
||||
def _reduce_74( val, _values, result )
|
||||
result = ast AST::ResourceParam, :param => val[0], :value => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 335
|
||||
module_eval <<'.,.,', 'grammar.ra', 337
|
||||
def _reduce_75( val, _values, result )
|
||||
result = ast AST::ResourceParam, :param => val[0], :value => val[2],
|
||||
:add => true
|
||||
|
@ -1409,21 +1411,21 @@ module_eval <<'.,.,', 'grammar.ra', 335
|
|||
|
||||
# reduce 77 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 343
|
||||
module_eval <<'.,.,', 'grammar.ra', 345
|
||||
def _reduce_78( val, _values, result )
|
||||
result = ast AST::ASTArray
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 343
|
||||
module_eval <<'.,.,', 'grammar.ra', 345
|
||||
def _reduce_79( val, _values, result )
|
||||
result = val[0]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 352
|
||||
module_eval <<'.,.,', 'grammar.ra', 354
|
||||
def _reduce_80( val, _values, result )
|
||||
if val[0].instance_of?(AST::ASTArray)
|
||||
val[0].push(val[2])
|
||||
|
@ -1437,7 +1439,7 @@ module_eval <<'.,.,', 'grammar.ra', 352
|
|||
|
||||
# reduce 81 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 361
|
||||
module_eval <<'.,.,', 'grammar.ra', 363
|
||||
def _reduce_82( val, _values, result )
|
||||
if val[0].instance_of?(AST::ASTArray)
|
||||
result = val[0].push(val[2])
|
||||
|
@ -1480,7 +1482,7 @@ module_eval <<'.,.,', 'grammar.ra', 361
|
|||
|
||||
# reduce 98 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 388
|
||||
module_eval <<'.,.,', 'grammar.ra', 390
|
||||
def _reduce_99( val, _values, result )
|
||||
args = aryfy(val[2])
|
||||
result = ast AST::Function,
|
||||
|
@ -1491,7 +1493,7 @@ module_eval <<'.,.,', 'grammar.ra', 388
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 393
|
||||
module_eval <<'.,.,', 'grammar.ra', 395
|
||||
def _reduce_100( val, _values, result )
|
||||
result = ast AST::Function,
|
||||
:name => val[0],
|
||||
|
@ -1501,28 +1503,28 @@ module_eval <<'.,.,', 'grammar.ra', 393
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 397
|
||||
module_eval <<'.,.,', 'grammar.ra', 399
|
||||
def _reduce_101( val, _values, result )
|
||||
result = ast AST::String, :value => val[0]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 399
|
||||
module_eval <<'.,.,', 'grammar.ra', 401
|
||||
def _reduce_102( val, _values, result )
|
||||
result = ast AST::FlatString, :value => val[0]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 403
|
||||
module_eval <<'.,.,', 'grammar.ra', 405
|
||||
def _reduce_103( val, _values, result )
|
||||
result = ast AST::Boolean, :value => val[0]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 408
|
||||
module_eval <<'.,.,', 'grammar.ra', 410
|
||||
def _reduce_104( val, _values, result )
|
||||
Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized")
|
||||
result = ast AST::ResourceReference, :type => val[0], :title => val[2]
|
||||
|
@ -1530,15 +1532,16 @@ module_eval <<'.,.,', 'grammar.ra', 408
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 410
|
||||
module_eval <<'.,.,', 'grammar.ra', 412
|
||||
def _reduce_105( val, _values, result )
|
||||
result = ast AST::ResourceReference, :type => val[0], :title => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 423
|
||||
module_eval <<'.,.,', 'grammar.ra', 426
|
||||
def _reduce_106( val, _values, result )
|
||||
@lexer.commentpop
|
||||
args = {
|
||||
:test => val[1],
|
||||
:statements => val[3]
|
||||
|
@ -1553,8 +1556,9 @@ module_eval <<'.,.,', 'grammar.ra', 423
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 435
|
||||
module_eval <<'.,.,', 'grammar.ra', 439
|
||||
def _reduce_107( val, _values, result )
|
||||
@lexer.commentpop
|
||||
args = {
|
||||
:test => val[1],
|
||||
:statements => ast(AST::Nop)
|
||||
|
@ -1571,15 +1575,17 @@ module_eval <<'.,.,', 'grammar.ra', 435
|
|||
|
||||
# reduce 108 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 440
|
||||
module_eval <<'.,.,', 'grammar.ra', 445
|
||||
def _reduce_109( val, _values, result )
|
||||
@lexer.commentpop
|
||||
result = ast AST::Else, :statements => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 443
|
||||
module_eval <<'.,.,', 'grammar.ra', 449
|
||||
def _reduce_110( val, _values, result )
|
||||
@lexer.commentpop
|
||||
result = ast AST::Else, :statements => ast(AST::Nop)
|
||||
result
|
||||
end
|
||||
|
@ -1587,127 +1593,128 @@ module_eval <<'.,.,', 'grammar.ra', 443
|
|||
|
||||
# reduce 111 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 460
|
||||
module_eval <<'.,.,', 'grammar.ra', 466
|
||||
def _reduce_112( val, _values, result )
|
||||
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 463
|
||||
module_eval <<'.,.,', 'grammar.ra', 469
|
||||
def _reduce_113( val, _values, result )
|
||||
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 466
|
||||
module_eval <<'.,.,', 'grammar.ra', 472
|
||||
def _reduce_114( val, _values, result )
|
||||
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 469
|
||||
module_eval <<'.,.,', 'grammar.ra', 475
|
||||
def _reduce_115( val, _values, result )
|
||||
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 472
|
||||
module_eval <<'.,.,', 'grammar.ra', 478
|
||||
def _reduce_116( val, _values, result )
|
||||
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 475
|
||||
module_eval <<'.,.,', 'grammar.ra', 481
|
||||
def _reduce_117( val, _values, result )
|
||||
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 478
|
||||
module_eval <<'.,.,', 'grammar.ra', 484
|
||||
def _reduce_118( val, _values, result )
|
||||
result = ast AST::Minus, :value => val[1]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 481
|
||||
module_eval <<'.,.,', 'grammar.ra', 487
|
||||
def _reduce_119( val, _values, result )
|
||||
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 484
|
||||
module_eval <<'.,.,', 'grammar.ra', 490
|
||||
def _reduce_120( val, _values, result )
|
||||
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 487
|
||||
module_eval <<'.,.,', 'grammar.ra', 493
|
||||
def _reduce_121( val, _values, result )
|
||||
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 490
|
||||
module_eval <<'.,.,', 'grammar.ra', 496
|
||||
def _reduce_122( val, _values, result )
|
||||
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 493
|
||||
module_eval <<'.,.,', 'grammar.ra', 499
|
||||
def _reduce_123( val, _values, result )
|
||||
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 496
|
||||
module_eval <<'.,.,', 'grammar.ra', 502
|
||||
def _reduce_124( val, _values, result )
|
||||
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 499
|
||||
module_eval <<'.,.,', 'grammar.ra', 505
|
||||
def _reduce_125( val, _values, result )
|
||||
result = ast AST::Not, :value => val[1]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 502
|
||||
module_eval <<'.,.,', 'grammar.ra', 508
|
||||
def _reduce_126( val, _values, result )
|
||||
result = ast AST::BooleanOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 505
|
||||
module_eval <<'.,.,', 'grammar.ra', 511
|
||||
def _reduce_127( val, _values, result )
|
||||
result = ast AST::BooleanOperator, :operator => val[1], :lval => val[0], :rval => val[2]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 508
|
||||
module_eval <<'.,.,', 'grammar.ra', 514
|
||||
def _reduce_128( val, _values, result )
|
||||
result = val[1]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 516
|
||||
module_eval <<'.,.,', 'grammar.ra', 523
|
||||
def _reduce_129( val, _values, result )
|
||||
@lexer.commentpop
|
||||
options = val[3]
|
||||
unless options.instance_of?(AST::ASTArray)
|
||||
options = ast AST::ASTArray, :children => [val[3]]
|
||||
|
@ -1719,7 +1726,7 @@ module_eval <<'.,.,', 'grammar.ra', 516
|
|||
|
||||
# reduce 130 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 526
|
||||
module_eval <<'.,.,', 'grammar.ra', 533
|
||||
def _reduce_131( val, _values, result )
|
||||
if val[0].instance_of?(AST::ASTArray)
|
||||
val[0].push val[1]
|
||||
|
@ -1731,15 +1738,17 @@ module_eval <<'.,.,', 'grammar.ra', 526
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 530
|
||||
module_eval <<'.,.,', 'grammar.ra', 538
|
||||
def _reduce_132( val, _values, result )
|
||||
@lexer.commentpop
|
||||
result = ast AST::CaseOpt, :value => val[0], :statements => val[3]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 535
|
||||
module_eval <<'.,.,', 'grammar.ra', 544
|
||||
def _reduce_133( val, _values, result )
|
||||
@lexer.commentpop
|
||||
result = ast(AST::CaseOpt,
|
||||
:value => val[0],
|
||||
:statements => ast(AST::ASTArray)
|
||||
|
@ -1750,7 +1759,7 @@ module_eval <<'.,.,', 'grammar.ra', 535
|
|||
|
||||
# reduce 134 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 545
|
||||
module_eval <<'.,.,', 'grammar.ra', 554
|
||||
def _reduce_135( val, _values, result )
|
||||
if val[0].instance_of?(AST::ASTArray)
|
||||
val[0].push(val[2])
|
||||
|
@ -1762,7 +1771,7 @@ module_eval <<'.,.,', 'grammar.ra', 545
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 549
|
||||
module_eval <<'.,.,', 'grammar.ra', 558
|
||||
def _reduce_136( val, _values, result )
|
||||
result = ast AST::Selector, :param => val[0], :values => val[2]
|
||||
result
|
||||
|
@ -1771,16 +1780,17 @@ module_eval <<'.,.,', 'grammar.ra', 549
|
|||
|
||||
# reduce 137 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 551
|
||||
module_eval <<'.,.,', 'grammar.ra', 564
|
||||
def _reduce_138( val, _values, result )
|
||||
result = val[1]
|
||||
@lexer.commentpop
|
||||
result = val[1]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
# reduce 139 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 562
|
||||
module_eval <<'.,.,', 'grammar.ra', 574
|
||||
def _reduce_140( val, _values, result )
|
||||
if val[0].instance_of?(AST::ASTArray)
|
||||
val[0].push(val[2])
|
||||
|
@ -1792,7 +1802,7 @@ module_eval <<'.,.,', 'grammar.ra', 562
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 566
|
||||
module_eval <<'.,.,', 'grammar.ra', 578
|
||||
def _reduce_141( val, _values, result )
|
||||
result = ast AST::ResourceParam, :param => val[0], :value => val[2]
|
||||
result
|
||||
|
@ -1813,28 +1823,28 @@ module_eval <<'.,.,', 'grammar.ra', 566
|
|||
|
||||
# reduce 148 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 577
|
||||
module_eval <<'.,.,', 'grammar.ra', 589
|
||||
def _reduce_149( val, _values, result )
|
||||
result = ast AST::Default, :value => val[0]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 579
|
||||
module_eval <<'.,.,', 'grammar.ra', 591
|
||||
def _reduce_150( val, _values, result )
|
||||
result = [val[0].value]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 583
|
||||
module_eval <<'.,.,', 'grammar.ra', 595
|
||||
def _reduce_151( val, _values, result )
|
||||
results = val[0] << val[2].value
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 591
|
||||
module_eval <<'.,.,', 'grammar.ra', 603
|
||||
def _reduce_152( val, _values, result )
|
||||
val[1].each do |file|
|
||||
import(file)
|
||||
|
@ -1845,8 +1855,9 @@ module_eval <<'.,.,', 'grammar.ra', 591
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 601
|
||||
module_eval <<'.,.,', 'grammar.ra', 614
|
||||
def _reduce_153( val, _values, result )
|
||||
@lexer.commentpop
|
||||
newdefine classname(val[1]), :arguments => val[2], :code => val[4]
|
||||
@lexer.indefine = false
|
||||
result = nil
|
||||
|
@ -1856,8 +1867,9 @@ module_eval <<'.,.,', 'grammar.ra', 601
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 605
|
||||
module_eval <<'.,.,', 'grammar.ra', 619
|
||||
def _reduce_154( val, _values, result )
|
||||
@lexer.commentpop
|
||||
newdefine classname(val[1]), :arguments => val[2]
|
||||
@lexer.indefine = false
|
||||
result = nil
|
||||
|
@ -1865,8 +1877,9 @@ module_eval <<'.,.,', 'grammar.ra', 605
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 613
|
||||
module_eval <<'.,.,', 'grammar.ra', 628
|
||||
def _reduce_155( val, _values, result )
|
||||
@lexer.commentpop
|
||||
# Our class gets defined in the parent namespace, not our own.
|
||||
@lexer.namepop
|
||||
newclass classname(val[1]), :code => val[4], :parent => val[2]
|
||||
|
@ -1875,8 +1888,9 @@ module_eval <<'.,.,', 'grammar.ra', 613
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 618
|
||||
module_eval <<'.,.,', 'grammar.ra', 634
|
||||
def _reduce_156( val, _values, result )
|
||||
@lexer.commentpop
|
||||
# Our class gets defined in the parent namespace, not our own.
|
||||
@lexer.namepop
|
||||
newclass classname(val[1]), :parent => val[2]
|
||||
|
@ -1885,16 +1899,18 @@ module_eval <<'.,.,', 'grammar.ra', 618
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 623
|
||||
module_eval <<'.,.,', 'grammar.ra', 640
|
||||
def _reduce_157( val, _values, result )
|
||||
@lexer.commentpop
|
||||
newnode val[1], :parent => val[2], :code => val[4]
|
||||
result = nil
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 626
|
||||
module_eval <<'.,.,', 'grammar.ra', 644
|
||||
def _reduce_158( val, _values, result )
|
||||
@lexer.commentpop
|
||||
newnode val[1], :parent => val[2]
|
||||
result = nil
|
||||
result
|
||||
|
@ -1909,7 +1925,7 @@ module_eval <<'.,.,', 'grammar.ra', 626
|
|||
|
||||
# reduce 162 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 640
|
||||
module_eval <<'.,.,', 'grammar.ra', 658
|
||||
def _reduce_163( val, _values, result )
|
||||
result = val[0]
|
||||
result = [result] unless result.is_a?(Array)
|
||||
|
@ -1926,14 +1942,14 @@ module_eval <<'.,.,', 'grammar.ra', 640
|
|||
|
||||
# reduce 167 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 649
|
||||
module_eval <<'.,.,', 'grammar.ra', 667
|
||||
def _reduce_168( val, _values, result )
|
||||
result = nil
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 653
|
||||
module_eval <<'.,.,', 'grammar.ra', 671
|
||||
def _reduce_169( val, _values, result )
|
||||
result = ast AST::ASTArray, :children => []
|
||||
result
|
||||
|
@ -1942,14 +1958,14 @@ module_eval <<'.,.,', 'grammar.ra', 653
|
|||
|
||||
# reduce 170 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 658
|
||||
module_eval <<'.,.,', 'grammar.ra', 676
|
||||
def _reduce_171( val, _values, result )
|
||||
result = nil
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 662
|
||||
module_eval <<'.,.,', 'grammar.ra', 680
|
||||
def _reduce_172( val, _values, result )
|
||||
result = val[1]
|
||||
result = [result] unless result[0].is_a?(Array)
|
||||
|
@ -1959,7 +1975,7 @@ module_eval <<'.,.,', 'grammar.ra', 662
|
|||
|
||||
# reduce 173 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 669
|
||||
module_eval <<'.,.,', 'grammar.ra', 687
|
||||
def _reduce_174( val, _values, result )
|
||||
result = val[0]
|
||||
result = [result] unless result[0].is_a?(Array)
|
||||
|
@ -1968,7 +1984,7 @@ module_eval <<'.,.,', 'grammar.ra', 669
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 674
|
||||
module_eval <<'.,.,', 'grammar.ra', 692
|
||||
def _reduce_175( val, _values, result )
|
||||
Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype")
|
||||
result = [val[0], val[2]]
|
||||
|
@ -1976,7 +1992,7 @@ module_eval <<'.,.,', 'grammar.ra', 674
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 678
|
||||
module_eval <<'.,.,', 'grammar.ra', 696
|
||||
def _reduce_176( val, _values, result )
|
||||
Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype")
|
||||
result = [val[0]]
|
||||
|
@ -1984,14 +2000,14 @@ module_eval <<'.,.,', 'grammar.ra', 678
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 680
|
||||
module_eval <<'.,.,', 'grammar.ra', 698
|
||||
def _reduce_177( val, _values, result )
|
||||
result = [val[0], val[2]]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 682
|
||||
module_eval <<'.,.,', 'grammar.ra', 700
|
||||
def _reduce_178( val, _values, result )
|
||||
result = [val[0]]
|
||||
result
|
||||
|
@ -2000,7 +2016,7 @@ module_eval <<'.,.,', 'grammar.ra', 682
|
|||
|
||||
# reduce 179 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 687
|
||||
module_eval <<'.,.,', 'grammar.ra', 705
|
||||
def _reduce_180( val, _values, result )
|
||||
result = val[1]
|
||||
result
|
||||
|
@ -2009,7 +2025,7 @@ module_eval <<'.,.,', 'grammar.ra', 687
|
|||
|
||||
# reduce 181 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 692
|
||||
module_eval <<'.,.,', 'grammar.ra', 710
|
||||
def _reduce_182( val, _values, result )
|
||||
result = val[1]
|
||||
result
|
||||
|
@ -2020,14 +2036,14 @@ module_eval <<'.,.,', 'grammar.ra', 692
|
|||
|
||||
# reduce 184 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 698
|
||||
module_eval <<'.,.,', 'grammar.ra', 716
|
||||
def _reduce_185( val, _values, result )
|
||||
result = ast AST::Variable, :value => val[0]
|
||||
result
|
||||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 706
|
||||
module_eval <<'.,.,', 'grammar.ra', 724
|
||||
def _reduce_186( val, _values, result )
|
||||
if val[1].instance_of?(AST::ASTArray)
|
||||
result = val[1]
|
||||
|
@ -2038,7 +2054,7 @@ module_eval <<'.,.,', 'grammar.ra', 706
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 713
|
||||
module_eval <<'.,.,', 'grammar.ra', 731
|
||||
def _reduce_187( val, _values, result )
|
||||
if val[1].instance_of?(AST::ASTArray)
|
||||
result = val[1]
|
||||
|
@ -2049,7 +2065,7 @@ module_eval <<'.,.,', 'grammar.ra', 713
|
|||
end
|
||||
.,.,
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 715
|
||||
module_eval <<'.,.,', 'grammar.ra', 733
|
||||
def _reduce_188( val, _values, result )
|
||||
result = ast AST::ASTArray
|
||||
result
|
||||
|
@ -2062,7 +2078,7 @@ module_eval <<'.,.,', 'grammar.ra', 715
|
|||
|
||||
# reduce 191 omitted
|
||||
|
||||
module_eval <<'.,.,', 'grammar.ra', 720
|
||||
module_eval <<'.,.,', 'grammar.ra', 738
|
||||
def _reduce_192( val, _values, result )
|
||||
result = nil
|
||||
result
|
||||
|
|
|
@ -18,6 +18,7 @@ class Puppet::Parser::Parser
|
|||
attr_reader :version, :environment
|
||||
attr_accessor :files
|
||||
|
||||
attr_accessor :lexer
|
||||
|
||||
# Add context to a message; useful for error messages and such.
|
||||
def addcontext(message, obj = nil)
|
||||
|
@ -56,7 +57,9 @@ class Puppet::Parser::Parser
|
|||
end
|
||||
end
|
||||
|
||||
return klass.new(hash)
|
||||
k = klass.new(hash)
|
||||
k.doc = lexer.getcomment if !k.nil? and k.use_docs and k.doc.empty?
|
||||
return k
|
||||
end
|
||||
|
||||
# The fully qualifed name, with the full namespace.
|
||||
|
@ -272,6 +275,7 @@ class Puppet::Parser::Parser
|
|||
end
|
||||
code = options[:code]
|
||||
parent = options[:parent]
|
||||
doc = options[:doc]
|
||||
|
||||
# If the class is already defined, then add code to it.
|
||||
if other = @astset.classes[name]
|
||||
|
@ -304,6 +308,12 @@ class Puppet::Parser::Parser
|
|||
other.code ||= code
|
||||
end
|
||||
end
|
||||
|
||||
if other.doc and doc
|
||||
other.doc += doc
|
||||
else
|
||||
other.doc ||= doc
|
||||
end
|
||||
else
|
||||
# Define it anew.
|
||||
# Note we're doing something somewhat weird here -- we're setting
|
||||
|
@ -312,6 +322,8 @@ class Puppet::Parser::Parser
|
|||
args = {:namespace => name, :classname => name, :parser => self}
|
||||
args[:code] = code if code
|
||||
args[:parentclass] = parent if parent
|
||||
args[:doc] = doc
|
||||
|
||||
@astset.classes[name] = ast AST::HostClass, args
|
||||
end
|
||||
|
||||
|
@ -336,7 +348,8 @@ class Puppet::Parser::Parser
|
|||
:arguments => options[:arguments],
|
||||
:code => options[:code],
|
||||
:parser => self,
|
||||
:classname => name
|
||||
:classname => name,
|
||||
:doc => options[:doc]
|
||||
}
|
||||
|
||||
[:code, :arguments].each do |param|
|
||||
|
@ -350,6 +363,7 @@ class Puppet::Parser::Parser
|
|||
# table, not according to namespaces.
|
||||
def newnode(names, options = {})
|
||||
names = [names] unless names.instance_of?(Array)
|
||||
doc = lexer.getcomment
|
||||
names.collect do |name|
|
||||
name = name.to_s.downcase
|
||||
if other = @astset.nodes[name]
|
||||
|
@ -358,7 +372,8 @@ class Puppet::Parser::Parser
|
|||
name = name.to_s if name.is_a?(Symbol)
|
||||
args = {
|
||||
:name => name,
|
||||
:parser => self
|
||||
:parser => self,
|
||||
:doc => doc
|
||||
}
|
||||
if options[:code]
|
||||
args[:code] = options[:code]
|
||||
|
@ -399,6 +414,7 @@ class Puppet::Parser::Parser
|
|||
self.string = string
|
||||
end
|
||||
begin
|
||||
@yydebug = false
|
||||
main = yyparse(@lexer,:scan)
|
||||
rescue Racc::ParseError => except
|
||||
error = Puppet::ParseError.new(except)
|
||||
|
|
|
@ -37,7 +37,7 @@ class Puppet::Parser::Resource::Reference < Puppet::ResourceReference
|
|||
if self.title == :main
|
||||
tmp = @scope.findclass("")
|
||||
else
|
||||
unless tmp = @scope.findclass(self.title)
|
||||
unless tmp = @scope.parser.classes[self.title]
|
||||
fail Puppet::ParseError, "Could not find class '%s'" % self.title
|
||||
end
|
||||
end
|
||||
|
@ -46,8 +46,9 @@ class Puppet::Parser::Resource::Reference < Puppet::ResourceReference
|
|||
fail Puppet::ParseError, "Could not find node '%s'" % self.title
|
||||
end
|
||||
else # normal definitions
|
||||
# We have to swap these variables around so the errors are right.
|
||||
tmp = @scope.finddefine(self.type)
|
||||
# The resource type is capitalized, so we have to downcase. Really,
|
||||
# we should have a better interface for finding these, but eh.
|
||||
tmp = @scope.parser.definitions[self.type.downcase]
|
||||
end
|
||||
|
||||
if tmp
|
||||
|
|
|
@ -1,33 +1,18 @@
|
|||
# A simple wrapper for templates, so they don't have full access to
|
||||
# the scope objects.
|
||||
class Puppet::Parser::TemplateWrapper
|
||||
attr_accessor :scope, :file
|
||||
attr_accessor :scope, :file, :string
|
||||
include Puppet::Util
|
||||
Puppet::Util.logmethods(self)
|
||||
|
||||
def initialize(scope, filename)
|
||||
def initialize(scope)
|
||||
@__scope__ = scope
|
||||
@__file__ = Puppet::Module::find_template(filename, scope.compiler.environment)
|
||||
|
||||
unless FileTest.exists?(file)
|
||||
raise Puppet::ParseError,
|
||||
"Could not find template %s" % file
|
||||
end
|
||||
|
||||
# We'll only ever not have a parser in testing, but, eh.
|
||||
if scope.parser
|
||||
scope.parser.watch_file(file)
|
||||
end
|
||||
end
|
||||
|
||||
def scope
|
||||
@__scope__
|
||||
end
|
||||
|
||||
def file
|
||||
@__file__
|
||||
end
|
||||
|
||||
# Should return true if a variable is defined, false if it is not
|
||||
def has_variable?(name)
|
||||
if scope.lookupvar(name.to_s, false) != :undefined
|
||||
|
@ -77,11 +62,34 @@ class Puppet::Parser::TemplateWrapper
|
|||
end
|
||||
end
|
||||
|
||||
def result
|
||||
def file=(filename)
|
||||
@file = Puppet::Module::find_template(filename, scope.compiler.environment)
|
||||
|
||||
unless FileTest.exists?(file)
|
||||
raise Puppet::ParseError,
|
||||
"Could not find template %s" % file
|
||||
end
|
||||
|
||||
# We'll only ever not have a parser in testing, but, eh.
|
||||
if scope.parser
|
||||
scope.parser.watch_file(file)
|
||||
end
|
||||
|
||||
@string = File.read(file)
|
||||
end
|
||||
|
||||
def result(string = nil)
|
||||
if string
|
||||
self.string = string
|
||||
template_source = "inline template"
|
||||
else
|
||||
template_source = file
|
||||
end
|
||||
|
||||
# Expose all the variables in our scope as instance variables of the
|
||||
# current object, making it possible to access them without conflict
|
||||
# to the regular methods.
|
||||
benchmark(:debug, "Bound template variables for #{file}") do
|
||||
benchmark(:debug, "Bound template variables for #{template_source}") do
|
||||
scope.to_hash.each { |name, value|
|
||||
if name.kind_of?(String)
|
||||
realname = name.gsub(/[^\w]/, "_")
|
||||
|
@ -93,8 +101,8 @@ class Puppet::Parser::TemplateWrapper
|
|||
end
|
||||
|
||||
result = nil
|
||||
benchmark(:debug, "Interpolated template #{file}") do
|
||||
template = ERB.new(File.read(file), 0, "-")
|
||||
benchmark(:debug, "Interpolated template #{template_source}") do
|
||||
template = ERB.new(self.string, 0, "-")
|
||||
result = template.result(binding)
|
||||
end
|
||||
|
||||
|
@ -102,7 +110,7 @@ class Puppet::Parser::TemplateWrapper
|
|||
end
|
||||
|
||||
def to_s
|
||||
"template[%s]" % file
|
||||
"template[%s]" % (file ? file : "inline")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -445,7 +445,7 @@ class Puppet::Property < Puppet::Parameter
|
|||
end
|
||||
|
||||
# This doc will probably get overridden
|
||||
@doc ||= "The basic property that the object should be in."
|
||||
@doc ||= "The basic property that the resource should be in."
|
||||
end
|
||||
|
||||
def self.inherited(sub)
|
||||
|
|
|
@ -28,6 +28,11 @@ module Puppet
|
|||
@resource[membership] == :inclusive
|
||||
end
|
||||
|
||||
#dearrayify was motivated because to simplify the implementation of the OrderedList property
|
||||
def dearrayify(array)
|
||||
array.sort.join(delimiter)
|
||||
end
|
||||
|
||||
def should
|
||||
unless defined? @should and @should
|
||||
return nil
|
||||
|
@ -39,7 +44,7 @@ module Puppet
|
|||
members = add_should_with_current(members, retrieve)
|
||||
end
|
||||
|
||||
members.sort.join(delimiter)
|
||||
dearrayify(members)
|
||||
end
|
||||
|
||||
def delimiter
|
||||
|
@ -57,7 +62,7 @@ module Puppet
|
|||
|
||||
def prepare_is_for_comparison(is)
|
||||
if is.is_a? Array
|
||||
is = is.sort.join(delimiter)
|
||||
is = dearrayify(is)
|
||||
end
|
||||
is
|
||||
end
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
require 'puppet/property/list'
|
||||
|
||||
module Puppet
|
||||
class Property
|
||||
class OrderedList < List
|
||||
|
||||
def add_should_with_current(should, current)
|
||||
if current.is_a?(Array)
|
||||
#tricky trick
|
||||
#Preserve all the current items in the list
|
||||
#but move them to the back of the line
|
||||
should = should + (current - should)
|
||||
end
|
||||
should
|
||||
end
|
||||
|
||||
def dearrayify(array)
|
||||
array.join(delimiter)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,16 +2,16 @@
|
|||
# Copyright (C) 2008 Red Hat Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# modify it under the terms of the GNU General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
# version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# You should have received a copy of the GNU General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
require 'puppet/provider/nameservice/directoryservice'
|
||||
|
||||
Puppet::Type.type(:computer).provide :directoryservice, :parent => Puppet::Provider::NameService::DirectoryService do
|
||||
desc "Computer object management using DirectoryService on OS X.
|
||||
|
||||
Note that these are distinctly different kinds of objects to 'hosts',
|
||||
as they require a MAC address and can have all sorts of policy attached to
|
||||
them.
|
||||
|
||||
This provider only manages Computer objects in the local directory service
|
||||
domain, not in remote directories.
|
||||
|
||||
If you wish to manage /etc/hosts on Mac OS X, then simply use the host
|
||||
type as per other platforms.
|
||||
"
|
||||
|
||||
confine :operatingsystem => :darwin
|
||||
defaultfor :operatingsystem => :darwin
|
||||
|
||||
# hurray for abstraction. The nameservice directoryservice provider can
|
||||
# handle everything we need. super.
|
||||
end
|
|
@ -42,6 +42,9 @@ class Puppet::Provider::Confine
|
|||
for_binary
|
||||
end
|
||||
|
||||
# Used for logging.
|
||||
attr_accessor :label
|
||||
|
||||
def initialize(values)
|
||||
values = [values] unless values.is_a?(Array)
|
||||
@values = values
|
||||
|
@ -61,7 +64,7 @@ class Puppet::Provider::Confine
|
|||
def valid?
|
||||
values.each do |value|
|
||||
unless pass?(value)
|
||||
Puppet.debug message(value)
|
||||
Puppet.debug(label + ": " + message(value))
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,8 +24,13 @@ class Puppet::Provider::Confine::Variable < Puppet::Provider::Confine
|
|||
@facter_value
|
||||
end
|
||||
|
||||
def initialize(values)
|
||||
super
|
||||
@values = @values.collect { |v| v.to_s.downcase }
|
||||
end
|
||||
|
||||
def message(value)
|
||||
"facter value '%s' for '%s' not in required list '%s'" % [value, self.name, values.join(",")]
|
||||
"facter value '%s' for '%s' not in required list '%s'" % [test_value, self.name, values.join(",")]
|
||||
end
|
||||
|
||||
# Compare the passed-in value to the retrieved value.
|
||||
|
@ -35,10 +40,16 @@ class Puppet::Provider::Confine::Variable < Puppet::Provider::Confine
|
|||
|
||||
def reset
|
||||
# Reset the cache. We want to cache it during a given
|
||||
# run, but across runs.
|
||||
# run, but not across runs.
|
||||
@facter_value = nil
|
||||
end
|
||||
|
||||
def valid?
|
||||
@values.include?(test_value.to_s.downcase)
|
||||
ensure
|
||||
reset
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setting?
|
||||
|
|
|
@ -19,10 +19,13 @@ class Puppet::Provider::ConfineCollection
|
|||
confine.name = test
|
||||
@confines << confine
|
||||
end
|
||||
@confines[-1].label = self.label
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
attr_reader :label
|
||||
def initialize(label)
|
||||
@label = label
|
||||
@confines = []
|
||||
end
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ module Puppet::Provider::Confiner
|
|||
|
||||
def confine_collection
|
||||
unless defined?(@confine_collection)
|
||||
@confine_collection = Puppet::Provider::ConfineCollection.new
|
||||
@confine_collection = Puppet::Provider::ConfineCollection.new(self.to_s)
|
||||
end
|
||||
@confine_collection
|
||||
end
|
||||
|
|
|
@ -16,8 +16,9 @@ require 'puppet/provider/nameservice/directoryservice'
|
|||
|
||||
Puppet::Type.type(:group).provide :directoryservice, :parent => Puppet::Provider::NameService::DirectoryService do
|
||||
desc "Group management using DirectoryService on OS X."
|
||||
|
||||
|
||||
commands :dscl => "/usr/bin/dscl"
|
||||
confine :operatingsystem => :darwin
|
||||
#defaultfor :operatingsystem => :darwin
|
||||
defaultfor :operatingsystem => :darwin
|
||||
has_feature :manages_members
|
||||
end
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
#--
|
||||
# Copyright (C) 2008 Jeffrey J McCune.
|
||||
|
||||
# This program and entire repository is free software; you can
|
||||
# redistribute it and/or modify it under the terms of the GNU
|
||||
# General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
# Author: Jeff McCune <mccune.jeff@gmail.com>
|
||||
|
||||
require 'tempfile'
|
||||
|
||||
Puppet::Type.type(:mcx).provide :mcxcontent, :parent => Puppet::Provider do
|
||||
|
||||
desc "MCX Settings management using DirectoryService on OS X.
|
||||
|
||||
This provider manages the entire MCXSettings attribute available
|
||||
to some directory services nodes. This management is 'all or nothing'
|
||||
in that discrete application domain key value pairs are not managed
|
||||
by this provider.
|
||||
|
||||
It is recommended to use WorkGroup Manager to configure Users, Groups,
|
||||
Computers, or ComputerLists, then use 'ralsh mcx' to generate a puppet
|
||||
manifest from the resulting configuration.
|
||||
|
||||
Original Author: Jeff McCune (mccune.jeff@gmail.com)"
|
||||
|
||||
# This provides a mapping of puppet types to DirectoryService
|
||||
# type strings.
|
||||
TypeMap = {
|
||||
:user => "Users",
|
||||
:group => "Groups",
|
||||
:computer => "Computers",
|
||||
:computerlist => "ComputerLists",
|
||||
}
|
||||
|
||||
class MCXContentProviderException < Exception
|
||||
|
||||
end
|
||||
|
||||
commands :dscl => "/usr/bin/dscl"
|
||||
confine :operatingsystem => :darwin
|
||||
defaultfor :operatingsystem => :darwin
|
||||
|
||||
# self.instances is all important.
|
||||
# This is the only class method, it returns
|
||||
# an array of instances of this class.
|
||||
def self.instances
|
||||
mcx_list = []
|
||||
for ds_type in TypeMap.keys
|
||||
ds_path = "/Local/Default/#{TypeMap[ds_type]}"
|
||||
output = dscl 'localhost', '-list', ds_path
|
||||
member_list = output.split
|
||||
for ds_name in member_list
|
||||
content = mcxexport(ds_type, ds_name)
|
||||
if content.empty?
|
||||
Puppet.debug "/#{TypeMap[ds_type]}/#{ds_name} has no MCX data."
|
||||
else
|
||||
# This node has MCX data.
|
||||
rsrc = self.new(:name => "/#{TypeMap[ds_type]}/#{ds_name}",
|
||||
:ds_type => ds_type,
|
||||
:ds_name => ds_name,
|
||||
:content => content)
|
||||
mcx_list << rsrc
|
||||
end
|
||||
end
|
||||
end
|
||||
return mcx_list
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# mcxexport is used by instances, and therefore
|
||||
# a class method.
|
||||
def self.mcxexport(ds_type, ds_name)
|
||||
ds_t = TypeMap[ds_type]
|
||||
ds_n = ds_name.to_s
|
||||
ds_path = "/Local/Default/#{ds_t}/#{ds_n}"
|
||||
dscl 'localhost', '-mcxexport', ds_path
|
||||
end
|
||||
|
||||
def mcximport(ds_type, ds_name, val)
|
||||
ds_t = TypeMap[ds_type]
|
||||
ds_n = ds_name.to_s
|
||||
ds_path = "/Local/Default/#{ds_t}/#{ds_name}"
|
||||
|
||||
tmp = Tempfile.new('puppet_mcx')
|
||||
begin
|
||||
tmp << val
|
||||
tmp.flush
|
||||
dscl 'localhost', '-mcximport', ds_path, tmp.path
|
||||
ensure
|
||||
tmp.close
|
||||
tmp.unlink
|
||||
end
|
||||
end
|
||||
|
||||
# Given the resource name string, parse ds_type out.
|
||||
def parse_type(name)
|
||||
tmp = name.split('/')[1]
|
||||
if ! tmp.is_a? String
|
||||
raise MCXContentProviderException,
|
||||
"Coult not parse ds_type from resource name '#{name}'. Specify with ds_type parameter."
|
||||
end
|
||||
# De-pluralize and downcase.
|
||||
tmp = tmp.chop.downcase.to_sym
|
||||
if not TypeMap.keys.member? tmp
|
||||
raise MCXContentProviderException,
|
||||
"Coult not parse ds_type from resource name '#{name}'. Specify with ds_type parameter."
|
||||
end
|
||||
return tmp
|
||||
end
|
||||
|
||||
# Given the resource name string, parse ds_name out.
|
||||
def parse_name(name)
|
||||
ds_name = name.split('/')[2]
|
||||
if ! ds_name.is_a? String
|
||||
raise MCXContentProviderException,
|
||||
"Could not parse ds_name from resource name '#{name}'. Specify with ds_name parameter."
|
||||
end
|
||||
return ds_name
|
||||
end
|
||||
|
||||
# Gather ds_type and ds_name from resource or
|
||||
# parse it out of the name.
|
||||
# This is a private instance method, not a class method.
|
||||
def get_dsparams
|
||||
ds_type = resource[:ds_type]
|
||||
if ds_type.nil?
|
||||
ds_type = parse_type(resource[:name])
|
||||
end
|
||||
raise MCXContentProviderException unless TypeMap.keys.include? ds_type.to_sym
|
||||
|
||||
ds_name = resource[:ds_name]
|
||||
if ds_name.nil?
|
||||
ds_name = parse_name(resource[:name])
|
||||
end
|
||||
|
||||
rval = {
|
||||
:ds_type => ds_type.to_sym,
|
||||
:ds_name => ds_name,
|
||||
}
|
||||
|
||||
return rval
|
||||
|
||||
end
|
||||
|
||||
public
|
||||
|
||||
def create
|
||||
self.content=(resource[:content])
|
||||
end
|
||||
|
||||
def destroy
|
||||
ds_parms = get_dsparams
|
||||
ds_t = TypeMap[ds_parms[:ds_type]]
|
||||
ds_n = ds_parms[:ds_name].to_s
|
||||
ds_path = "/Local/Default/#{ds_t}/#{ds_n}"
|
||||
|
||||
dscl 'localhost', '-mcxdelete', ds_path
|
||||
end
|
||||
|
||||
def exists?
|
||||
# JJM Just re-use the content method and see if it's empty.
|
||||
begin
|
||||
mcx = content
|
||||
rescue Puppet::ExecutionFailure => e
|
||||
return false
|
||||
end
|
||||
has_mcx = ! mcx.empty?
|
||||
return has_mcx
|
||||
end
|
||||
|
||||
def content
|
||||
ds_parms = get_dsparams
|
||||
mcx = self.class.mcxexport(ds_parms[:ds_type],
|
||||
ds_parms[:ds_name])
|
||||
return mcx
|
||||
end
|
||||
|
||||
def content=(value)
|
||||
# dscl localhost -mcximport
|
||||
ds_parms = get_dsparams
|
||||
mcx = mcximport(ds_parms[:ds_type],
|
||||
ds_parms[:ds_name],
|
||||
resource[:content])
|
||||
return mcx
|
||||
end
|
||||
|
||||
end
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
require 'puppet'
|
||||
require 'puppet/provider/nameservice'
|
||||
require 'facter/util/plist'
|
||||
|
||||
|
||||
class Puppet::Provider::NameService
|
||||
class DirectoryService < Puppet::Provider::NameService
|
||||
|
@ -26,6 +28,7 @@ class DirectoryService < Puppet::Provider::NameService
|
|||
attr_writer :ds_path
|
||||
end
|
||||
|
||||
|
||||
# JJM 2007-07-24: Not yet sure what initvars() does. I saw it in netinfo.rb
|
||||
# I do know, however, that it makes methods "work" =)
|
||||
# e.g. addcmd isn't available if this method call isn't present.
|
||||
|
@ -36,9 +39,9 @@ class DirectoryService < Puppet::Provider::NameService
|
|||
initvars()
|
||||
|
||||
commands :dscl => "/usr/bin/dscl"
|
||||
commands :dseditgroup => "/usr/sbin/dseditgroup"
|
||||
confine :operatingsystem => :darwin
|
||||
# JJM FIXME: This will need to be the default around October 2007.
|
||||
# defaultfor :operatingsystem => :darwin
|
||||
defaultfor :operatingsystem => :darwin
|
||||
|
||||
|
||||
# JJM 2007-07-25: This map is used to map NameService attributes to their
|
||||
|
@ -55,6 +58,10 @@ class DirectoryService < Puppet::Provider::NameService
|
|||
'UniqueID' => :uid,
|
||||
'RealName' => :comment,
|
||||
'Password' => :password,
|
||||
'GeneratedUID' => :guid,
|
||||
'IPAddress' => :ip_address,
|
||||
'ENetAddress' => :en_address,
|
||||
'GroupMembership' => :members,
|
||||
}
|
||||
# JJM The same table as above, inverted.
|
||||
@@ns_to_ds_attribute_map = {
|
||||
|
@ -65,16 +72,19 @@ class DirectoryService < Puppet::Provider::NameService
|
|||
:uid => 'UniqueID',
|
||||
:comment => 'RealName',
|
||||
:password => 'Password',
|
||||
:guid => 'GeneratedUID',
|
||||
:en_address => 'ENetAddress',
|
||||
:ip_address => 'IPAddress',
|
||||
:members => 'GroupMembership',
|
||||
}
|
||||
|
||||
@@password_hash_dir = "/var/db/shadow/hash"
|
||||
|
||||
def self.instances
|
||||
# JJM Class method that provides an array of instance objects of this
|
||||
# type.
|
||||
|
||||
# JJM: Properties are dependent on the Puppet::Type we're managine.
|
||||
type_property_array = [:name] + @resource_type.validproperties
|
||||
# JJM: No sense reporting the password. It's hashed.
|
||||
type_property_array.delete(:password) if type_property_array.include? :password
|
||||
|
||||
# Create a new instance of this Puppet::Type for each object present
|
||||
# on the system.
|
||||
|
@ -119,7 +129,7 @@ class DirectoryService < Puppet::Provider::NameService
|
|||
|
||||
all_present_str_array = list_all_present()
|
||||
|
||||
# JJM: Return nil if the named object isn't present.
|
||||
# NBK: shortcut the process if the resource is missing
|
||||
return nil unless all_present_str_array.include? resource_name
|
||||
|
||||
dscl_vector = get_exec_preamble("-read", resource_name)
|
||||
|
@ -132,44 +142,37 @@ class DirectoryService < Puppet::Provider::NameService
|
|||
# JJM: We need a new hash to return back to our caller.
|
||||
attribute_hash = Hash.new
|
||||
|
||||
# JJM: First, the output string goes into an array.
|
||||
# Then, the each array element is split
|
||||
# If you want to figure out what this is doing, I suggest
|
||||
# ruby-debug, and stepping through it.
|
||||
dscl_output.split("\n").each do |line|
|
||||
# JJM: Split the attribute name and the list of values.
|
||||
ds_attribute, ds_values_string = line.split(':')
|
||||
|
||||
# Split sets the values to nil if there's nothing after the :
|
||||
ds_values_string ||= ""
|
||||
|
||||
# JJM: skip this attribute line if the Puppet::Type doesn't care about it.
|
||||
dscl_plist = Plist.parse_xml(dscl_output)
|
||||
dscl_plist.keys().each do |key|
|
||||
ds_attribute = key.sub("dsAttrTypeStandard:", "")
|
||||
next unless (@@ds_to_ns_attribute_map.keys.include?(ds_attribute) and type_properties.include? @@ds_to_ns_attribute_map[ds_attribute])
|
||||
|
||||
# JJM: We asked dscl to output url encoded values so we're able
|
||||
# to machine parse on whitespace. We need to urldecode:
|
||||
# " Jeff%20McCune John%20Doe " => ["Jeff McCune", "John Doe"]
|
||||
ds_value_array = ds_values_string.scan(/[^\s]+/).collect do |v|
|
||||
url_decoded_value = CGI::unescape v
|
||||
if url_decoded_value =~ /^[-0-9]+$/
|
||||
url_decoded_value.to_i
|
||||
else
|
||||
url_decoded_value
|
||||
end
|
||||
ds_value = dscl_plist[key]
|
||||
case @@ds_to_ns_attribute_map[ds_attribute]
|
||||
when :members:
|
||||
ds_value = ds_value # only members uses arrays so far
|
||||
when :gid, :uid:
|
||||
# OS X stores objects like uid/gid as strings.
|
||||
# Try casting to an integer for these cases to be
|
||||
# consistent with the other providers and the group type
|
||||
# validation
|
||||
begin
|
||||
ds_value = Integer(ds_value[0])
|
||||
rescue ArgumentError
|
||||
ds_value = ds_value[0]
|
||||
end
|
||||
else ds_value = ds_value[0]
|
||||
end
|
||||
|
||||
# JJM: Finally, we're able to build up our attribute hash.
|
||||
# Remember, the hash is keyed by NameService attribute names,
|
||||
# not DirectoryService attribute names.
|
||||
# NOTE: We're also sort of cheating here... DirectoryService
|
||||
# is robust enough to allow multiple values for almost every
|
||||
# attribute in the system. Traditional NameService things
|
||||
# really don't handle this case, so we'll always pull thet first
|
||||
# value returned from DirectoryService.
|
||||
# THERE MAY BE AN ORDERING ISSUE HERE, but I think it's ok...
|
||||
attribute_hash[@@ds_to_ns_attribute_map[ds_attribute]] = ds_value_array[0]
|
||||
attribute_hash[@@ds_to_ns_attribute_map[ds_attribute]] = ds_value
|
||||
end
|
||||
return attribute_hash
|
||||
|
||||
# NBK: need to read the existing password here as it's not actually
|
||||
# stored in the user record. It is stored at a path that involves the
|
||||
# UUID of the user record for non-Mobile local acccounts.
|
||||
# Mobile Accounts are out of scope for this provider for now
|
||||
if @resource_type.validproperties.include?(:password)
|
||||
attribute_hash[:password] = self.get_password(attribute_hash[:guid])
|
||||
end
|
||||
return attribute_hash
|
||||
end
|
||||
|
||||
def self.get_exec_preamble(ds_action, resource_name = nil)
|
||||
|
@ -181,7 +184,7 @@ class DirectoryService < Puppet::Provider::NameService
|
|||
# We EXPECT name to be @resource[:name] when called from an instance object.
|
||||
|
||||
# There are two ways to specify paths in 10.5. See man dscl.
|
||||
command_vector = [ command(:dscl), "-url", "." ]
|
||||
command_vector = [ command(:dscl), "-plist", "." ]
|
||||
# JJM: The actual action to perform. See "man dscl"
|
||||
# Common actiosn: -create, -delete, -merge, -append, -passwd
|
||||
command_vector << ds_action
|
||||
|
@ -196,6 +199,52 @@ class DirectoryService < Puppet::Provider::NameService
|
|||
# e.g. 'dscl / -create /Users/mccune'
|
||||
return command_vector
|
||||
end
|
||||
|
||||
def self.set_password(resource_name, guid, password_hash)
|
||||
password_hash_file = "#{@@password_hash_dir}/#{guid}"
|
||||
begin
|
||||
File.open(password_hash_file, 'w') { |f| f.write(password_hash)}
|
||||
rescue Errno::EACCES => detail
|
||||
raise Puppet::Error, "Could not write to password hash file: #{detail}"
|
||||
end
|
||||
|
||||
# NBK: For shadow hashes, the user AuthenticationAuthority must contain a value of
|
||||
# ";ShadowHash;". The LKDC in 10.5 makes this more interesting though as it
|
||||
# will dynamically generate ;Kerberosv5;;username@LKDC:SHA1 attributes if
|
||||
# missing. Thus we make sure we only set ;ShadowHash; if it is missing, and
|
||||
# we can do this with the merge command. This allows people to continue to
|
||||
# use other custom AuthenticationAuthority attributes without stomping on them.
|
||||
#
|
||||
# There is a potential problem here in that we're only doing this when setting
|
||||
# the password, and the attribute could get modified at other times while the
|
||||
# hash doesn't change and so this doesn't get called at all... but
|
||||
# without switching all the other attributes to merge instead of create I can't
|
||||
# see a simple enough solution for this that doesn't modify the user record
|
||||
# every single time. This should be a rather rare edge case. (famous last words)
|
||||
|
||||
dscl_vector = self.get_exec_preamble("-merge", resource_name)
|
||||
dscl_vector << "AuthenticationAuthority" << ";ShadowHash;"
|
||||
begin
|
||||
dscl_output = execute(dscl_vector)
|
||||
rescue Puppet::ExecutionFailure => detail
|
||||
raise Puppet::Error, "Could not set AuthenticationAuthority."
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_password(guid)
|
||||
password_hash = nil
|
||||
password_hash_file = "#{@@password_hash_dir}/#{guid}"
|
||||
# TODO: sort out error conditions?
|
||||
if File.exists?(password_hash_file)
|
||||
if not File.readable?(password_hash_file)
|
||||
raise Puppet::Error("Could not read password hash file at #{password_hash_file} for #{@resource[:name]}")
|
||||
end
|
||||
f = File.new(password_hash_file)
|
||||
password_hash = f.read
|
||||
f.close
|
||||
end
|
||||
password_hash
|
||||
end
|
||||
|
||||
def ensure=(ensure_value)
|
||||
super
|
||||
|
@ -206,7 +255,6 @@ class DirectoryService < Puppet::Provider::NameService
|
|||
if ensure_value == :present
|
||||
@resource.class.validproperties.each do |name|
|
||||
next if name == :ensure
|
||||
|
||||
# LAK: We use property.sync here rather than directly calling
|
||||
# the settor method because the properties might do some kind
|
||||
# of conversion. In particular, the user gid property might
|
||||
|
@ -223,79 +271,130 @@ class DirectoryService < Puppet::Provider::NameService
|
|||
end
|
||||
|
||||
def password=(passphrase)
|
||||
# JJM: Setting the password is a special case. We don't just
|
||||
# set the attribute because we need to update the password
|
||||
# databases.
|
||||
# FIRST, make sure the AuthenticationAuthority is ;ShadowHash; If
|
||||
# we don't do this, we don't get a shadow hash account. ("Obviously...")
|
||||
dscl_vector = self.class.get_exec_preamble("-create", @resource[:name])
|
||||
dscl_vector << "AuthenticationAuthority" << ";ShadowHash;"
|
||||
begin
|
||||
dscl_output = execute(dscl_vector)
|
||||
rescue Puppet::ExecutionFailure => detail
|
||||
raise Puppet::Error, "Could not set AuthenticationAuthority."
|
||||
end
|
||||
|
||||
# JJM: Second, we need to actually set the password. dscl does
|
||||
# some magic, creating the proper hash for us based on the
|
||||
# AuthenticationAuthority attribute, set above.
|
||||
dscl_vector = self.class.get_exec_preamble("-passwd", @resource[:name])
|
||||
dscl_vector << passphrase
|
||||
# JJM: Should we not log the password string? This may be a security
|
||||
# risk...
|
||||
begin
|
||||
dscl_output = execute(dscl_vector)
|
||||
rescue Puppet::ExecutionFailure => detail
|
||||
raise Puppet::Error, "Could not set password using command vector: %{dscl_vector.inspect}"
|
||||
end
|
||||
exec_arg_vector = self.class.get_exec_preamble("-read", @resource.name)
|
||||
exec_arg_vector << @@ns_to_ds_attribute_map[:guid]
|
||||
begin
|
||||
guid_output = execute(exec_arg_vector)
|
||||
guid_plist = Plist.parse_xml(guid_output)
|
||||
# Although GeneratedUID like all DirectoryService values can be multi-valued
|
||||
# according to the schema, in practice user accounts cannot have multiple UUIDs
|
||||
# otherwise Bad Things Happen, so we just deal with the first value.
|
||||
guid = guid_plist["dsAttrTypeStandard:#{@@ns_to_ds_attribute_map[:guid]}"][0]
|
||||
self.class.set_password(@resource.name, guid, passphrase)
|
||||
rescue Puppet::ExecutionFailure => detail
|
||||
raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
|
||||
end
|
||||
end
|
||||
|
||||
# JJM: nameservice.rb defines methods for each attribute of the type.
|
||||
# We implement these methods here, by implementing get() and set()
|
||||
# See the resource_type= method defined in nameservice.rb
|
||||
# I'm not sure what the implications are of doing things this way.
|
||||
# It was a bit difficult to sort out what was happening in my head,
|
||||
# but ruby-debug makes this process much more transparent.
|
||||
#
|
||||
def set(property, value)
|
||||
# JJM: As it turns out, the set method defined in our parent class
|
||||
# is fine. It just calls the modifycmd() method, which
|
||||
# I'll implement here.
|
||||
super
|
||||
end
|
||||
# NBK: we override @parent.set as we need to execute a series of commands
|
||||
# to deal with array values, rather than the single command nameservice.rb
|
||||
# expects to be returned by modifycmd. Thus we don't bother defining modifycmd.
|
||||
|
||||
def get(param)
|
||||
hash = getinfo(false)
|
||||
if hash
|
||||
return hash[param]
|
||||
def set(param, value)
|
||||
self.class.validate(param, value)
|
||||
current_members = @property_value_cache_hash[:members]
|
||||
if param == :members
|
||||
# If we are meant to be authoritative for the group membership
|
||||
# then remove all existing members who haven't been specified
|
||||
# in the manifest.
|
||||
if @resource[:auth_membership] and not current_members.nil?
|
||||
remove_unwanted_members(current_members, value)
|
||||
end
|
||||
|
||||
# if they're not a member, make them one.
|
||||
add_members(current_members, value)
|
||||
else
|
||||
return :absent
|
||||
exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
|
||||
# JJM: The following line just maps the NS name to the DS name
|
||||
# e.g. { :uid => 'UniqueID' }
|
||||
exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(param)]
|
||||
# JJM: The following line sends the actual value to set the property to
|
||||
exec_arg_vector << value.to_s
|
||||
begin
|
||||
execute(exec_arg_vector)
|
||||
rescue Puppet::ExecutionFailure => detail
|
||||
raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def modifycmd(property, value)
|
||||
# JJM: This method will assemble a exec vector which modifies
|
||||
# a single property and it's value using dscl.
|
||||
# JJM: With /usr/bin/dscl, the -create option will destroy an
|
||||
# existing property record if it exists
|
||||
# NBK: we override @parent.create as we need to execute a series of commands
|
||||
# to create objects with dscl, rather than the single command nameservice.rb
|
||||
# expects to be returned by addcmd. Thus we don't bother defining addcmd.
|
||||
def create
|
||||
if exists?
|
||||
info "already exists"
|
||||
# The object already exists
|
||||
return nil
|
||||
end
|
||||
|
||||
# NBK: First we create the object with a known guid so we can set the contents
|
||||
# of the password hash if required
|
||||
# Shelling out sucks, but for a single use case it doesn't seem worth
|
||||
# requiring people install a UUID library that doesn't come with the system.
|
||||
# This should be revisited if Puppet starts managing UUIDs for other platform
|
||||
# user records.
|
||||
guid = %x{/usr/bin/uuidgen}.chomp
|
||||
|
||||
exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
|
||||
# JJM: The following line just maps the NS name to the DS name
|
||||
# e.g. { :uid => 'UniqueID' }
|
||||
exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)]
|
||||
# JJM: The following line sends the actual value to set the property to
|
||||
exec_arg_vector << value.to_s
|
||||
return exec_arg_vector
|
||||
exec_arg_vector << @@ns_to_ds_attribute_map[:guid] << guid
|
||||
begin
|
||||
execute(exec_arg_vector)
|
||||
rescue Puppet::ExecutionFailure => detail
|
||||
raise Puppet::Error, "Could not set GeneratedUID for %s %s: %s" %
|
||||
[@resource.class.name, @resource.name, detail]
|
||||
end
|
||||
|
||||
if value = @resource.should(:password) and value != ""
|
||||
self.class.set_password(@resource[:name], guid, value)
|
||||
end
|
||||
|
||||
# Now we create all the standard properties
|
||||
Puppet::Type.type(@resource.class.name).validproperties.each do |property|
|
||||
next if property == :ensure
|
||||
if value = @resource.should(property) and value != ""
|
||||
if property == :members
|
||||
add_members(nil, value)
|
||||
else
|
||||
exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
|
||||
exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)]
|
||||
next if property == :password # skip setting the password here
|
||||
exec_arg_vector << value.to_s
|
||||
begin
|
||||
execute(exec_arg_vector)
|
||||
rescue Puppet::ExecutionFailure => detail
|
||||
raise Puppet::Error, "Could not create %s %s: %s" %
|
||||
[@resource.class.name, @resource.name, detail]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def addcmd
|
||||
# JJM 2007-07-24:
|
||||
# - addcmd returns an array to be executed to create a new object.
|
||||
# - This method is probably being called from the
|
||||
# ensure= method in nameservice.rb, or here...
|
||||
# - This should only be called if the object doesn't exist.
|
||||
# JJM: Blame nameservice.rb for the terse method name. =)
|
||||
#
|
||||
self.class.get_exec_preamble("-create", @resource[:name])
|
||||
def remove_unwanted_members(current_members, new_members)
|
||||
current_members.each do |member|
|
||||
if not value.include?(member)
|
||||
cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-d", member, @resource[:name]]
|
||||
begin
|
||||
execute(cmd)
|
||||
rescue Puppet::ExecutionFailure => detail
|
||||
raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_members(current_members, new_members)
|
||||
new_members.each do |user|
|
||||
if current_members.nil? or not current_members.include?(user)
|
||||
cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-a", user, @resource[:name]]
|
||||
begin
|
||||
execute(cmd)
|
||||
rescue Puppet::ExecutionFailure => detail
|
||||
raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def deletecmd
|
||||
|
@ -341,9 +440,13 @@ class DirectoryService < Puppet::Provider::NameService
|
|||
# list, then report on the remaining list. Pretty whacky, ehh?
|
||||
type_properties = [:name] + self.class.resource_type.validproperties
|
||||
type_properties.delete(:ensure) if type_properties.include? :ensure
|
||||
type_properties << :guid # append GeneratedUID so we just get the report here
|
||||
@property_value_cache_hash = self.class.single_report(@resource[:name], *type_properties)
|
||||
[:uid, :gid].each do |param|
|
||||
@property_value_cache_hash[param] = @property_value_cache_hash[param].to_i if @property_value_cache_hash and @property_value_cache_hash.include?(param)
|
||||
end
|
||||
end
|
||||
return @property_value_cache_hash
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,9 +12,6 @@
|
|||
# As a result, we store installed .app.dmg file names
|
||||
# in /var/db/.puppet_appdmg_installed_<name>
|
||||
|
||||
# require 'ruby-debug'
|
||||
# Debugger.start
|
||||
|
||||
require 'puppet/provider/package'
|
||||
Puppet::Type.type(:package).provide(:appdmg, :parent => Puppet::Provider::Package) do
|
||||
desc "Package management which copies application bundles to a target."
|
||||
|
|
|
@ -99,10 +99,16 @@ Puppet::Type.type(:package).provide :apt, :parent => :dpkg, :source => :dpkg do
|
|||
end
|
||||
|
||||
def uninstall
|
||||
if @resource[:responsefile]
|
||||
self.run_preseed
|
||||
end
|
||||
aptget "-y", "-q", :remove, @resource[:name]
|
||||
end
|
||||
|
||||
def purge
|
||||
if @resource[:responsefile]
|
||||
self.run_preseed
|
||||
end
|
||||
aptget '-y', '-q', :remove, '--purge', @resource[:name]
|
||||
# workaround a "bug" in apt, that already removed packages are not purged
|
||||
super
|
||||
|
|
|
@ -4,6 +4,8 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr
|
|||
desc "RPM packaging support; should work anywhere with a working ``rpm``
|
||||
binary."
|
||||
|
||||
has_feature :versionable
|
||||
|
||||
# The query format by which we identify installed packages
|
||||
NEVRAFORMAT = "%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}"
|
||||
NEVRA_FIELDS = [:name, :epoch, :version, :release, :arch]
|
||||
|
|
|
@ -100,5 +100,9 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do
|
|||
# Install in yum can be used for update, too
|
||||
self.install
|
||||
end
|
||||
end
|
||||
|
||||
def purge
|
||||
yum "-y", :erase, @resource[:name]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -39,6 +39,15 @@ def pkg_lists(my):
|
|||
|
||||
my.doTsSetup()
|
||||
my.doRpmDBSetup()
|
||||
|
||||
# Yum 2.2/2.3 python libraries require a couple of extra function calls to setup package sacks.
|
||||
# They also don't have a __version__ attribute
|
||||
try:
|
||||
yumver = yum.__version__
|
||||
except AttributeError:
|
||||
my.doRepoSetup()
|
||||
my.doSackSetup()
|
||||
|
||||
return my.doPackageLists('updates')
|
||||
|
||||
def shell_out():
|
||||
|
|
|
@ -2,27 +2,37 @@
|
|||
#
|
||||
# author Brice Figureau <brice-puppet@daysofwonder.com>
|
||||
Puppet::Type.type(:service).provide :daemontools, :parent => :base do
|
||||
desc "Daemontools service management.
|
||||
This provider manages daemons running supervised by D.J.Bernstein daemontools.
|
||||
It tries to detect the service directory, with by order of preference:
|
||||
* /service
|
||||
* /etc/service
|
||||
* /var/lib/svscan
|
||||
The daemon directory should be placed in a directory that can be
|
||||
by default in:
|
||||
* /var/lib/service
|
||||
* /etc
|
||||
or this can be overriden in the service resource parameters:
|
||||
desc """
|
||||
Daemontools service management.
|
||||
This provider manages daemons running supervised by D.J.Bernstein daemontools.
|
||||
It tries to detect the service directory, with by order of preference::
|
||||
|
||||
* /service
|
||||
* /etc/service
|
||||
* /var/lib/svscan
|
||||
|
||||
The daemon directory should be placed in a directory that can be
|
||||
by default in::
|
||||
|
||||
* /var/lib/service
|
||||
* /etc
|
||||
|
||||
or this can be overriden in the service resource parameters::
|
||||
|
||||
service {
|
||||
\"myservice\":
|
||||
provider => \"daemontools\", path => \"/path/to/daemons\";
|
||||
}
|
||||
|
||||
This provider supports out of the box:
|
||||
* start/stop (mapped to enable/disable)
|
||||
* enable/disable
|
||||
* restart
|
||||
* status"
|
||||
This provider supports out of the box::
|
||||
|
||||
* start/stop (mapped to enable/disable)
|
||||
* enable/disable
|
||||
* restart
|
||||
* status
|
||||
|
||||
|
||||
"""
|
||||
|
||||
commands :svc => "/usr/bin/svc"
|
||||
commands :svstat => "/usr/bin/svstat"
|
||||
|
|
|
@ -35,7 +35,7 @@ Puppet::Type.type(:service).provide :gentoo, :parent => :init do
|
|||
return :false unless line
|
||||
|
||||
# If it's enabled then it will print output showing service | runlevel
|
||||
if output =~ /#{@resource[:name]}\s*|\s*default/
|
||||
if output =~ /#{@resource[:name]}\s*\|\s*default/
|
||||
return :true
|
||||
else
|
||||
return :false
|
||||
|
|
|
@ -24,24 +24,30 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
|
|||
|
||||
# List all services of this type.
|
||||
def self.instances
|
||||
path = self.defpath
|
||||
unless FileTest.directory?(path)
|
||||
Puppet.notice "Service path %s does not exist" % path
|
||||
next
|
||||
end
|
||||
|
||||
check = [:ensure]
|
||||
|
||||
if public_method_defined? :enabled?
|
||||
check << :enable
|
||||
end
|
||||
|
||||
Dir.entries(path).reject { |e|
|
||||
fullpath = File.join(path, e)
|
||||
e =~ /^\./ or ! FileTest.executable?(fullpath)
|
||||
}.collect do |name|
|
||||
new(:name => name, :path => path)
|
||||
self.defpath = [self.defpath] unless self.defpath.is_a? Array
|
||||
|
||||
instances = []
|
||||
|
||||
self.defpath.each do |path|
|
||||
unless FileTest.directory?(path)
|
||||
Puppet.debug "Service path %s does not exist" % path
|
||||
next
|
||||
end
|
||||
|
||||
check = [:ensure]
|
||||
|
||||
if public_method_defined? :enabled?
|
||||
check << :enable
|
||||
end
|
||||
|
||||
Dir.entries(path).each do |name|
|
||||
fullpath = File.join(path, name)
|
||||
next if name =~ /^\./
|
||||
next if not FileTest.executable?(fullpath)
|
||||
instances << new(:name => name, :path => path)
|
||||
end
|
||||
end
|
||||
instances
|
||||
end
|
||||
|
||||
# Mark that our init script supports 'status' commands.
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
require 'facter/util/plist'
|
||||
|
||||
Puppet::Type.type(:service).provide :launchd, :parent => :base do
|
||||
desc "launchd service management framework.
|
||||
|
||||
This provider manages launchd jobs, the default service framework for
|
||||
Mac OS X, that has also been open sourced by Apple for possible use on
|
||||
other platforms.
|
||||
|
||||
See:
|
||||
* http://developer.apple.com/macosx/launchd.html
|
||||
* http://launchd.macosforge.org/
|
||||
|
||||
This provider reads plists out of the following directories:
|
||||
* /System/Library/LaunchDaemons
|
||||
* /System/Library/LaunchAgents
|
||||
* /Library/LaunchDaemons
|
||||
* /Library/LaunchAgents
|
||||
|
||||
and builds up a list of services based upon each plists \"Label\" entry.
|
||||
|
||||
This provider supports:
|
||||
* ensure => running/stopped,
|
||||
* enable => true/false
|
||||
* status
|
||||
* restart
|
||||
|
||||
Here is how the Puppet states correspond to launchd states:
|
||||
* stopped => job unloaded
|
||||
* started => job loaded
|
||||
* enabled => 'Disable' removed from job plist file
|
||||
* disabled => 'Disable' added to job plist file
|
||||
|
||||
Note that this allows you to do something launchctl can't do, which is to
|
||||
be in a state of \"stopped/enabled\ or \"running/disabled\".
|
||||
"
|
||||
|
||||
commands :launchctl => "/bin/launchctl"
|
||||
|
||||
defaultfor :operatingsystem => :darwin
|
||||
confine :operatingsystem => :darwin
|
||||
|
||||
has_feature :enableable
|
||||
|
||||
Launchd_Paths = ["/Library/LaunchAgents",
|
||||
"/Library/LaunchDaemons",
|
||||
"/System/Library/LaunchAgents",
|
||||
"/System/Library/LaunchDaemons",]
|
||||
|
||||
|
||||
# returns a label => path map for either all jobs, or just a single
|
||||
# job if the label is specified
|
||||
def self.jobsearch(label=nil)
|
||||
label_to_path_map = {}
|
||||
Launchd_Paths.each do |path|
|
||||
if FileTest.exists?(path)
|
||||
Dir.entries(path).each do |f|
|
||||
next if f =~ /^\..*$/
|
||||
next if FileTest.directory?(f)
|
||||
fullpath = File.join(path, f)
|
||||
job = Plist::parse_xml(fullpath)
|
||||
if job and job.has_key?("Label")
|
||||
if job["Label"] == label
|
||||
return { label => fullpath }
|
||||
else
|
||||
label_to_path_map[job["Label"]] = fullpath
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# if we didn't find the job above and we should have, error.
|
||||
if label
|
||||
raise Puppet::Error.new("Unable to find launchd plist for job: #{label}")
|
||||
end
|
||||
# if returning all jobs
|
||||
label_to_path_map
|
||||
end
|
||||
|
||||
|
||||
def self.instances
|
||||
jobs = self.jobsearch
|
||||
jobs.keys.collect do |job|
|
||||
new(:name => job, :provider => :launchd, :path => jobs[job])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# finds the path for a given label and returns the path and parsed plist
|
||||
# as an array of [path, plist]. Note plist is really a Hash here.
|
||||
def plist_from_label(label)
|
||||
job = self.class.jobsearch(label)
|
||||
job_path = job[label]
|
||||
job_plist = Plist::parse_xml(job_path)
|
||||
if not job_plist
|
||||
raise Puppet::Error.new("Unable to parse launchd plist at path: #{job_path}")
|
||||
end
|
||||
[job_path, job_plist]
|
||||
end
|
||||
|
||||
|
||||
def status
|
||||
# launchctl list <jobname> exits zero if the job is loaded
|
||||
# and non-zero if it isn't. Simple way to check...
|
||||
begin
|
||||
launchctl :list, resource[:name]
|
||||
return :running
|
||||
rescue Puppet::ExecutionFailure
|
||||
return :stopped
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# start the service. To get to a state of running/enabled, we need to
|
||||
# conditionally enable at load, then disable by modifying the plist file
|
||||
# directly.
|
||||
def start
|
||||
job_path, job_plist = plist_from_label(resource[:name])
|
||||
did_enable_job = false
|
||||
cmds = []
|
||||
cmds << :launchctl << :load
|
||||
if self.enabled? == :false # launchctl won't load disabled jobs
|
||||
cmds << "-w"
|
||||
did_enable_job = true
|
||||
end
|
||||
cmds << job_path
|
||||
begin
|
||||
execute(cmds)
|
||||
rescue Puppet::ExecutionFailure
|
||||
raise Puppet::Error.new("Unable to start service: %s at path: %s" % [resource[:name], job_path])
|
||||
end
|
||||
# As load -w clears the Disabled flag, we need to add it in after
|
||||
if did_enable_job and resource[:enable] == :false
|
||||
self.disable
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def stop
|
||||
job_path, job_plist = plist_from_label(resource[:name])
|
||||
did_disable_job = false
|
||||
cmds = []
|
||||
cmds << :launchctl << :unload
|
||||
if self.enabled? == :true # keepalive jobs can't be stopped without disabling
|
||||
cmds << "-w"
|
||||
did_disable_job = true
|
||||
end
|
||||
cmds << job_path
|
||||
begin
|
||||
execute(cmds)
|
||||
rescue Puppet::ExecutionFailure
|
||||
raise Puppet::Error.new("Unable to stop service: %s at path: %s" % [resource[:name], job_path])
|
||||
end
|
||||
# As unload -w sets the Disabled flag, we need to add it in after
|
||||
if did_disable_job and resource[:enable] == :true
|
||||
self.enable
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# launchd jobs are enabled by default. They are only disabled if the key
|
||||
# "Disabled" is set to true, but it can also be set to false to enable it.
|
||||
def enabled?
|
||||
job_path, job_plist = plist_from_label(resource[:name])
|
||||
if job_plist.has_key?("Disabled")
|
||||
if job_plist["Disabled"] # inverse of disabled is enabled
|
||||
return :false
|
||||
end
|
||||
end
|
||||
return :true
|
||||
end
|
||||
|
||||
|
||||
# enable and disable are a bit hacky. We write out the plist with the appropriate value
|
||||
# rather than dealing with launchctl as it is unable to change the Disabled flag
|
||||
# without actually loading/unloading the job.
|
||||
def enable
|
||||
job_path, job_plist = plist_from_label(resource[:name])
|
||||
if self.enabled? == :false
|
||||
job_plist.delete("Disabled")
|
||||
Plist::Emit.save_plist(job_plist, job_path)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def disable
|
||||
job_path, job_plist = plist_from_label(resource[:name])
|
||||
job_plist["Disabled"] = true
|
||||
Plist::Emit.save_plist(job_plist, job_path)
|
||||
end
|
||||
|
||||
|
||||
end
|
|
@ -2,26 +2,36 @@
|
|||
#
|
||||
# author Brice Figureau <brice-puppet@daysofwonder.com>
|
||||
Puppet::Type.type(:service).provide :runit, :parent => :daemontools do
|
||||
desc "Runit service management.
|
||||
This provider manages daemons running supervised by Runit.
|
||||
It tries to detect the service directory, with by order of preference:
|
||||
* /service
|
||||
* /var/service
|
||||
* /etc/service
|
||||
The daemon directory should be placed in a directory that can be
|
||||
by default in:
|
||||
* /etc/sv
|
||||
or this can be overriden in the service resource parameters:
|
||||
desc """
|
||||
Runit service management.
|
||||
This provider manages daemons running supervised by Runit.
|
||||
It tries to detect the service directory, with by order of preference::
|
||||
|
||||
* /service
|
||||
* /var/service
|
||||
* /etc/service
|
||||
|
||||
The daemon directory should be placed in a directory that can be
|
||||
by default in::
|
||||
|
||||
* /etc/sv
|
||||
|
||||
or this can be overriden in the service resource parameters::
|
||||
|
||||
service {
|
||||
\"myservice\":
|
||||
provider => \"runit\", path => \"/path/to/daemons\";
|
||||
}
|
||||
|
||||
This provider supports out of the box:
|
||||
* start/stop
|
||||
* enable/disable
|
||||
* restart
|
||||
* status"
|
||||
This provider supports out of the box::
|
||||
|
||||
* start/stop
|
||||
* enable/disable
|
||||
* restart
|
||||
* status
|
||||
|
||||
|
||||
"""
|
||||
|
||||
commands :sv => "/usr/bin/sv"
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ Puppet::Type.type(:ssh_authorized_key).provide(:parsed,
|
|||
if record[:options].nil?
|
||||
record[:options] = [:absent]
|
||||
else
|
||||
record[:options] = record[:options].split(',')
|
||||
record[:options] = Puppet::Type::Ssh_authorized_key::ProviderParsed.parse_options(record[:options])
|
||||
end
|
||||
},
|
||||
:pre_gen => proc { |record|
|
||||
|
@ -71,5 +71,25 @@ Puppet::Type.type(:ssh_authorized_key).provide(:parsed,
|
|||
File.chown(Puppet::Util.uid(user), nil, @property_hash[:target])
|
||||
end
|
||||
end
|
||||
|
||||
# parse sshv2 option strings, wich is a comma separated list of
|
||||
# either key="values" elements or bare-word elements
|
||||
def self.parse_options(options)
|
||||
result = []
|
||||
scanner = StringScanner.new(options)
|
||||
while !scanner.eos?
|
||||
scanner.skip(/[ \t]*/)
|
||||
# scan a long option
|
||||
if out = scanner.scan(/[-a-z0-9A-Z_]+=\".*?\"/) or out = scanner.scan(/[-a-z0-9A-Z_]+/)
|
||||
result << out
|
||||
else
|
||||
# found an unscannable token, let's abort
|
||||
break
|
||||
end
|
||||
# eat a comma
|
||||
scanner.skip(/[ \t]*,[ \t]*/)
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,17 +22,15 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd do
|
|||
value !~ /\s/
|
||||
end
|
||||
|
||||
has_features :manages_homedir, :allows_duplicates, :manages_solaris_rbac
|
||||
|
||||
if Puppet.features.libshadow?
|
||||
has_feature :manages_passwords
|
||||
end
|
||||
has_features :manages_homedir, :allows_duplicates, :manages_solaris_rbac, :manages_passwords
|
||||
|
||||
#must override this to hand the keyvalue pairs
|
||||
def add_properties
|
||||
cmd = []
|
||||
Puppet::Type.type(:user).validproperties.each do |property|
|
||||
next if property == :ensure
|
||||
#skip the password because we can't create it with the solaris useradd
|
||||
next if [:ensure, :password].include?(property)
|
||||
# 1680 Now you can set the hashed passwords on solaris:lib/puppet/provider/user/user_role_add.rb
|
||||
# the value needs to be quoted, mostly because -c might
|
||||
# have spaces in it
|
||||
if value = @resource.should(property) and value != ""
|
||||
|
@ -87,6 +85,10 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd do
|
|||
else
|
||||
run(addcmd, "create")
|
||||
end
|
||||
# added to handle case when password is specified
|
||||
if @resource[:password]
|
||||
self.password = @resource[:password]
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -152,5 +154,40 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd do
|
|||
def keys=(keys_hash)
|
||||
run([command(:modify)] + build_keys_cmd(keys_hash) << @resource[:name], "modify attribute key pairs")
|
||||
end
|
||||
|
||||
#Read in /etc/shadow, find the line for this user (skipping comments, because who knows) and return the hashed pw (the second entry)
|
||||
#No abstraction, all esoteric knowledge of file formats, yay
|
||||
def password
|
||||
#got perl?
|
||||
if ary = File.readlines("/etc/shadow").reject { |r| r =~ /^[^\w]/}.collect { |l| l.split(':')[0..1] }.find { |user, passwd| user == @resource[:name] }
|
||||
pass = ary[1]
|
||||
end
|
||||
pass
|
||||
end
|
||||
|
||||
#Read in /etc/shadow, find the line for our used and rewrite it with the new pw
|
||||
#Smooth like 80 grit
|
||||
def password=(cryptopw)
|
||||
begin
|
||||
File.open("/etc/shadow", "r") do |shadow|
|
||||
File.open("/etc/shadow_tmp", "w", 0600) do |shadow_tmp|
|
||||
while line = shadow.gets do
|
||||
line_arr = line.split(':')
|
||||
if line_arr[0] == @resource[:name]
|
||||
line_arr[1] = cryptopw
|
||||
line = line_arr.join(':')
|
||||
end
|
||||
shadow_tmp.print line
|
||||
end
|
||||
end
|
||||
end
|
||||
File.rename("/etc/shadow_tmp", "/etc/shadow")
|
||||
rescue => detail
|
||||
fail "Could not write temporary shadow file: %s" % detail
|
||||
ensure
|
||||
# Make sure this *always* gets deleted
|
||||
File.unlink("/etc/shadow_tmp") if File.exist?("/etc/shadow_tmp")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
Puppet::Type.type(:zfs).provide(:solaris) do
|
||||
desc "Provider for Solaris zfs."
|
||||
|
||||
commands :zfs => "/usr/sbin/zfs"
|
||||
defaultfor :operatingsystem => :solaris
|
||||
|
||||
def add_properties
|
||||
properties = []
|
||||
Puppet::Type.type(:zfs).validproperties.each do |property|
|
||||
next if property == :ensure
|
||||
if value = @resource[property] and value != ""
|
||||
properties << "-o" << "#{property}=#{value}"
|
||||
end
|
||||
end
|
||||
properties
|
||||
end
|
||||
|
||||
def arrayify_second_line_on_whitespace(text)
|
||||
if second_line = text.split("\n")[1]
|
||||
second_line.split("\s")
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
zfs *([:create] + add_properties + [@resource[:name]])
|
||||
end
|
||||
|
||||
def delete
|
||||
zfs(:destroy, @resource[:name])
|
||||
end
|
||||
|
||||
def exists?
|
||||
if zfs(:list).split("\n").detect { |line| line.split("\s")[0] == @resource[:name] }
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
[:mountpoint, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir].each do |field|
|
||||
define_method(field) do
|
||||
#special knowledge of format
|
||||
#the command returns values in this format with the header
|
||||
#NAME PROPERTY VALUE SOURCE
|
||||
arrayify_second_line_on_whitespace(zfs(:get, field, @resource[:name]))[2]
|
||||
end
|
||||
|
||||
define_method(field.to_s + "=") do |should|
|
||||
zfs(:set, "#{field}=#{should}", @resource[:name])
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -12,6 +12,7 @@ Puppet::Type.type(:zone).provide(:solaris) do
|
|||
|
||||
properties = {}
|
||||
line.split(":").each_with_index { |value, index|
|
||||
next unless fields[index]
|
||||
properties[fields[index]] = value
|
||||
}
|
||||
|
||||
|
@ -35,9 +36,8 @@ Puppet::Type.type(:zone).provide(:solaris) do
|
|||
# Perform all of our configuration steps.
|
||||
def configure
|
||||
# If the thing is entirely absent, then we need to create the config.
|
||||
str = %{create -b
|
||||
set zonepath=%s
|
||||
} % @resource[:path]
|
||||
# Is there someway to get this on one line?
|
||||
str = "create -b #{@resource[:create_args]}\nset zonepath=%s\n" % @resource[:path]
|
||||
|
||||
# Then perform all of our configuration steps. It's annoying
|
||||
# that we need this much internal info on the resource.
|
||||
|
@ -65,7 +65,11 @@ set zonepath=%s
|
|||
end
|
||||
|
||||
def install
|
||||
zoneadm :install
|
||||
if @resource[:install_args]
|
||||
zoneadm :install, @resource[:install_args].split(" ")
|
||||
else
|
||||
zoneadm :install
|
||||
end
|
||||
end
|
||||
|
||||
# Look up the current status.
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
Puppet::Type.type(:zpool).provide(:solaris) do
|
||||
desc "Provider for Solaris zpool."
|
||||
|
||||
commands :zpool => "/usr/sbin/zpool"
|
||||
defaultfor :operatingsystem => :solaris
|
||||
|
||||
def process_zpool_data(pool_array)
|
||||
if pool_array == []
|
||||
return Hash.new(:absent)
|
||||
end
|
||||
#get the name and get rid of it
|
||||
pool = Hash.new([])
|
||||
pool[:pool] = pool_array[0]
|
||||
pool_array.shift
|
||||
|
||||
#order matters here :(
|
||||
tmp = []
|
||||
|
||||
pool_array.reverse.each_with_index do |value, i|
|
||||
case value
|
||||
when "spares": pool[:spare] = tmp.reverse and tmp.clear
|
||||
when "logs": pool[:log] = tmp.reverse and tmp.clear
|
||||
when "mirror", "raidz1", "raidz2":
|
||||
sym = value == "mirror" ? :mirror : :raidz
|
||||
pool[sym].unshift(tmp.reverse.join(' '))
|
||||
pool[:raid_parity] = "raidz2" if value == "raidz2"
|
||||
tmp.clear
|
||||
else
|
||||
tmp << value
|
||||
pool[:disk] = tmp.reverse if i == 0
|
||||
end
|
||||
end
|
||||
|
||||
pool
|
||||
end
|
||||
|
||||
def get_pool_data
|
||||
#this is all voodoo dependent on the output from zpool
|
||||
zpool_data = %x{ zpool status #{@resource[:pool]}}.split("\n").select { |line| line.index("\t") == 0 }.collect { |l| l.strip.split("\s")[0] }
|
||||
zpool_data.shift
|
||||
zpool_data
|
||||
end
|
||||
|
||||
def current_pool
|
||||
unless (defined?(@current_pool) and @current_pool)
|
||||
@current_pool = process_zpool_data(get_pool_data)
|
||||
end
|
||||
@current_pool
|
||||
end
|
||||
|
||||
def flush
|
||||
@current_pool= nil
|
||||
end
|
||||
|
||||
#Adds log and spare
|
||||
def build_named(name)
|
||||
if prop = @resource[name.intern]
|
||||
[name] + prop.collect { |p| p.split(' ') }.flatten
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
#query for parity and set the right string
|
||||
def raidzarity
|
||||
@resource[:raid_parity] ? @resource[:raid_parity] : "raidz1"
|
||||
end
|
||||
|
||||
#handle mirror or raid
|
||||
def handle_multi_arrays(prefix, array)
|
||||
array.collect{ |a| [prefix] + a.split(' ') }.flatten
|
||||
end
|
||||
|
||||
#builds up the vdevs for create command
|
||||
def build_vdevs
|
||||
if disk = @resource[:disk]
|
||||
disk.collect { |d| d.split(' ') }.flatten
|
||||
elsif mirror = @resource[:mirror]
|
||||
handle_multi_arrays("mirror", mirror)
|
||||
elsif raidz = @resource[:raidz]
|
||||
handle_multi_arrays(raidzarity, raidz)
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
zpool(*([:create, @resource[:pool]] + build_vdevs + build_named("spare") + build_named("log")))
|
||||
end
|
||||
|
||||
def delete
|
||||
zpool :destroy, @resource[:pool]
|
||||
end
|
||||
|
||||
def exists?
|
||||
if current_pool[:pool] == :absent
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
[:disk, :mirror, :raidz, :log, :spare].each do |field|
|
||||
define_method(field) do
|
||||
current_pool[field]
|
||||
end
|
||||
|
||||
define_method(field.to_s + "=") do |should|
|
||||
Puppet.warning "NO CHANGES BEING MADE: zpool %s does not match, should be '%s' currently is '%s'" % [field, should, current_pool[field]]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
class AddEnvironmentToHost < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :hosts, :environment, :string
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :hosts, :environment
|
||||
end
|
||||
end
|
|
@ -54,6 +54,7 @@ class Puppet::Rails::Schema
|
|||
create_table :hosts do |t|
|
||||
t.column :name, :string, :null => false
|
||||
t.column :ip, :string
|
||||
t.column :environment, :string
|
||||
t.column :last_compile, :datetime
|
||||
t.column :last_freshcheck, :datetime
|
||||
t.column :last_report, :datetime
|
||||
|
|
|
@ -43,6 +43,10 @@ class Puppet::Rails::Host < ActiveRecord::Base
|
|||
host.ip = ip
|
||||
end
|
||||
|
||||
if env = node.environment
|
||||
host.environment = env
|
||||
end
|
||||
|
||||
# Store the facts into the database.
|
||||
host.setfacts node.parameters
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ Signals
|
|||
-------
|
||||
The ``puppetd`` and ``puppetmasterd`` executables catch some signals for special
|
||||
handling. Both daemons catch (``SIGHUP``), which forces the server to restart
|
||||
tself. Predictably, interrupt and terminate (``SIGINT`` and ``SIGHUP``) will shut
|
||||
tself. Predictably, interrupt and terminate (``SIGINT`` and ``SIGTERM``) will shut
|
||||
down the server, whether it be an instance of ``puppetd`` or ``puppetmasterd``.
|
||||
|
||||
Sending the ``SIGUSR1`` signal to an instance of ``puppetd`` will cause it to
|
||||
|
|
|
@ -151,12 +151,14 @@ Puppet::Reports.register_report(:tagmail) do
|
|||
reports.each do |emails, messages|
|
||||
Puppet.info "Sending report to %s" % emails.join(", ")
|
||||
# We need to open a separate process for every set of email addresses
|
||||
IO.popen(Puppet[:sendmail] + " " + emails.join(" "), "w") do |p|
|
||||
p.puts "From: #{Puppet[:reportfrom]}"
|
||||
p.puts "Subject: Puppet Report for %s" % self.host
|
||||
p.puts "To: " + emails.join(", ")
|
||||
sync.synchronize do
|
||||
IO.popen(Puppet[:sendmail] + " " + emails.join(" "), "w") do |p|
|
||||
p.puts "From: #{Puppet[:reportfrom]}"
|
||||
p.puts "Subject: Puppet Report for %s" % self.host
|
||||
p.puts "To: " + emails.join(", ")
|
||||
|
||||
p.puts messages
|
||||
p.puts messages
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue => detail
|
||||
|
@ -174,5 +176,11 @@ Puppet::Reports.register_report(:tagmail) do
|
|||
# Don't bother waiting for the pid to return.
|
||||
Process.detach(pid)
|
||||
end
|
||||
|
||||
def sync
|
||||
unless defined?(@sync)
|
||||
@sync = Sync.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -53,12 +53,10 @@ class Puppet::Transaction::Change
|
|||
# The transaction catches any exceptions here.
|
||||
events = @property.sync
|
||||
if events.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
if events.is_a?(Array)
|
||||
events = [(@property.name.to_s + "_changed").to_sym]
|
||||
elsif events.is_a?(Array)
|
||||
if events.empty?
|
||||
return nil
|
||||
events = [(@property.name.to_s + "_changed").to_sym]
|
||||
end
|
||||
else
|
||||
events = [events]
|
||||
|
|
|
@ -53,6 +53,7 @@ module Puppet
|
|||
Puppet.debug "Defining %s on %s" % [param, ref]
|
||||
trans[param] = value
|
||||
}
|
||||
trans.catalog = self.catalog
|
||||
Puppet::Type::Component.create(trans)
|
||||
end
|
||||
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
# Copyright (C) 2008 Red Hat Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# modify it under the terms of the GNU General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
# version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Publicretu
|
||||
# You should have received a copy of the GNU General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
|
|
@ -87,7 +87,7 @@ Puppet::Type.newtype(:component) do
|
|||
|
||||
@reference = Puppet::ResourceReference.new(:component, @title)
|
||||
|
||||
if catalog and ! catalog.resource[@reference.to_s]
|
||||
if catalog and ! catalog.resource(@reference.to_s)
|
||||
catalog.alias(self, @reference.to_s)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
Puppet::Type.newtype(:computer) do
|
||||
|
||||
@doc = "Computer object management using DirectoryService on OS X.
|
||||
|
||||
Note that these are distinctly different kinds of objects to 'hosts',
|
||||
as they require a MAC address and can have all sorts of policy attached to
|
||||
them.
|
||||
|
||||
This provider only manages Computer objects in the local directory service
|
||||
domain, not in remote directories.
|
||||
|
||||
If you wish to manage /etc/hosts on Mac OS X, then simply use the host
|
||||
type as per other platforms.
|
||||
|
||||
This type primarily exists to create localhost Computer objects that MCX
|
||||
policy can then be attached to."
|
||||
|
||||
# ensurable
|
||||
|
||||
# We autorequire the computer object in case it is being managed at the
|
||||
# file level by Puppet.
|
||||
|
||||
autorequire(:file) do
|
||||
if self[:name]
|
||||
"/var/db/dslocal/nodes/Default/computers/#{self[:name]}.plist"
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:ensure, :parent => Puppet::Property::Ensure) do
|
||||
desc "Control the existences of this computer record. Set this attribute to
|
||||
``present`` to ensure the computer record exists. Set it to ``absent``
|
||||
to delete any computer records with this name"
|
||||
newvalue(:present) do
|
||||
provider.create
|
||||
end
|
||||
|
||||
newvalue(:absent) do
|
||||
provider.delete
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:name) do
|
||||
desc "The authoritative 'short' name of the computer record."
|
||||
isnamevar
|
||||
end
|
||||
|
||||
newparam(:realname) do
|
||||
desc "The 'long' name of the computer record."
|
||||
end
|
||||
|
||||
newproperty(:en_address) do
|
||||
desc "The MAC address of the primary network interface. Must match en0."
|
||||
end
|
||||
|
||||
newproperty(:ip_address) do
|
||||
desc "The IP Address of the Computer object."
|
||||
end
|
||||
|
||||
end
|
|
@ -854,7 +854,7 @@ module Puppet
|
|||
# file creation/modification, so we have to do some extra checking.
|
||||
def property_fix
|
||||
properties.each do |thing|
|
||||
next unless [:mode, :owner, :group].include?(thing.name)
|
||||
next unless [:mode, :owner, :group, :seluser, :selrole, :seltype, :selrange].include?(thing.name)
|
||||
|
||||
# Make sure we get a new stat objct
|
||||
self.stat(true)
|
||||
|
|
|
@ -2,11 +2,10 @@ module Puppet
|
|||
Puppet.type(:file).ensurable do
|
||||
require 'etc'
|
||||
desc "Whether to create files that don't currently exist.
|
||||
Possible values are *absent*, *present* (will match any form of
|
||||
file existence, and if the file is missing will create an empty
|
||||
file), *file*, and *directory*. Specifying ``absent`` will delete
|
||||
the file, although currently this will not recursively delete
|
||||
directories.
|
||||
Possible values are *absent*, *present*, *file*, and *directory*.
|
||||
Specifying ``present`` will match any form of file existence, and
|
||||
if the file is missing will create an empty file. Specifying
|
||||
``absent`` will delete the file (and directory if recurse => true).
|
||||
|
||||
Anything other than those values will be considered to be a symlink.
|
||||
For instance, the following text creates a link::
|
||||
|
|
|
@ -103,22 +103,8 @@ module Puppet
|
|||
end
|
||||
|
||||
def sync
|
||||
unless @resource.stat(false)
|
||||
stat = @resource.stat(true)
|
||||
|
||||
unless stat
|
||||
self.debug "File does not exist; cannot set mode"
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
mode = self.should
|
||||
|
||||
if mode == :absent
|
||||
# This is really only valid for create states...
|
||||
return nil
|
||||
end
|
||||
|
||||
begin
|
||||
File.chmod(mode, @resource[:path])
|
||||
rescue => detail
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
module Puppet
|
||||
Puppet.type(:file).newproperty(:owner) do
|
||||
include Puppet::Util::POSIX
|
||||
include Puppet::Util::Warnings
|
||||
|
||||
require 'etc'
|
||||
desc "To whom the file should belong. Argument can be user name or
|
||||
user ID."
|
||||
|
@ -24,40 +27,32 @@ module Puppet
|
|||
end
|
||||
end
|
||||
|
||||
def name2id(value)
|
||||
if value.is_a?(Symbol)
|
||||
return value.to_s
|
||||
def insync?(current)
|
||||
unless Puppet::Util::SUIDManager.uid == 0
|
||||
warning "Cannot manage ownership unless running as root"
|
||||
return true
|
||||
end
|
||||
begin
|
||||
user = Etc.getpwnam(value)
|
||||
if user.uid == ""
|
||||
return nil
|
||||
|
||||
@should.each do |value|
|
||||
if value =~ /^\d+$/
|
||||
uid = Integer(value)
|
||||
elsif value.is_a?(String)
|
||||
fail "Could not find user %s" % value unless uid = uid(value)
|
||||
else
|
||||
uid = value
|
||||
end
|
||||
return user.uid
|
||||
rescue ArgumentError => detail
|
||||
return nil
|
||||
|
||||
return true if uid == current
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
# Determine if the user is valid, and if so, return the UID
|
||||
def validuser?(value)
|
||||
if value =~ /^\d+$/
|
||||
value = value.to_i
|
||||
end
|
||||
|
||||
if value.is_a?(Integer)
|
||||
# verify the user is a valid user
|
||||
if tmp = id2name(value)
|
||||
return value
|
||||
else
|
||||
return false
|
||||
end
|
||||
if number = uid(value)
|
||||
return number
|
||||
else
|
||||
if tmp = name2id(value)
|
||||
return tmp
|
||||
else
|
||||
return false
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -99,13 +94,6 @@ module Puppet
|
|||
return :absent
|
||||
end
|
||||
|
||||
# Set our method appropriately, depending on links.
|
||||
if stat.ftype == "link" and @resource[:links] != :follow
|
||||
@method = :lchown
|
||||
else
|
||||
@method = :chown
|
||||
end
|
||||
|
||||
currentvalue = stat.uid
|
||||
|
||||
# On OS X, files that are owned by -2 get returned as really
|
||||
|
@ -120,44 +108,24 @@ module Puppet
|
|||
end
|
||||
|
||||
def sync
|
||||
unless Puppet::Util::SUIDManager.uid == 0
|
||||
unless defined? @@notifieduid
|
||||
self.notice "Cannot manage ownership unless running as root"
|
||||
#@resource.delete(self.name)
|
||||
@@notifieduid = true
|
||||
end
|
||||
return nil
|
||||
# Set our method appropriately, depending on links.
|
||||
if resource[:links] == :manage
|
||||
method = :lchown
|
||||
else
|
||||
method = :chown
|
||||
end
|
||||
|
||||
user = nil
|
||||
unless user = self.validuser?(self.should)
|
||||
tmp = self.should
|
||||
unless defined? @@usermissing
|
||||
@@usermissing = {}
|
||||
end
|
||||
|
||||
if @@usermissing.include?(tmp)
|
||||
@@usermissing[tmp] += 1
|
||||
else
|
||||
self.notice "user %s does not exist" % tmp
|
||||
@@usermissing[tmp] = 1
|
||||
end
|
||||
return nil
|
||||
uid = nil
|
||||
@should.each do |user|
|
||||
break if uid = validuser?(user)
|
||||
end
|
||||
|
||||
unless @resource.stat(false)
|
||||
unless @resource.stat(true)
|
||||
self.debug "File does not exist; cannot set owner"
|
||||
return nil
|
||||
end
|
||||
#self.debug "%s: after refresh, is '%s'" % [self.class.name,@is]
|
||||
end
|
||||
raise Puppet::Error, "Could not find user(s) %s" % @should.join(",") unless uid
|
||||
|
||||
begin
|
||||
File.send(@method, user, nil, @resource[:path])
|
||||
File.send(method, uid, nil, @resource[:path])
|
||||
rescue => detail
|
||||
raise Puppet::Error, "Failed to set owner to '%s': %s" %
|
||||
[user, detail]
|
||||
raise Puppet::Error, "Failed to set owner to '%s': %s" % [uid, detail]
|
||||
end
|
||||
|
||||
return :file_changed
|
||||
|
|
|
@ -38,18 +38,13 @@ module Puppet
|
|||
return nil
|
||||
end
|
||||
property_default = self.parse_selinux_context(property, context)
|
||||
self.debug "Found #{property} default '#{property_default}' for #{@resource[:path]}"
|
||||
if not property_default.nil?
|
||||
self.debug "Found #{property} default '#{property_default}' for #{@resource[:path]}"
|
||||
end
|
||||
return property_default
|
||||
end
|
||||
|
||||
def sync
|
||||
unless @resource.stat(false)
|
||||
stat = @resource.stat(true)
|
||||
unless stat
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
self.set_selinux_context(@resource[:path], @should, name)
|
||||
return :file_changed
|
||||
end
|
||||
|
|
|
@ -1,28 +1,22 @@
|
|||
# Manage Unix groups. This class is annoyingly complicated; There
|
||||
# is some variety in whether systems use 'groupadd' or 'addgroup', but OS X
|
||||
# significantly complicates the picture by using NetInfo. Eventually we
|
||||
# will also need to deal with systems that have their groups hosted elsewhere
|
||||
# (e.g., in LDAP). That will likely only be a problem for OS X, since it
|
||||
# currently does not use the POSIX interfaces, since lookupd's cache screws
|
||||
# things up.
|
||||
|
||||
require 'etc'
|
||||
require 'facter'
|
||||
|
||||
module Puppet
|
||||
newtype(:group) do
|
||||
@doc = "Manage groups. This type can only create groups. Group
|
||||
membership must be managed on individual users. This resource type
|
||||
uses the prescribed native tools for creating groups and generally
|
||||
uses POSIX APIs for retrieving information about them. It does
|
||||
not directly modify ``/etc/group`` or anything.
|
||||
@doc = "Manage groups. On most platforms this can only create groups.
|
||||
Group membership must be managed on individual users.
|
||||
|
||||
For most platforms, the tools used are ``groupadd`` and its ilk;
|
||||
for Mac OS X, NetInfo is used. This is currently unconfigurable,
|
||||
but if you desperately need it to be so, please contact us."
|
||||
On some platforms such as OS X, group membership is managed as an
|
||||
attribute of the group, not the user record. Providers must have
|
||||
the feature 'manages_members' to manage the 'members' property of
|
||||
a group record."
|
||||
|
||||
feature :manages_members,
|
||||
"For directories where membership is an attribute of groups not users."
|
||||
|
||||
newproperty(:ensure) do
|
||||
desc "The basic state that the object should be in."
|
||||
ensurable do
|
||||
desc "Create or remove the group."
|
||||
|
||||
newvalue(:present) do
|
||||
provider.create
|
||||
|
@ -35,20 +29,6 @@ module Puppet
|
|||
|
||||
:group_removed
|
||||
end
|
||||
|
||||
# If they're talking about the thing at all, they generally want to
|
||||
# say it should exist.
|
||||
defaultto do
|
||||
if @resource.managed?
|
||||
:present
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def retrieve
|
||||
return provider.exists? ? :present : :absent
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:gid) do
|
||||
|
@ -87,13 +67,28 @@ module Puppet
|
|||
return gid
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:members, :array_matching => :all, :required_features => :manages_members) do
|
||||
desc "The members of the group. For directory services where group
|
||||
membership is stored in the group objects, not the users."
|
||||
|
||||
def change_to_s(currentvalue, newvalue)
|
||||
currentvalue = currentvalue.join(",") if currentvalue != :absent
|
||||
newvalue = newvalue.join(",")
|
||||
super(currentvalue, newvalue)
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:auth_membership) do
|
||||
desc "whether the provider is authoritative for group membership."
|
||||
defaultto true
|
||||
end
|
||||
|
||||
newparam(:name) do
|
||||
desc "The group name. While naming limitations vary by
|
||||
system, it is advisable to keep the name to the degenerate
|
||||
limitations, which is a maximum of 8 characters beginning with
|
||||
a letter."
|
||||
|
||||
isnamevar
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
#--
|
||||
# Copyright (C) 2008 Jeffrey J McCune.
|
||||
|
||||
# This program and entire repository is free software; you can
|
||||
# redistribute it and/or modify it under the terms of the GNU
|
||||
# General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
# Author: Jeff McCune <mccune.jeff@gmail.com>
|
||||
|
||||
Puppet::Type.newtype(:mcx) do
|
||||
|
||||
@doc = "MCX object management using DirectoryService on OS X.
|
||||
|
||||
Original Author: Jeff McCune <mccune.jeff@gmail.com>
|
||||
|
||||
The default provider of this type merely manages the XML plist as
|
||||
reported by the dscl -mcxexport command. This is similar to the
|
||||
content property of the file type in Puppet.
|
||||
|
||||
The recommended method of using this type is to use Work Group Manager
|
||||
to manage users and groups on the local computer, record the resulting
|
||||
puppet manifest using the command 'ralsh mcx' then deploying this
|
||||
to other machines.
|
||||
"
|
||||
feature :manages_content, \
|
||||
"The provider can manage MCXSettings as a string.",
|
||||
:methods => [:content, :content=]
|
||||
|
||||
ensurable do
|
||||
desc "Create or remove the MCX setting."
|
||||
|
||||
newvalue(:present) do
|
||||
provider.create
|
||||
end
|
||||
|
||||
newvalue(:absent) do
|
||||
provider.destroy
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
newparam(:name) do
|
||||
desc "The name of the resource being managed.
|
||||
The default naming convention follows Directory Service paths:
|
||||
'/Computers/localhost'
|
||||
'/Groups/admin'
|
||||
'/Users/localadmin'
|
||||
|
||||
The ds_type and ds_name type parameters are not necessary if the
|
||||
default naming convention is followed."
|
||||
isnamevar
|
||||
end
|
||||
|
||||
newparam(:ds_type) do
|
||||
|
||||
desc "The DirectoryService type this MCX setting attaches to."
|
||||
|
||||
newvalues(:user, :group, :computer, :computerlist)
|
||||
|
||||
end
|
||||
|
||||
newparam(:ds_name) do
|
||||
desc "The name to attach the MCX Setting to.
|
||||
e.g. 'localhost' when ds_type => computer. This setting is not
|
||||
required, as it may be parsed so long as the resource name is
|
||||
parseable. e.g. /Groups/admin where 'group' is the dstype."
|
||||
end
|
||||
|
||||
newproperty(:content, :required_features => :manages_content) do
|
||||
desc "The XML Plist. The value of MCXSettings in DirectoryService.
|
||||
This is the standard output from the system command:
|
||||
dscl localhost -mcxexport /Local/Default/<ds_type>/<ds_name>
|
||||
Note that ds_type is capitalized and plural in the dscl command."
|
||||
end
|
||||
|
||||
# JJM Yes, this is not DRY at all. Because of the code blocks
|
||||
# autorequire must be done this way. I think.
|
||||
|
||||
def setup_autorequire(type)
|
||||
# value returns a Symbol
|
||||
name = value(:name)
|
||||
ds_type = value(:ds_type)
|
||||
ds_name = value(:ds_name)
|
||||
if ds_type == type
|
||||
rval = [ ds_name.to_s ]
|
||||
else
|
||||
rval = [ ]
|
||||
end
|
||||
rval
|
||||
end
|
||||
|
||||
autorequire(:user) do
|
||||
setup_autorequire(:user)
|
||||
end
|
||||
|
||||
autorequire(:group) do
|
||||
setup_autorequire(:group)
|
||||
end
|
||||
|
||||
autorequire(:computer) do
|
||||
setup_autorequire(:computer)
|
||||
end
|
||||
|
||||
end
|
|
@ -38,6 +38,22 @@ module Puppet
|
|||
should be specified as an array."
|
||||
|
||||
defaultto do :absent end
|
||||
|
||||
def is_to_s(value)
|
||||
if value == :absent or value.include?(:absent)
|
||||
super
|
||||
else
|
||||
value.join(",")
|
||||
end
|
||||
end
|
||||
|
||||
def should_to_s(value)
|
||||
if value == :absent or value.include?(:absent)
|
||||
super
|
||||
else
|
||||
value.join(",")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
autorequire(:user) do
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
require 'etc'
|
||||
require 'facter'
|
||||
require 'puppet/property/list'
|
||||
require 'puppet/property/ordered_list'
|
||||
require 'puppet/property/keyvalue'
|
||||
|
||||
module Puppet
|
||||
|
@ -98,6 +99,16 @@ module Puppet
|
|||
end
|
||||
end
|
||||
|
||||
def insync?(is)
|
||||
# We know the 'is' is a number, so we need to convert the 'should' to a number,
|
||||
# too.
|
||||
@should.each do |value|
|
||||
return true if number = Puppet::Util.gid(value) and is == number
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
def sync
|
||||
found = false
|
||||
@should.each do |value|
|
||||
|
@ -109,6 +120,8 @@ module Puppet
|
|||
end
|
||||
|
||||
fail "Could not find group(s) %s" % @should.join(",") unless found
|
||||
|
||||
# Use the default event.
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -263,6 +276,17 @@ module Puppet
|
|||
end
|
||||
end
|
||||
|
||||
#autorequire the roles that the user has
|
||||
autorequire(:user) do
|
||||
reqs = []
|
||||
|
||||
if roles_property = @parameters[:roles] and roles = roles_property.should
|
||||
reqs += roles.split(',')
|
||||
end
|
||||
|
||||
reqs
|
||||
end
|
||||
|
||||
newparam(:role_membership) do
|
||||
desc "Whether specified roles should be treated as the only roles
|
||||
of which the user is a member or whether they should merely
|
||||
|
@ -301,7 +325,7 @@ module Puppet
|
|||
defaultto :minimum
|
||||
end
|
||||
|
||||
newproperty(:profiles, :parent => Puppet::Property::List, :required_features => :manages_solaris_rbac) do
|
||||
newproperty(:profiles, :parent => Puppet::Property::OrderedList, :required_features => :manages_solaris_rbac) do
|
||||
desc "The profiles the user has. Multiple profiles should be
|
||||
specified as an array."
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
module Puppet
|
||||
newtype(:zfs) do
|
||||
@doc = "Manage zfs. Create destroy and set properties on zfs instances."
|
||||
|
||||
ensurable
|
||||
|
||||
newparam(:name) do
|
||||
desc "The full name for this filesystem. (including the zpool)"
|
||||
end
|
||||
|
||||
newproperty(:mountpoint) do
|
||||
desc "The mountpoint property."
|
||||
end
|
||||
|
||||
newproperty(:compression) do
|
||||
desc "The compression property."
|
||||
end
|
||||
|
||||
newproperty(:copies) do
|
||||
desc "The copies property."
|
||||
end
|
||||
|
||||
newproperty(:quota) do
|
||||
desc "The quota property."
|
||||
end
|
||||
|
||||
newproperty(:reservation) do
|
||||
desc "The reservation property."
|
||||
end
|
||||
|
||||
newproperty(:sharenfs) do
|
||||
desc "The sharenfs property."
|
||||
end
|
||||
|
||||
newproperty(:snapdir) do
|
||||
desc "The sharenfs property."
|
||||
end
|
||||
|
||||
autorequire(:zpool) do
|
||||
#strip the zpool off the zfs name and autorequire it
|
||||
[@parameters[:name].value.split('/')[0]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -358,6 +358,14 @@ end
|
|||
end
|
||||
end
|
||||
|
||||
newparam(:create_args) do
|
||||
desc "Arguments to the zonecfg create command. This can be used to create branded zones."
|
||||
end
|
||||
|
||||
newparam(:install_args) do
|
||||
desc "Arguments to the zoneadm install command. This can be used to create branded zones."
|
||||
end
|
||||
|
||||
newparam(:realhostname) do
|
||||
desc "The actual hostname of the zone."
|
||||
end
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
module Puppet
|
||||
newtype(:zpool) do
|
||||
@doc = "Manage zpools. Create and delete zpools. The provider WILL NOT SYNC, only report differences.
|
||||
|
||||
Supports vdevs with mirrors, raidz, logs and spares."
|
||||
|
||||
ensurable
|
||||
|
||||
newproperty(:disk, :array_matching => :all) do
|
||||
desc "The disk(s) for this pool. Can be an array or space separated string"
|
||||
end
|
||||
|
||||
newproperty(:mirror, :array_matching => :all) do
|
||||
desc "List of all the devices to mirror for this pool. Each mirror should be a space separated string.
|
||||
mirror => [\"disk1 disk2\", \"disk3 disk4\"]"
|
||||
|
||||
validate do |value|
|
||||
if value.include?(",")
|
||||
raise ArgumentError, "mirror names must be provided as string separated, not a comma-separated list"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:raidz, :array_matching => :all) do
|
||||
desc "List of all the devices to raid for this pool. Should be an array of space separated strings.
|
||||
raidz => [\"disk1 disk2\", \"disk3 disk4\"]"
|
||||
|
||||
validate do |value|
|
||||
if value.include?(",")
|
||||
raise ArgumentError, "raid names must be provided as string separated, not a comma-separated list"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:spare, :array_matching => :all) do
|
||||
desc "Spare disk(s) for this pool."
|
||||
end
|
||||
|
||||
newproperty(:log, :array_matching => :all) do
|
||||
desc "Log disks for this pool. (doesn't support mirroring yet)"
|
||||
end
|
||||
|
||||
newparam(:pool) do
|
||||
desc "The name for this pool."
|
||||
isnamevar
|
||||
end
|
||||
|
||||
newparam(:raid_parity) do
|
||||
desc "Determines parity when using raidz property."
|
||||
end
|
||||
|
||||
validate do
|
||||
has_should = [:disk, :mirror, :raidz].select { |prop| self.should(prop) }
|
||||
if has_should.length > 1
|
||||
self.fail "You cannot specify %s on this type (only one)" % has_should.join(" and ")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -268,7 +268,7 @@ module Util
|
|||
|
||||
# Execute the desired command, and return the status and output.
|
||||
# def execute(command, failonfail = true, uid = nil, gid = nil)
|
||||
def execute(command, arguments = {:failonfail => true})
|
||||
def execute(command, arguments = {:failonfail => true, :combine => true})
|
||||
if command.is_a?(Array)
|
||||
command = command.flatten.collect { |i| i.to_s }
|
||||
str = command.join(" ")
|
||||
|
@ -301,9 +301,13 @@ module Util
|
|||
|
||||
# The idea here is to avoid IO#read whenever possible.
|
||||
output_file="/dev/null"
|
||||
error_file="/dev/null"
|
||||
if ! arguments[:squelch]
|
||||
require "tempfile"
|
||||
output_file = Tempfile.new("puppet")
|
||||
if arguments[:combine]
|
||||
error_file=output_file
|
||||
end
|
||||
end
|
||||
|
||||
oldverb = $VERBOSE
|
||||
|
@ -319,7 +323,8 @@ module Util
|
|||
begin
|
||||
$stdin.reopen("/dev/null")
|
||||
$stdout.reopen(output_file)
|
||||
$stderr.reopen(output_file)
|
||||
$stderr.reopen(error_file)
|
||||
|
||||
3.upto(256){|fd| IO::new(fd).close rescue nil}
|
||||
if arguments[:gid]
|
||||
Process.egid = arguments[:gid]
|
||||
|
|
|
@ -218,10 +218,10 @@ class Puppet::Util::FileType
|
|||
begin
|
||||
output = Puppet::Util.execute(%w{crontab -l}, :uid => @path)
|
||||
return "" if output.include?("can't open your crontab")
|
||||
raise Puppet::Error, "User %s not authorized to use cron" % @path if output.include?("you are not authorized to use cron")
|
||||
return output
|
||||
rescue
|
||||
# If there's a failure, treat it like an empty file.
|
||||
return ""
|
||||
rescue => detail
|
||||
raise Puppet::Error, "Could not read crontab for %s: %s" % [@path, detail]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,11 +4,6 @@ require 'puppet'
|
|||
# A class for handling metrics. This is currently ridiculously hackish.
|
||||
class Puppet::Util::Metric
|
||||
|
||||
# Load the library as a feature, so we can test its presence.
|
||||
# It's only used by this class, so there's no reason to move it
|
||||
# to the main feature list.
|
||||
Puppet.features.add :rrd, :libs => 'RRDtool'
|
||||
|
||||
attr_accessor :type, :name, :value, :label
|
||||
attr_writer :values
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
|
||||
module Puppet::Util::RDoc
|
||||
|
||||
module_function
|
||||
|
||||
# launch a rdoc documenation process
|
||||
# with the files/dir passed in +files+
|
||||
def rdoc(outputdir, files)
|
||||
begin
|
||||
Puppet[:ignoreimport] = true
|
||||
|
||||
# then rdoc
|
||||
require 'rdoc/rdoc'
|
||||
|
||||
# load our parser
|
||||
require 'puppet/util/rdoc/parser'
|
||||
|
||||
r = RDoc::RDoc.new
|
||||
RDoc::RDoc::GENERATORS["puppet"] = RDoc::RDoc::Generator.new("puppet/util/rdoc/generators/puppet_generator.rb",
|
||||
"PuppetGenerator".intern,
|
||||
"puppet")
|
||||
# specify our own format & where to output
|
||||
options = [ "--fmt", "puppet",
|
||||
"--quiet",
|
||||
"--op", outputdir ]
|
||||
|
||||
options += files
|
||||
|
||||
# launch the documentation process
|
||||
r.document(options)
|
||||
rescue RDoc::RDocError => e
|
||||
raise Puppet::ParseError.new("RDoc error %s" % e)
|
||||
end
|
||||
end
|
||||
|
||||
# launch a output to console manifest doc
|
||||
def manifestdoc(files)
|
||||
Puppet[:ignoreimport] = true
|
||||
files.select { |f| FileTest.file?(f) }.each do |f|
|
||||
parser = Puppet::Parser::Parser.new(:environment => Puppet[:environment])
|
||||
parser.file = f
|
||||
ast = parser.parse
|
||||
output(f, ast)
|
||||
end
|
||||
end
|
||||
|
||||
# Ouputs to the console the documentation
|
||||
# of a manifest
|
||||
def output(file, ast)
|
||||
astobj = []
|
||||
ast[:nodes].each do |name, k|
|
||||
astobj << k if k.file == file
|
||||
end
|
||||
ast[:classes].each do |name, k|
|
||||
astobj << k if k.file == file
|
||||
end
|
||||
ast[:definitions].each do |name, k|
|
||||
astobj << k if k.file == file
|
||||
end
|
||||
astobj.sort! {|a,b| a.line <=> b.line }.each do |k|
|
||||
output_astnode_doc(k)
|
||||
end
|
||||
end
|
||||
|
||||
def output_astnode_doc(ast)
|
||||
puts ast.doc if !ast.doc.nil? and !ast.doc.empty?
|
||||
if Puppet.settings[:document_all]
|
||||
# scan each underlying resources to produce documentation
|
||||
code = ast.code.children if ast.code.is_a?(Puppet::Parser::AST::ASTArray)
|
||||
code ||= ast.code
|
||||
output_resource_doc(code) unless code.nil?
|
||||
end
|
||||
end
|
||||
|
||||
def output_resource_doc(code)
|
||||
code.sort { |a,b| a.line <=> b.line }.each do |stmt|
|
||||
output_resource_doc(stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
|
||||
|
||||
if stmt.is_a?(Puppet::Parser::AST::Resource)
|
||||
puts stmt.doc if !stmt.doc.nil? and !stmt.doc.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,219 @@
|
|||
require 'rdoc/code_objects'
|
||||
|
||||
module RDoc
|
||||
|
||||
# This modules contains various class that are used to hold information
|
||||
# about the various Puppet language structures we found while parsing.
|
||||
#
|
||||
# Those will be mapped to their html counterparts which are defined in
|
||||
# PuppetGenerator.
|
||||
|
||||
# PuppetTopLevel is a top level (usually a .pp/.rb file)
|
||||
class PuppetTopLevel < TopLevel
|
||||
attr_accessor :module_name, :global
|
||||
|
||||
# will contain all plugins
|
||||
@@all_plugins = {}
|
||||
|
||||
# contains all cutoms facts
|
||||
@@all_facts = {}
|
||||
|
||||
def initialize(toplevel)
|
||||
super(toplevel.file_relative_name)
|
||||
end
|
||||
|
||||
def self.all_plugins
|
||||
@@all_plugins.values
|
||||
end
|
||||
|
||||
def self.all_facts
|
||||
@@all_facts.values
|
||||
end
|
||||
end
|
||||
|
||||
# PuppetModule holds a Puppet Module
|
||||
# This is mapped to an HTMLPuppetModule
|
||||
# it leverage the RDoc (ruby) module infrastructure
|
||||
class PuppetModule < NormalModule
|
||||
attr_accessor :facts, :plugins
|
||||
|
||||
def initialize(name,superclass=nil)
|
||||
@facts = []
|
||||
@plugins = []
|
||||
super(name,superclass)
|
||||
end
|
||||
|
||||
def initialize_classes_and_modules
|
||||
super
|
||||
@nodes = {}
|
||||
end
|
||||
|
||||
def add_plugin(plugin)
|
||||
add_to(@plugins, plugin)
|
||||
end
|
||||
|
||||
def add_fact(fact)
|
||||
add_to(@facts, fact)
|
||||
end
|
||||
|
||||
def add_node(name,superclass)
|
||||
cls = @nodes[name]
|
||||
unless cls
|
||||
cls = PuppetNode.new(name, superclass)
|
||||
@nodes[name] = cls if !@done_documenting
|
||||
cls.parent = self
|
||||
cls.section = @current_section
|
||||
end
|
||||
cls
|
||||
end
|
||||
|
||||
def each_fact
|
||||
@facts.each {|c| yield c}
|
||||
end
|
||||
|
||||
def each_plugin
|
||||
@plugins.each {|c| yield c}
|
||||
end
|
||||
|
||||
def each_node
|
||||
@nodes.each {|c| yield c}
|
||||
end
|
||||
|
||||
def nodes
|
||||
@nodes.values
|
||||
end
|
||||
end
|
||||
|
||||
# PuppetClass holds a puppet class
|
||||
# It is mapped to a HTMLPuppetClass for display
|
||||
# It leverages RDoc (ruby) Class
|
||||
class PuppetClass < ClassModule
|
||||
attr_accessor :resource_list
|
||||
|
||||
def initialize(name, superclass)
|
||||
super(name,superclass)
|
||||
@resource_list = []
|
||||
end
|
||||
|
||||
def add_resource(resource)
|
||||
add_to(@resource_list, resource)
|
||||
end
|
||||
|
||||
def is_module?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# PuppetNode holds a puppet node
|
||||
# It is mapped to a HTMLPuppetNode for display
|
||||
# A node is just a variation of a class
|
||||
class PuppetNode < PuppetClass
|
||||
def initialize(name, superclass)
|
||||
super(name,superclass)
|
||||
end
|
||||
|
||||
def is_module?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Plugin holds a native puppet plugin (function,type...)
|
||||
# It is mapped to a HTMLPuppetPlugin for display
|
||||
class Plugin < Context
|
||||
attr_accessor :name, :type
|
||||
|
||||
def initialize(name, type)
|
||||
super()
|
||||
@name = name
|
||||
@type = type
|
||||
@comment = ""
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
@name <=> other.name
|
||||
end
|
||||
|
||||
def full_name
|
||||
@name
|
||||
end
|
||||
|
||||
def http_url(prefix)
|
||||
path = full_name.split("::")
|
||||
File.join(prefix, *path) + ".html"
|
||||
end
|
||||
|
||||
def is_fact?
|
||||
false
|
||||
end
|
||||
|
||||
def to_s
|
||||
res = self.class.name + ": " + @name + " (" + @type + ")\n"
|
||||
res << @comment.to_s
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
# Fact holds a custom fact
|
||||
# It is mapped to a HTMLPuppetPlugin for display
|
||||
class Fact < Context
|
||||
attr_accessor :name, :confine
|
||||
|
||||
def initialize(name, confine)
|
||||
super()
|
||||
@name = name
|
||||
@confine = confine
|
||||
@comment = ""
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
@name <=> other.name
|
||||
end
|
||||
|
||||
def is_fact?
|
||||
true
|
||||
end
|
||||
|
||||
def full_name
|
||||
@name
|
||||
end
|
||||
|
||||
def to_s
|
||||
res = self.class.name + ": " + @name + "\n"
|
||||
res << @comment.to_s
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
# PuppetResource holds a puppet resource
|
||||
# It is mapped to a HTMLPuppetResource for display
|
||||
# A resource is defined by its "normal" form Type[title]
|
||||
class PuppetResource < CodeObject
|
||||
attr_accessor :type, :title, :params
|
||||
|
||||
def initialize(type, title, comment, params)
|
||||
super()
|
||||
@type = type
|
||||
@title = title
|
||||
@comment = comment
|
||||
@params = params
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
full_name <=> other.full_name
|
||||
end
|
||||
|
||||
def full_name
|
||||
@type + "[" + @title + "]"
|
||||
end
|
||||
|
||||
def name
|
||||
full_name
|
||||
end
|
||||
|
||||
def to_s
|
||||
res = @type + "[" + @title + "]\n"
|
||||
res << @comment.to_s
|
||||
res
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,829 @@
|
|||
require 'rdoc/generators/html_generator'
|
||||
require 'puppet/util/rdoc/code_objects'
|
||||
module Generators
|
||||
|
||||
# This module holds all the classes needed to generate the HTML documentation
|
||||
# of a bunch of puppet manifests.
|
||||
#
|
||||
# It works by traversing all the code objects defined by the Puppet RDoc::Parser
|
||||
# and produces HTML counterparts objects that in turns are used by RDoc template engine
|
||||
# to produce the final HTML.
|
||||
#
|
||||
# It is also responsible of creating the whole directory hierarchy, and various index
|
||||
# files.
|
||||
#
|
||||
# It is to be noted that the whole system is built on top of ruby RDoc. As such there
|
||||
# is an implicit mapping of puppet entities to ruby entitites:
|
||||
#
|
||||
# Puppet => Ruby
|
||||
# ------------------------
|
||||
# Module Module
|
||||
# Class Class
|
||||
# Definition Method
|
||||
# Resource
|
||||
# Node
|
||||
# Plugin
|
||||
# Fact
|
||||
|
||||
MODULE_DIR = "modules"
|
||||
NODE_DIR = "nodes"
|
||||
PLUGIN_DIR = "plugins"
|
||||
|
||||
# This is a specialized HTMLGenerator tailored to Puppet manifests
|
||||
class PuppetGenerator < HTMLGenerator
|
||||
|
||||
def PuppetGenerator.for(options)
|
||||
AllReferences::reset
|
||||
HtmlMethod::reset
|
||||
|
||||
if options.all_one_file
|
||||
PuppetGeneratorInOne.new(options)
|
||||
else
|
||||
PuppetGenerator.new(options)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(options) #:not-new:
|
||||
@options = options
|
||||
load_html_template
|
||||
end
|
||||
|
||||
# loads our own html template file
|
||||
def load_html_template
|
||||
begin
|
||||
require 'puppet/util/rdoc/generators/template/puppet/puppet'
|
||||
extend RDoc::Page
|
||||
rescue LoadError
|
||||
$stderr.puts "Could not find Puppet template '#{template}'"
|
||||
exit 99
|
||||
end
|
||||
end
|
||||
|
||||
def gen_method_index
|
||||
# we don't generate an all define index
|
||||
# as the presentation is per module/per class
|
||||
end
|
||||
|
||||
# This is the central method, it generates the whole structures
|
||||
# along with all the indices.
|
||||
def generate_html
|
||||
super
|
||||
gen_into(@nodes)
|
||||
gen_into(@plugins)
|
||||
end
|
||||
|
||||
##
|
||||
# Generate:
|
||||
# the list of modules
|
||||
# the list of classes and definitions of a specific module
|
||||
# the list of all classes
|
||||
# the list of nodes
|
||||
# the list of resources
|
||||
def build_indices
|
||||
@allfiles = []
|
||||
@nodes = []
|
||||
@plugins = []
|
||||
|
||||
# contains all the seen modules
|
||||
@modules = {}
|
||||
@allclasses = {}
|
||||
|
||||
# build the modules, classes and per modules classes and define list
|
||||
@toplevels.each do |toplevel|
|
||||
next unless toplevel.document_self
|
||||
file = HtmlFile.new(toplevel, @options, FILE_DIR)
|
||||
classes = []
|
||||
methods = []
|
||||
modules = []
|
||||
nodes = []
|
||||
|
||||
# find all classes of this toplevel
|
||||
# store modules if we find one
|
||||
toplevel.each_classmodule do |k|
|
||||
generate_class_list(classes, modules, k, toplevel, CLASS_DIR)
|
||||
end
|
||||
|
||||
# find all defines belonging to this toplevel
|
||||
HtmlMethod.all_methods.each do |m|
|
||||
# find parent module, check this method is not already
|
||||
# defined.
|
||||
if m.context.parent.toplevel === toplevel
|
||||
methods << m
|
||||
end
|
||||
end
|
||||
|
||||
classes.each do |k|
|
||||
@allclasses[k.index_name] = k if !@allclasses.has_key?(k.index_name)
|
||||
end
|
||||
|
||||
# generate nodes and plugins found
|
||||
classes.each do |k|
|
||||
if k.context.is_module?
|
||||
k.context.each_node do |name,node|
|
||||
nodes << HTMLPuppetNode.new(node, toplevel, NODE_DIR, @options)
|
||||
@nodes << nodes.last
|
||||
end
|
||||
k.context.each_plugin do |plugin|
|
||||
@plugins << HTMLPuppetPlugin.new(plugin, toplevel, PLUGIN_DIR, @options)
|
||||
end
|
||||
k.context.each_fact do |fact|
|
||||
@plugins << HTMLPuppetPlugin.new(fact, toplevel, PLUGIN_DIR, @options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@files << file
|
||||
@allfiles << { "file" => file, "modules" => modules, "classes" => classes, "methods" => methods, "nodes" => nodes }
|
||||
end
|
||||
|
||||
@classes = @allclasses.values
|
||||
end
|
||||
|
||||
# produce a class/module list of HTMLPuppetModule/HTMLPuppetClass
|
||||
# based on the code object traversal.
|
||||
def generate_class_list(classes, modules, from, html_file, class_dir)
|
||||
if from.is_module? and !@modules.has_key?(from.name)
|
||||
k = HTMLPuppetModule.new(from, html_file, class_dir, @options)
|
||||
classes << k
|
||||
@modules[from.name] = k
|
||||
modules << @modules[from.name]
|
||||
elsif from.is_module?
|
||||
modules << @modules[from.name]
|
||||
elsif !from.is_module?
|
||||
k = HTMLPuppetClass.new(from, html_file, class_dir, @options)
|
||||
classes << k
|
||||
end
|
||||
from.each_classmodule do |mod|
|
||||
generate_class_list(classes, modules, mod, html_file, class_dir)
|
||||
end
|
||||
end
|
||||
|
||||
# generate all the subdirectories, modules, classes and files
|
||||
def gen_sub_directories
|
||||
begin
|
||||
super
|
||||
File.makedirs(MODULE_DIR)
|
||||
File.makedirs(NODE_DIR)
|
||||
File.makedirs(PLUGIN_DIR)
|
||||
rescue
|
||||
$stderr.puts $!.message
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
# generate the index of modules
|
||||
def gen_file_index
|
||||
gen_top_index(@modules.values, 'All Modules', RDoc::Page::TOP_INDEX, "fr_modules_index.html")
|
||||
end
|
||||
|
||||
# generate a top index
|
||||
def gen_top_index(collection, title, template, filename)
|
||||
template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template)
|
||||
res = []
|
||||
collection.sort.each do |f|
|
||||
if f.document_self
|
||||
res << { "classlist" => CGI.escapeHTML("#{MODULE_DIR}/fr_#{f.index_name}.html"), "module" => CGI.escapeHTML("#{CLASS_DIR}/#{f.index_name}.html"),"name" => CGI.escapeHTML(f.index_name) }
|
||||
end
|
||||
end
|
||||
|
||||
values = {
|
||||
"entries" => res,
|
||||
'list_title' => CGI.escapeHTML(title),
|
||||
'index_url' => main_url,
|
||||
'charset' => @options.charset,
|
||||
'style_url' => style_url('', @options.css),
|
||||
}
|
||||
|
||||
File.open(filename, "w") do |f|
|
||||
template.write_html_on(f, values)
|
||||
end
|
||||
end
|
||||
|
||||
# generate the all classes index file and the combo index
|
||||
def gen_class_index
|
||||
gen_an_index(@classes, 'All Classes', RDoc::Page::CLASS_INDEX, "fr_class_index.html")
|
||||
@allfiles.each do |file|
|
||||
unless file['file'].context.file_relative_name =~ /\.rb$/
|
||||
gen_composite_index(file,
|
||||
RDoc::Page::COMBO_INDEX,
|
||||
"#{MODULE_DIR}/fr_#{file["file"].context.module_name}.html")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def gen_composite_index(collection, template, filename)\
|
||||
return if FileTest.exists?(filename)
|
||||
|
||||
template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template)
|
||||
res1 = []
|
||||
collection['classes'].sort.each do |f|
|
||||
if f.document_self
|
||||
unless f.context.is_module?
|
||||
res1 << { "href" => "../"+CGI.escapeHTML(f.path), "name" => CGI.escapeHTML(f.index_name) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
res2 = []
|
||||
collection['methods'].sort.each do |f|
|
||||
if f.document_self
|
||||
res2 << { "href" => "../"+f.path, "name" => f.index_name.sub(/\(.*\)$/,'') }
|
||||
end
|
||||
end
|
||||
|
||||
module_name = []
|
||||
res3 = []
|
||||
res4 = []
|
||||
collection['modules'].sort.each do |f|
|
||||
module_name << { "href" => "../"+CGI.escapeHTML(f.path), "name" => CGI.escapeHTML(f.index_name) }
|
||||
unless f.facts.nil?
|
||||
f.facts.each do |fact|
|
||||
res3 << {"href" => "../"+CGI.escapeHTML(AllReferences["PLUGIN(#{fact.name})"].path), "name" => CGI.escapeHTML(fact.name)}
|
||||
end
|
||||
end
|
||||
unless f.plugins.nil?
|
||||
f.plugins.each do |plugin|
|
||||
res4 << {"href" => "../"+CGI.escapeHTML(AllReferences["PLUGIN(#{plugin.name})"].path), "name" => CGI.escapeHTML(plugin.name)}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
res5 = []
|
||||
collection['nodes'].sort.each do |f|
|
||||
if f.document_self
|
||||
res5 << { "href" => "../"+CGI.escapeHTML(f.path), "name" => CGI.escapeHTML(f.name) }
|
||||
end
|
||||
end
|
||||
|
||||
values = {
|
||||
"module" => module_name,
|
||||
"classes" => res1,
|
||||
'classes_title' => CGI.escapeHTML("Classes"),
|
||||
'defines_title' => CGI.escapeHTML("Defines"),
|
||||
'facts_title' => CGI.escapeHTML("Custom Facts"),
|
||||
'plugins_title' => CGI.escapeHTML("Plugins"),
|
||||
'nodes_title' => CGI.escapeHTML("Nodes"),
|
||||
'index_url' => main_url,
|
||||
'charset' => @options.charset,
|
||||
'style_url' => style_url('', @options.css),
|
||||
}
|
||||
|
||||
values["defines"] = res2 if res2.size>0
|
||||
values["facts"] = res3 if res3.size>0
|
||||
values["plugins"] = res4 if res4.size>0
|
||||
values["nodes"] = res5 if res5.size>0
|
||||
|
||||
File.open(filename, "w") do |f|
|
||||
template.write_html_on(f, values)
|
||||
end
|
||||
end
|
||||
|
||||
# returns the initial_page url
|
||||
def main_url
|
||||
main_page = @options.main_page
|
||||
ref = nil
|
||||
if main_page
|
||||
ref = AllReferences[main_page]
|
||||
if ref
|
||||
ref = ref.path
|
||||
else
|
||||
$stderr.puts "Could not find main page #{main_page}"
|
||||
end
|
||||
end
|
||||
|
||||
unless ref
|
||||
for file in @files
|
||||
if file.document_self and file.context.global
|
||||
ref = CGI.escapeHTML("#{CLASS_DIR}/#{file.context.module_name}.html")
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless ref
|
||||
for file in @files
|
||||
if file.document_self and !file.context.global
|
||||
ref = CGI.escapeHTML("#{CLASS_DIR}/#{file.context.module_name}.html")
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless ref
|
||||
$stderr.puts "Couldn't find anything to document"
|
||||
$stderr.puts "Perhaps you've used :stopdoc: in all classes"
|
||||
exit(1)
|
||||
end
|
||||
|
||||
ref
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# This module is used to hold/generate a list of puppet resources
|
||||
# this is used in HTMLPuppetClass and HTMLPuppetNode
|
||||
module ResourceContainer
|
||||
def collect_resources
|
||||
list = @context.resource_list
|
||||
@resources = list.collect {|m| HTMLPuppetResource.new(m, self, @options) }
|
||||
end
|
||||
|
||||
def build_resource_summary_list(path_prefix='')
|
||||
collect_resources unless @resources
|
||||
resources = @resources.sort
|
||||
res = []
|
||||
resources.each do |r|
|
||||
res << {
|
||||
"name" => CGI.escapeHTML(r.name),
|
||||
"aref" => "#{path_prefix}\##{r.aref}"
|
||||
}
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
def build_resource_detail_list(section)
|
||||
outer = []
|
||||
resources = @resources.sort
|
||||
resources.each do |r|
|
||||
row = {}
|
||||
if r.section == section and r.document_self
|
||||
row["name"] = CGI.escapeHTML(r.name)
|
||||
desc = r.description.strip
|
||||
row["m_desc"] = desc unless desc.empty?
|
||||
row["aref"] = r.aref
|
||||
row["params"] = r.params
|
||||
outer << row
|
||||
end
|
||||
end
|
||||
outer
|
||||
end
|
||||
end
|
||||
|
||||
class HTMLPuppetClass < HtmlClass
|
||||
include ResourceContainer
|
||||
|
||||
def value_hash
|
||||
super
|
||||
rl = build_resource_summary_list
|
||||
@values["resources"] = rl unless rl.empty?
|
||||
|
||||
@context.sections.each do |section|
|
||||
secdata = @values["sections"].select { |secdata| secdata["secsequence"] == section.sequence }
|
||||
if secdata.size == 1
|
||||
secdata = secdata[0]
|
||||
|
||||
rdl = build_resource_detail_list(section)
|
||||
secdata["resource_list"] = rdl unless rdl.empty?
|
||||
end
|
||||
end
|
||||
@values
|
||||
end
|
||||
end
|
||||
|
||||
class HTMLPuppetNode < ContextUser
|
||||
include ResourceContainer
|
||||
|
||||
attr_reader :path
|
||||
|
||||
def initialize(context, html_file, prefix, options)
|
||||
super(context, options)
|
||||
|
||||
@html_file = html_file
|
||||
@is_module = context.is_module?
|
||||
@values = {}
|
||||
|
||||
context.viewer = self
|
||||
|
||||
if options.all_one_file
|
||||
@path = context.full_name
|
||||
else
|
||||
@path = http_url(context.full_name, prefix)
|
||||
end
|
||||
|
||||
AllReferences.add("NODE(#{@context.full_name})", self)
|
||||
end
|
||||
|
||||
def name
|
||||
@context.name
|
||||
end
|
||||
|
||||
# return the relative file name to store this class in,
|
||||
# which is also its url
|
||||
def http_url(full_name, prefix)
|
||||
path = full_name.dup
|
||||
if path['<<']
|
||||
path.gsub!(/<<\s*(\w*)/) { "from-#$1" }
|
||||
end
|
||||
File.join(prefix, path.split("::")) + ".html"
|
||||
end
|
||||
|
||||
def parent_name
|
||||
@context.parent.full_name
|
||||
end
|
||||
|
||||
def index_name
|
||||
name
|
||||
end
|
||||
|
||||
def write_on(f)
|
||||
value_hash
|
||||
template = TemplatePage.new(RDoc::Page::BODYINC,
|
||||
RDoc::Page::NODE_PAGE,
|
||||
RDoc::Page::METHOD_LIST)
|
||||
template.write_html_on(f, @values)
|
||||
end
|
||||
|
||||
def value_hash
|
||||
class_attribute_values
|
||||
add_table_of_sections
|
||||
|
||||
@values["charset"] = @options.charset
|
||||
@values["style_url"] = style_url(path, @options.css)
|
||||
|
||||
d = markup(@context.comment)
|
||||
@values["description"] = d unless d.empty?
|
||||
|
||||
ml = build_method_summary_list
|
||||
@values["methods"] = ml unless ml.empty?
|
||||
|
||||
rl = build_resource_summary_list
|
||||
@values["resources"] = rl unless rl.empty?
|
||||
|
||||
il = build_include_list(@context)
|
||||
@values["includes"] = il unless il.empty?
|
||||
|
||||
@values["sections"] = @context.sections.map do |section|
|
||||
|
||||
secdata = {
|
||||
"sectitle" => section.title,
|
||||
"secsequence" => section.sequence,
|
||||
"seccomment" => markup(section.comment)
|
||||
}
|
||||
|
||||
al = build_alias_summary_list(section)
|
||||
secdata["aliases"] = al unless al.empty?
|
||||
|
||||
co = build_constants_summary_list(section)
|
||||
secdata["constants"] = co unless co.empty?
|
||||
|
||||
al = build_attribute_list(section)
|
||||
secdata["attributes"] = al unless al.empty?
|
||||
|
||||
cl = build_class_list(0, @context, section)
|
||||
secdata["classlist"] = cl unless cl.empty?
|
||||
|
||||
mdl = build_method_detail_list(section)
|
||||
secdata["method_list"] = mdl unless mdl.empty?
|
||||
|
||||
rdl = build_resource_detail_list(section)
|
||||
secdata["resource_list"] = rdl unless rdl.empty?
|
||||
|
||||
secdata
|
||||
end
|
||||
|
||||
@values
|
||||
end
|
||||
|
||||
def build_attribute_list(section)
|
||||
atts = @context.attributes.sort
|
||||
res = []
|
||||
atts.each do |att|
|
||||
next unless att.section == section
|
||||
if att.visibility == :public || att.visibility == :protected || @options.show_all
|
||||
entry = {
|
||||
"name" => CGI.escapeHTML(att.name),
|
||||
"rw" => att.rw,
|
||||
"a_desc" => markup(att.comment, true)
|
||||
}
|
||||
unless att.visibility == :public || att.visibility == :protected
|
||||
entry["rw"] << "-"
|
||||
end
|
||||
res << entry
|
||||
end
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
def class_attribute_values
|
||||
h_name = CGI.escapeHTML(name)
|
||||
|
||||
@values["classmod"] = "Node"
|
||||
@values["title"] = "#{@values['classmod']}: #{h_name}"
|
||||
|
||||
c = @context
|
||||
c = c.parent while c and !c.diagram
|
||||
|
||||
if c && c.diagram
|
||||
@values["diagram"] = diagram_reference(c.diagram)
|
||||
end
|
||||
|
||||
@values["full_name"] = h_name
|
||||
|
||||
parent_class = @context.superclass
|
||||
|
||||
if parent_class
|
||||
@values["parent"] = CGI.escapeHTML(parent_class)
|
||||
|
||||
if parent_name
|
||||
lookup = parent_name + "::" + parent_class
|
||||
else
|
||||
lookup = parent_class
|
||||
end
|
||||
lookup = "NODE(#{lookup})"
|
||||
parent_url = AllReferences[lookup] || AllReferences[parent_class]
|
||||
if parent_url and parent_url.document_self
|
||||
@values["par_url"] = aref_to(parent_url.path)
|
||||
end
|
||||
end
|
||||
|
||||
files = []
|
||||
@context.in_files.each do |f|
|
||||
res = {}
|
||||
full_path = CGI.escapeHTML(f.file_absolute_name)
|
||||
|
||||
res["full_path"] = full_path
|
||||
res["full_path_url"] = aref_to(f.viewer.path) if f.document_self
|
||||
|
||||
if @options.webcvs
|
||||
res["cvsurl"] = cvs_url( @options.webcvs, full_path )
|
||||
end
|
||||
|
||||
files << res
|
||||
end
|
||||
|
||||
@values['infiles'] = files
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
self.name <=> other.name
|
||||
end
|
||||
end
|
||||
|
||||
class HTMLPuppetModule < HtmlClass
|
||||
|
||||
def initialize(context, html_file, prefix, options)
|
||||
super(context, html_file, prefix, options)
|
||||
end
|
||||
|
||||
def value_hash
|
||||
@values = super
|
||||
|
||||
fl = build_facts_summary_list
|
||||
@values["facts"] = fl unless fl.empty?
|
||||
|
||||
pl = build_plugins_summary_list
|
||||
@values["plugins"] = pl unless pl.empty?
|
||||
|
||||
nl = build_nodes_list(0, @context)
|
||||
@values["nodelist"] = nl unless nl.empty?
|
||||
|
||||
@values
|
||||
end
|
||||
|
||||
def build_nodes_list(level, context)
|
||||
res = ""
|
||||
prefix = " ::" * level;
|
||||
|
||||
context.nodes.sort.each do |node|
|
||||
if node.document_self
|
||||
res <<
|
||||
prefix <<
|
||||
"Node " <<
|
||||
href(url(node.viewer.path), "link", node.full_name) <<
|
||||
"<br />\n"
|
||||
end
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
def build_facts_summary_list
|
||||
potentially_referenced_list(context.facts) {|fn| ["PLUGIN(#{fn})"] }
|
||||
end
|
||||
|
||||
def build_plugins_summary_list
|
||||
potentially_referenced_list(context.plugins) {|fn| ["PLUGIN(#{fn})"] }
|
||||
end
|
||||
|
||||
def facts
|
||||
@context.facts
|
||||
end
|
||||
|
||||
def plugins
|
||||
@context.plugins
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class HTMLPuppetPlugin < ContextUser
|
||||
attr_reader :path
|
||||
|
||||
def initialize(context, html_file, prefix, options)
|
||||
super(context, options)
|
||||
|
||||
@html_file = html_file
|
||||
@is_module = false
|
||||
@values = {}
|
||||
|
||||
context.viewer = self
|
||||
|
||||
if options.all_one_file
|
||||
@path = context.full_name
|
||||
else
|
||||
@path = http_url(context.full_name, prefix)
|
||||
end
|
||||
|
||||
AllReferences.add("PLUGIN(#{@context.full_name})", self)
|
||||
end
|
||||
|
||||
def name
|
||||
@context.name
|
||||
end
|
||||
|
||||
# return the relative file name to store this class in,
|
||||
# which is also its url
|
||||
def http_url(full_name, prefix)
|
||||
path = full_name.dup
|
||||
if path['<<']
|
||||
path.gsub!(/<<\s*(\w*)/) { "from-#$1" }
|
||||
end
|
||||
File.join(prefix, path.split("::")) + ".html"
|
||||
end
|
||||
|
||||
def parent_name
|
||||
@context.parent.full_name
|
||||
end
|
||||
|
||||
def index_name
|
||||
name
|
||||
end
|
||||
|
||||
def write_on(f)
|
||||
value_hash
|
||||
template = TemplatePage.new(RDoc::Page::BODYINC,
|
||||
RDoc::Page::PLUGIN_PAGE,
|
||||
RDoc::Page::PLUGIN_LIST)
|
||||
template.write_html_on(f, @values)
|
||||
end
|
||||
|
||||
def value_hash
|
||||
attribute_values
|
||||
add_table_of_sections
|
||||
|
||||
@values["charset"] = @options.charset
|
||||
@values["style_url"] = style_url(path, @options.css)
|
||||
|
||||
d = markup(@context.comment)
|
||||
@values["description"] = d unless d.empty?
|
||||
|
||||
if context.is_fact?
|
||||
unless context.confine.empty?
|
||||
res = {}
|
||||
res["type"] = context.confine[:type]
|
||||
res["value"] = context.confine[:value]
|
||||
@values["confine"] = [res]
|
||||
end
|
||||
else
|
||||
@values["type"] = context.type
|
||||
end
|
||||
|
||||
@values["sections"] = @context.sections.map do |section|
|
||||
secdata = {
|
||||
"sectitle" => section.title,
|
||||
"secsequence" => section.sequence,
|
||||
"seccomment" => markup(section.comment)
|
||||
}
|
||||
secdata
|
||||
end
|
||||
|
||||
@values
|
||||
end
|
||||
|
||||
def attribute_values
|
||||
h_name = CGI.escapeHTML(name)
|
||||
|
||||
if @context.is_fact?
|
||||
@values["classmod"] = "Fact"
|
||||
else
|
||||
@values["classmod"] = "Plugin"
|
||||
end
|
||||
@values["title"] = "#{@values['classmod']}: #{h_name}"
|
||||
|
||||
c = @context
|
||||
@values["full_name"] = h_name
|
||||
|
||||
files = []
|
||||
@context.in_files.each do |f|
|
||||
res = {}
|
||||
full_path = CGI.escapeHTML(f.file_absolute_name)
|
||||
|
||||
res["full_path"] = full_path
|
||||
res["full_path_url"] = aref_to(f.viewer.path) if f.document_self
|
||||
|
||||
if @options.webcvs
|
||||
res["cvsurl"] = cvs_url( @options.webcvs, full_path )
|
||||
end
|
||||
|
||||
files << res
|
||||
end
|
||||
|
||||
@values['infiles'] = files
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
self.name <=> other.name
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class HTMLPuppetResource
|
||||
include MarkUp
|
||||
|
||||
attr_reader :context
|
||||
|
||||
@@seq = "R000000"
|
||||
|
||||
def initialize(context, html_class, options)
|
||||
@context = context
|
||||
@html_class = html_class
|
||||
@options = options
|
||||
@@seq = @@seq.succ
|
||||
@seq = @@seq
|
||||
|
||||
context.viewer = self
|
||||
|
||||
AllReferences.add(name, self)
|
||||
end
|
||||
|
||||
def as_href(from_path)
|
||||
if @options.all_one_file
|
||||
"#" + path
|
||||
else
|
||||
HTMLGenerator.gen_url(from_path, path)
|
||||
end
|
||||
end
|
||||
|
||||
def name
|
||||
@context.name
|
||||
end
|
||||
|
||||
def section
|
||||
@context.section
|
||||
end
|
||||
|
||||
def index_name
|
||||
"#{@context.name}"
|
||||
end
|
||||
|
||||
def params
|
||||
@context.params
|
||||
end
|
||||
|
||||
def parent_name
|
||||
if @context.parent.parent
|
||||
@context.parent.parent.full_name
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def aref
|
||||
@seq
|
||||
end
|
||||
|
||||
def path
|
||||
if @options.all_one_file
|
||||
aref
|
||||
else
|
||||
@html_class.path + "#" + aref
|
||||
end
|
||||
end
|
||||
|
||||
def description
|
||||
markup(@context.comment)
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
@context <=> other.context
|
||||
end
|
||||
|
||||
def document_self
|
||||
@context.document_self
|
||||
end
|
||||
|
||||
def find_symbol(symbol, method=nil)
|
||||
res = @context.parent.find_symbol(symbol, method)
|
||||
if res
|
||||
res = res.viewer
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class PuppetGeneratorInOne < HTMLGeneratorInOne
|
||||
def gen_method_index
|
||||
gen_an_index(HtmlMethod.all_methods, 'Defines')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,437 @@
|
|||
# Puppet "parser" for the rdoc system
|
||||
# The parser uses puppet parser and traverse the AST to instruct RDoc about
|
||||
# our current structures. It also parses ruby files that could contain
|
||||
# either custom facts or puppet plugins (functions, types...)
|
||||
|
||||
# rdoc mandatory includes
|
||||
require "rdoc/code_objects"
|
||||
require "puppet/util/rdoc/code_objects"
|
||||
require "rdoc/tokenstream"
|
||||
require "rdoc/markup/simple_markup/preprocess"
|
||||
require "rdoc/parsers/parserfactory"
|
||||
|
||||
module RDoc
|
||||
|
||||
class Parser
|
||||
extend ParserFactory
|
||||
|
||||
# parser registration into RDoc
|
||||
parse_files_matching(/\.(rb|pp)$/)
|
||||
|
||||
# called with the top level file
|
||||
def initialize(top_level, file_name, content, options, stats)
|
||||
@options = options
|
||||
@stats = stats
|
||||
@input_file_name = file_name
|
||||
@top_level = PuppetTopLevel.new(top_level)
|
||||
@progress = $stderr unless options.quiet
|
||||
end
|
||||
|
||||
# main entry point
|
||||
def scan
|
||||
Puppet.info "rdoc: scanning %s" % @input_file_name
|
||||
if @input_file_name =~ /\.pp$/
|
||||
@parser = Puppet::Parser::Parser.new(:environment => Puppet[:environment])
|
||||
@parser.file = @input_file_name
|
||||
@ast = @parser.parse
|
||||
end
|
||||
scan_top_level(@top_level)
|
||||
@top_level
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# walk down the namespace and lookup/create container as needed
|
||||
def get_class_or_module(container, name)
|
||||
|
||||
# class ::A -> A is in the top level
|
||||
if name =~ /^::/
|
||||
container = @top_level
|
||||
end
|
||||
|
||||
names = name.split('::')
|
||||
|
||||
final_name = names.pop
|
||||
names.each do |name|
|
||||
prev_container = container
|
||||
container = container.find_module_named(name)
|
||||
unless container
|
||||
container = prev_container.add_module(PuppetClass, name)
|
||||
end
|
||||
end
|
||||
return [container, final_name]
|
||||
end
|
||||
|
||||
# split_module tries to find if +path+ belongs to the module path
|
||||
# if it does, it returns the module name, otherwise if we are sure
|
||||
# it is part of the global manifest path, "<site>" is returned.
|
||||
# And finally if this path couldn't be mapped anywhere, nil is returned.
|
||||
def split_module(path)
|
||||
# find a module
|
||||
fullpath = File.expand_path(path)
|
||||
Puppet.debug "rdoc: testing %s" % fullpath
|
||||
if fullpath =~ /(.*)\/([^\/]+)\/(?:manifests|plugins)\/.+\.(pp|rb)$/
|
||||
modpath = $1
|
||||
name = $2
|
||||
Puppet.debug "rdoc: module %s into %s ?" % [name, modpath]
|
||||
Puppet::Module.modulepath().each do |mp|
|
||||
if File.identical?(modpath,mp)
|
||||
Puppet.debug "rdoc: found module %s" % name
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
if fullpath =~ /\.(pp|rb)$/
|
||||
# there can be paths we don't want to scan under modules
|
||||
# imagine a ruby or manifest that would be distributed as part as a module
|
||||
# but we don't want those to be hosted under <site>
|
||||
Puppet::Module.modulepath().each do |mp|
|
||||
# check that fullpath is a descendant of mp
|
||||
dirname = fullpath
|
||||
while (dirname = File.dirname(dirname)) != '/'
|
||||
return nil if File.identical?(dirname,mp)
|
||||
end
|
||||
end
|
||||
end
|
||||
# we are under a global manifests
|
||||
Puppet.debug "rdoc: global manifests"
|
||||
return "<site>"
|
||||
end
|
||||
|
||||
# create documentation for the top level +container+
|
||||
def scan_top_level(container)
|
||||
# use the module README as documentation for the module
|
||||
comment = ""
|
||||
readme = File.join(File.dirname(File.dirname(@input_file_name)), "README")
|
||||
comment = File.open(readme,"r") { |f| f.read } if FileTest.readable?(readme)
|
||||
look_for_directives_in(container, comment) unless comment.empty?
|
||||
|
||||
# infer module name from directory
|
||||
name = split_module(@input_file_name)
|
||||
if name.nil?
|
||||
# skip .pp files that are not in manifests directories as we can't guarantee they're part
|
||||
# of a module or the global configuration.
|
||||
container.document_self = false
|
||||
return
|
||||
end
|
||||
|
||||
Puppet.debug "rdoc: scanning for %s" % name
|
||||
|
||||
container.module_name = name
|
||||
container.global=true if name == "<site>"
|
||||
|
||||
@stats.num_modules += 1
|
||||
container, name = get_class_or_module(container,name)
|
||||
mod = container.add_module(PuppetModule, name)
|
||||
mod.record_location(@top_level)
|
||||
mod.comment = comment
|
||||
|
||||
if @input_file_name =~ /\.pp$/
|
||||
parse_elements(mod)
|
||||
elsif @input_file_name =~ /\.rb$/
|
||||
parse_plugins(mod)
|
||||
end
|
||||
end
|
||||
|
||||
# create documentation for include statements we can find in +code+
|
||||
# and associate it with +container+
|
||||
def scan_for_include(container, code)
|
||||
code.each do |stmt|
|
||||
scan_for_include(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
|
||||
|
||||
if stmt.is_a?(Puppet::Parser::AST::Function) and stmt.name == "include"
|
||||
stmt.arguments.each do |included|
|
||||
Puppet.debug "found include: %s" % included.value
|
||||
container.add_include(Include.new(included.value, stmt.doc))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# create documentation for global variables assignements we can find in +code+
|
||||
# and associate it with +container+
|
||||
def scan_for_vardef(container, code)
|
||||
code.each do |stmt|
|
||||
scan_for_vardef(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
|
||||
|
||||
if stmt.is_a?(Puppet::Parser::AST::VarDef)
|
||||
Puppet.debug "rdoc: found constant: %s = %s" % [stmt.name.to_s, value_to_s(stmt.value)]
|
||||
container.add_constant(Constant.new(stmt.name.to_s, value_to_s(stmt.value), stmt.doc))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# create documentation for resources we can find in +code+
|
||||
# and associate it with +container+
|
||||
def scan_for_resource(container, code)
|
||||
code.each do |stmt|
|
||||
scan_for_resource(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
|
||||
|
||||
if stmt.is_a?(Puppet::Parser::AST::Resource)
|
||||
type = stmt.type.split("::").collect { |s| s.capitalize }.join("::")
|
||||
title = value_to_s(stmt.title)
|
||||
Puppet.debug "rdoc: found resource: %s[%s]" % [type,title]
|
||||
|
||||
param = []
|
||||
stmt.params.children.each do |p|
|
||||
res = {}
|
||||
res["name"] = p.param
|
||||
if !p.value.nil?
|
||||
if !p.value.is_a?(Puppet::Parser::AST::ASTArray)
|
||||
res["value"] = "'#{p.value}'"
|
||||
else
|
||||
res["value"] = "[%s]" % p.value.children.collect { |v| "'#{v}'" }.join(", ")
|
||||
end
|
||||
end
|
||||
param << res
|
||||
end
|
||||
|
||||
container.add_resource(PuppetResource.new(type, title, stmt.doc, param))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# create documentation for a class named +name+
|
||||
def document_class(name, klass, container)
|
||||
Puppet.debug "rdoc: found new class %s" % name
|
||||
container, name = get_class_or_module(container, name)
|
||||
|
||||
superclass = klass.parentclass
|
||||
superclass = "" if superclass.nil? or superclass.empty?
|
||||
|
||||
@stats.num_classes += 1
|
||||
comment = klass.doc
|
||||
look_for_directives_in(container, comment) unless comment.empty?
|
||||
cls = container.add_class(PuppetClass, name, superclass)
|
||||
cls.record_location(@top_level)
|
||||
|
||||
# scan class code for include
|
||||
code = klass.code.children if klass.code.is_a?(Puppet::Parser::AST::ASTArray)
|
||||
code ||= klass.code
|
||||
unless code.nil?
|
||||
scan_for_include(cls, code)
|
||||
scan_for_resource(cls, code) if Puppet.settings[:document_all]
|
||||
end
|
||||
|
||||
cls.comment = comment
|
||||
end
|
||||
|
||||
# create documentation for a node
|
||||
def document_node(name, node, container)
|
||||
Puppet.debug "rdoc: found new node %s" % name
|
||||
superclass = node.parentclass
|
||||
superclass = "" if superclass.nil? or superclass.empty?
|
||||
|
||||
comment = node.doc
|
||||
look_for_directives_in(container, comment) unless comment.empty?
|
||||
n = container.add_node(name, superclass)
|
||||
n.record_location(@top_level)
|
||||
|
||||
code = node.code.children if node.code.is_a?(Puppet::Parser::AST::ASTArray)
|
||||
code ||= node.code
|
||||
unless code.nil?
|
||||
scan_for_include(n, code)
|
||||
scan_for_vardef(n, code)
|
||||
scan_for_resource(n, code) if Puppet.settings[:document_all]
|
||||
end
|
||||
|
||||
n.comment = comment
|
||||
end
|
||||
|
||||
# create documentation for a define
|
||||
def document_define(name, define, container)
|
||||
Puppet.debug "rdoc: found new definition %s" % name
|
||||
# find superclas if any
|
||||
@stats.num_methods += 1
|
||||
|
||||
# find the parentclass
|
||||
# split define name by :: to find the complete module hierarchy
|
||||
container, name = get_class_or_module(container,name)
|
||||
|
||||
return if container.find_local_symbol(name)
|
||||
|
||||
# build up declaration
|
||||
declaration = ""
|
||||
define.arguments.each do |arg,value|
|
||||
declaration << "\$#{arg}"
|
||||
if !value.nil?
|
||||
declaration << " => "
|
||||
if !value.is_a?(Puppet::Parser::AST::ASTArray)
|
||||
declaration << "'#{value.value}'"
|
||||
else
|
||||
declaration << "[%s]" % value.children.collect { |v| "'#{v}'" }.join(", ")
|
||||
end
|
||||
end
|
||||
declaration << ", "
|
||||
end
|
||||
declaration.chop!.chop! if declaration.size > 1
|
||||
|
||||
# register method into the container
|
||||
meth = AnyMethod.new(declaration, name)
|
||||
container.add_method(meth)
|
||||
meth.comment = define.doc
|
||||
look_for_directives_in(container, meth.comment) unless meth.comment.empty?
|
||||
meth.params = "( " + declaration + " )"
|
||||
meth.visibility = :public
|
||||
meth.document_self = true
|
||||
meth.singleton = false
|
||||
end
|
||||
|
||||
# Traverse the AST tree and produce code-objects node
|
||||
# that contains the documentation
|
||||
def parse_elements(container)
|
||||
Puppet.debug "rdoc: scanning manifest"
|
||||
@ast[:classes].each do |name, klass|
|
||||
if klass.file == @input_file_name
|
||||
unless name.empty?
|
||||
document_class(name,klass,container)
|
||||
else # on main class document vardefs
|
||||
code = klass.code.children unless klass.code.is_a?(Puppet::Parser::AST::ASTArray)
|
||||
code ||= klass.code
|
||||
scan_for_vardef(container, code) unless code.nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ast[:definitions].each do |name, define|
|
||||
if define.file == @input_file_name
|
||||
document_define(name,define,container)
|
||||
end
|
||||
end
|
||||
|
||||
@ast[:nodes].each do |name, node|
|
||||
if node.file == @input_file_name
|
||||
document_node(name,node,container)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# create documentation for plugins
|
||||
def parse_plugins(container)
|
||||
Puppet.debug "rdoc: scanning plugin or fact"
|
||||
if @input_file_name =~ /\/facter\/[^\/]+\.rb$/
|
||||
parse_fact(container)
|
||||
else
|
||||
parse_puppet_plugin(container)
|
||||
end
|
||||
end
|
||||
|
||||
# this is a poor man custom fact parser :-)
|
||||
def parse_fact(container)
|
||||
comments = ""
|
||||
current_fact = nil
|
||||
File.open(@input_file_name) do |of|
|
||||
of.each do |line|
|
||||
# fetch comments
|
||||
if line =~ /^[ \t]*# ?(.*)$/
|
||||
comments += $1 + "\n"
|
||||
elsif line =~ /^[ \t]*Facter.add\(['"](.*?)['"]\)/
|
||||
current_fact = Fact.new($1,{})
|
||||
container.add_fact(current_fact)
|
||||
look_for_directives_in(container, comments) unless comments.empty?
|
||||
current_fact.comment = comments
|
||||
current_fact.record_location(@top_level)
|
||||
comments = ""
|
||||
Puppet.debug "rdoc: found custom fact %s" % current_fact.name
|
||||
elsif line =~ /^[ \t]*confine[ \t]*:(.*?)[ \t]*=>[ \t]*(.*)$/
|
||||
current_fact.confine = { :type => $1, :value => $2 } unless current_fact.nil?
|
||||
else # unknown line type
|
||||
comments =""
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# this is a poor man puppet plugin parser :-)
|
||||
# it doesn't extract doc nor desc :-(
|
||||
def parse_puppet_plugin(container)
|
||||
comments = ""
|
||||
current_plugin = nil
|
||||
|
||||
File.open(@input_file_name) do |of|
|
||||
of.each do |line|
|
||||
# fetch comments
|
||||
if line =~ /^[ \t]*# ?(.*)$/
|
||||
comments += $1 + "\n"
|
||||
elsif line =~ /^[ \t]*newfunction[ \t]*\([ \t]*:(.*?)[ \t]*,[ \t]*:type[ \t]*=>[ \t]*(:rvalue|:lvalue)\)/
|
||||
current_plugin = Plugin.new($1, "function")
|
||||
container.add_plugin(current_plugin)
|
||||
look_for_directives_in(container, comments) unless comments.empty?
|
||||
current_plugin.comment = comments
|
||||
current_plugin.record_location(@top_level)
|
||||
comments = ""
|
||||
Puppet.debug "rdoc: found new function plugins %s" % current_plugin.name
|
||||
elsif line =~ /^[ \t]*Puppet::Type.newtype[ \t]*\([ \t]*:(.*?)\)/
|
||||
current_plugin = Plugin.new($1, "type")
|
||||
container.add_plugin(current_plugin)
|
||||
look_for_directives_in(container, comments) unless comments.empty?
|
||||
current_plugin.comment = comments
|
||||
current_plugin.record_location(@top_level)
|
||||
comments = ""
|
||||
Puppet.debug "rdoc: found new type plugins %s" % current_plugin.name
|
||||
elsif line =~ /module Puppet::Parser::Functions/
|
||||
# skip
|
||||
else # unknown line type
|
||||
comments =""
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# look_for_directives_in scans the current +comment+ for RDoc directives
|
||||
def look_for_directives_in(context, comment)
|
||||
preprocess = SM::PreProcess.new(@input_file_name, @options.rdoc_include)
|
||||
|
||||
preprocess.handle(comment) do |directive, param|
|
||||
case directive
|
||||
when "stopdoc"
|
||||
context.stop_doc
|
||||
""
|
||||
when "startdoc"
|
||||
context.start_doc
|
||||
context.force_documentation = true
|
||||
""
|
||||
when "enddoc"
|
||||
#context.done_documenting = true
|
||||
#""
|
||||
throw :enddoc
|
||||
when "main"
|
||||
options = Options.instance
|
||||
options.main_page = param
|
||||
""
|
||||
when "title"
|
||||
options = Options.instance
|
||||
options.title = param
|
||||
""
|
||||
when "section"
|
||||
context.set_current_section(param, comment)
|
||||
comment.replace("") # 1.8 doesn't support #clear
|
||||
break
|
||||
else
|
||||
warn "Unrecognized directive '#{directive}'"
|
||||
break
|
||||
end
|
||||
end
|
||||
remove_private_comments(comment)
|
||||
end
|
||||
|
||||
def remove_private_comments(comment)
|
||||
comment.gsub!(/^#--.*?^#\+\+/m, '')
|
||||
comment.sub!(/^#--.*/m, '')
|
||||
end
|
||||
|
||||
# convert an AST value to a string
|
||||
def value_to_s(value)
|
||||
value = value.children if value.is_a?(Puppet::Parser::AST::ASTArray)
|
||||
if value.is_a?(Array)
|
||||
"['#{value.join(", ")}']"
|
||||
elsif [:true, true, "true"].include?(value)
|
||||
"true"
|
||||
elsif [:false, false, "false"].include?(value)
|
||||
"false"
|
||||
else
|
||||
value.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,74 +1,67 @@
|
|||
# Provides utility functions to help interfaces Puppet to SELinux.
|
||||
#
|
||||
# Currently this is implemented via the command line tools. At some
|
||||
# point support should be added to use the new SELinux ruby bindings
|
||||
# as that will be faster and more reliable then shelling out when they
|
||||
# are available. At this time (2008-09-26) these bindings aren't bundled on
|
||||
# any SELinux-using distribution I know of.
|
||||
# This requires the very new SELinux Ruby bindings. These bindings closely
|
||||
# mirror the SELinux C library interface.
|
||||
#
|
||||
# Support for the command line tools is not provided because the performance
|
||||
# was abysmal. At this time (2008-11-02) the only distribution providing
|
||||
# these Ruby SELinux bindings which I am aware of is Fedora (in libselinux-ruby).
|
||||
|
||||
require 'puppet/util'
|
||||
begin
|
||||
require 'selinux'
|
||||
rescue LoadError
|
||||
# Nothing
|
||||
end
|
||||
|
||||
module Puppet::Util::SELinux
|
||||
|
||||
include Puppet::Util
|
||||
|
||||
def selinux_support?
|
||||
FileTest.exists?("/selinux/enforce")
|
||||
unless defined? Selinux
|
||||
return false
|
||||
end
|
||||
if Selinux.is_selinux_enabled == 1
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
# Retrieve and return the full context of the file. If we don't have
|
||||
# SELinux support or if the stat call fails then return nil.
|
||||
# SELinux support or if the SELinux call fails then return nil.
|
||||
def get_selinux_current_context(file)
|
||||
unless selinux_support?
|
||||
return nil
|
||||
end
|
||||
context = ""
|
||||
begin
|
||||
execpipe("/usr/bin/stat -c %C #{file}") do |out|
|
||||
out.each do |line|
|
||||
context << line
|
||||
end
|
||||
end
|
||||
rescue Puppet::ExecutionFailure
|
||||
retval = Selinux.lgetfilecon(file)
|
||||
if retval == -1
|
||||
return nil
|
||||
end
|
||||
context.chomp!
|
||||
# Handle the case that the system seems to have SELinux support but
|
||||
# stat finds unlabled files.
|
||||
if context == "(null)"
|
||||
return nil
|
||||
end
|
||||
return context
|
||||
return retval[1]
|
||||
end
|
||||
|
||||
# Use the matchpathcon command, if present, to return the SELinux context
|
||||
# which the SELinux policy on the system expects the file to have. We can
|
||||
# use this to obtain a good default context. If the command does not
|
||||
# exist or the call fails return nil.
|
||||
#
|
||||
# Note: For this command to work a full, non-relative, filesystem path
|
||||
# should be given.
|
||||
# Retrieve and return the default context of the file. If we don't have
|
||||
# SELinux support or if the SELinux call fails to file a default then return nil.
|
||||
def get_selinux_default_context(file)
|
||||
unless selinux_support?
|
||||
return nil
|
||||
end
|
||||
unless FileTest.executable?("/usr/sbin/matchpathcon")
|
||||
# If the filesystem has no support for SELinux labels, return a default of nil
|
||||
# instead of what matchpathcon would return
|
||||
unless selinux_label_support?(file)
|
||||
return nil
|
||||
end
|
||||
context = ""
|
||||
# If the file exists we should pass the mode to matchpathcon for the most specific
|
||||
# matching. If not, we can pass a mode of 0.
|
||||
begin
|
||||
execpipe("/usr/sbin/matchpathcon #{file}") do |out|
|
||||
out.each do |line|
|
||||
context << line
|
||||
end
|
||||
end
|
||||
rescue Puppet::ExecutionFailure
|
||||
filestat = File.lstat(file)
|
||||
mode = filestat.mode
|
||||
rescue Errno::ENOENT
|
||||
mode = 0
|
||||
end
|
||||
retval = Selinux.matchpathcon(file, mode)
|
||||
if retval == -1
|
||||
return nil
|
||||
end
|
||||
# For a successful match, matchpathcon returns two fields separated by
|
||||
# a variable amount of whitespace. The second field is the full context.
|
||||
context = context.split(/\s/)[1]
|
||||
return context
|
||||
return retval[1]
|
||||
end
|
||||
|
||||
# Take the full SELinux context returned from the tools and parse it
|
||||
|
@ -91,32 +84,52 @@ module Puppet::Util::SELinux
|
|||
end
|
||||
|
||||
# This updates the actual SELinux label on the file. You can update
|
||||
# only a single component or update the entire context. It is just a
|
||||
# wrapper around the chcon command.
|
||||
# only a single component or update the entire context.
|
||||
# The caveat is that since setting a partial context makes no sense the
|
||||
# file has to already exist. Puppet (via the File resource) will always
|
||||
# just try to set components, even if all values are specified by the manifest.
|
||||
# I believe that the OS should always provide at least a fall-through context
|
||||
# though on any well-running system.
|
||||
def set_selinux_context(file, value, component = false)
|
||||
unless selinux_support?
|
||||
return nil
|
||||
end
|
||||
case component
|
||||
when :seluser
|
||||
flag = "-u"
|
||||
when :selrole
|
||||
flag = "-r"
|
||||
when :seltype
|
||||
flag = "-t"
|
||||
when :selrange
|
||||
flag = "-l"
|
||||
else
|
||||
flag = nil
|
||||
end
|
||||
|
||||
if flag.nil?
|
||||
cmd = ["/usr/bin/chcon","-h",value,file]
|
||||
if component
|
||||
# Must first get existing context to replace a single component
|
||||
context = Selinux.lgetfilecon(file)[1]
|
||||
if context == -1
|
||||
# We can't set partial context components when no context exists
|
||||
# unless/until we can find a way to make Puppet call this method
|
||||
# once for all selinux file label attributes.
|
||||
Puppet.warning "Can't set SELinux context on file unless the file already has some kind of context"
|
||||
return nil
|
||||
end
|
||||
context = context.split(':')
|
||||
case component
|
||||
when :seluser
|
||||
context[0] = value
|
||||
when :selrole
|
||||
context[1] = value
|
||||
when :seltype
|
||||
context[2] = value
|
||||
when :selrange
|
||||
context[3] = value
|
||||
else
|
||||
raise ArguementError, "set_selinux_context component must be one of :seluser, :selrole, :seltype, or :selrange"
|
||||
end
|
||||
context = context.join(':')
|
||||
else
|
||||
cmd = ["/usr/bin/chcon","-h",flag,value,file]
|
||||
context = value
|
||||
end
|
||||
|
||||
retval = Selinux.lsetfilecon(file, context)
|
||||
if retval == 0
|
||||
return true
|
||||
else
|
||||
Puppet.warning "Failed to set SELinux context %s on %s" % [context, file]
|
||||
return false
|
||||
end
|
||||
execute(cmd)
|
||||
return true
|
||||
end
|
||||
|
||||
# Since this call relies on get_selinux_default_context it also needs a
|
||||
|
@ -136,4 +149,63 @@ module Puppet::Util::SELinux
|
|||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
# Internal helper function to read and parse /proc/mounts
|
||||
def read_mounts
|
||||
begin
|
||||
mounts = File.read("/proc/mounts")
|
||||
rescue
|
||||
return nil
|
||||
end
|
||||
|
||||
mntpoint = {}
|
||||
|
||||
# Read all entries in /proc/mounts. The second column is the
|
||||
# mountpoint and the third column is the filesystem type.
|
||||
# We skip rootfs because it is always mounted at /
|
||||
mounts.collect do |line|
|
||||
params = line.split(' ')
|
||||
next if params[2] == 'rootfs'
|
||||
mntpoint[params[1]] = params[2]
|
||||
end
|
||||
return mntpoint
|
||||
end
|
||||
|
||||
# Internal helper function to return which type of filesystem a
|
||||
# given file path resides on
|
||||
def find_fs(file)
|
||||
unless mnts = read_mounts()
|
||||
return nil
|
||||
end
|
||||
|
||||
# For a given file:
|
||||
# Check if the filename is in the data structure;
|
||||
# return the fstype if it is.
|
||||
# Just in case: return something if you're down to "/" or ""
|
||||
# Remove the last slash and everything after it,
|
||||
# and repeat with that as the file for the next loop through.
|
||||
ary = file.split('/')
|
||||
while not ary.empty? do
|
||||
path = ary.join('/')
|
||||
if mnts.has_key?(path)
|
||||
return mnts[path]
|
||||
end
|
||||
ary.pop
|
||||
end
|
||||
return mnts['/']
|
||||
end
|
||||
|
||||
# Check filesystem a path resides on for SELinux support against
|
||||
# whitelist of known-good filesystems.
|
||||
# Returns true if the filesystem can support SELinux labels and
|
||||
# false if not.
|
||||
def selinux_label_support?(file)
|
||||
fstype = find_fs(file)
|
||||
if fstype.nil?
|
||||
return false
|
||||
end
|
||||
filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs']
|
||||
return filesystems.include?(fstype)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -9,8 +9,6 @@ class Puppet::Util::Settings
|
|||
include Enumerable
|
||||
include Puppet::Util
|
||||
|
||||
@@sync = Sync.new
|
||||
|
||||
attr_accessor :file
|
||||
attr_reader :timer
|
||||
|
||||
|
@ -21,22 +19,22 @@ class Puppet::Util::Settings
|
|||
|
||||
# Set a config value. This doesn't set the defaults, it sets the value itself.
|
||||
def []=(param, value)
|
||||
@@sync.synchronize do # yay, thread-safe
|
||||
param = symbolize(param)
|
||||
unless element = @config[param]
|
||||
raise ArgumentError,
|
||||
"Attempt to assign a value to unknown configuration parameter %s" % param.inspect
|
||||
end
|
||||
if element.respond_to?(:munge)
|
||||
value = element.munge(value)
|
||||
end
|
||||
if element.respond_to?(:handle)
|
||||
element.handle(value)
|
||||
end
|
||||
# Reset the name, so it's looked up again.
|
||||
if param == :name
|
||||
@name = nil
|
||||
end
|
||||
param = symbolize(param)
|
||||
unless element = @config[param]
|
||||
raise ArgumentError,
|
||||
"Attempt to assign a value to unknown configuration parameter %s" % param.inspect
|
||||
end
|
||||
if element.respond_to?(:munge)
|
||||
value = element.munge(value)
|
||||
end
|
||||
if element.respond_to?(:handle)
|
||||
element.handle(value)
|
||||
end
|
||||
# Reset the name, so it's looked up again.
|
||||
if param == :name
|
||||
@name = nil
|
||||
end
|
||||
@sync.synchronize do # yay, thread-safe
|
||||
@values[:memory][param] = value
|
||||
@cache.clear
|
||||
|
||||
|
@ -60,7 +58,6 @@ class Puppet::Util::Settings
|
|||
return options
|
||||
end
|
||||
|
||||
# Turn the config into a Puppet configuration and apply it
|
||||
def apply
|
||||
trans = self.to_transportable
|
||||
begin
|
||||
|
@ -87,25 +84,21 @@ class Puppet::Util::Settings
|
|||
|
||||
# Remove all set values, potentially skipping cli values.
|
||||
def clear(exceptcli = false)
|
||||
@config.each { |name, obj|
|
||||
unless exceptcli and obj.setbycli
|
||||
obj.clear
|
||||
@sync.synchronize do
|
||||
@values.each do |name, values|
|
||||
@values.delete(name) unless exceptcli and name == :cli
|
||||
end
|
||||
}
|
||||
@values.each do |name, values|
|
||||
next if name == :cli and exceptcli
|
||||
@values.delete(name)
|
||||
|
||||
# Don't clear the 'used' in this case, since it's a config file reparse,
|
||||
# and we want to retain this info.
|
||||
unless exceptcli
|
||||
@used = []
|
||||
end
|
||||
|
||||
@cache.clear
|
||||
|
||||
@name = nil
|
||||
end
|
||||
|
||||
# Don't clear the 'used' in this case, since it's a config file reparse,
|
||||
# and we want to retain this info.
|
||||
unless exceptcli
|
||||
@used = []
|
||||
end
|
||||
|
||||
@cache.clear
|
||||
|
||||
@name = nil
|
||||
end
|
||||
|
||||
# This is mostly just used for testing.
|
||||
|
@ -178,10 +171,12 @@ class Puppet::Util::Settings
|
|||
end
|
||||
str = str.intern
|
||||
if self.valid?(str)
|
||||
if self.boolean?(str)
|
||||
@values[:cli][str] = bool
|
||||
else
|
||||
@values[:cli][str] = value
|
||||
@sync.synchronize do
|
||||
if self.boolean?(str)
|
||||
@values[:cli][str] = bool
|
||||
else
|
||||
@values[:cli][str] = value
|
||||
end
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "Invalid argument %s" % opt
|
||||
|
@ -199,14 +194,17 @@ class Puppet::Util::Settings
|
|||
@shortnames.include?(short)
|
||||
end
|
||||
|
||||
# Create a new config object
|
||||
# Create a new collection of config settings.
|
||||
def initialize
|
||||
@config = {}
|
||||
@shortnames = {}
|
||||
|
||||
|
||||
@created = []
|
||||
@searchpath = nil
|
||||
|
||||
# Mutex-like thing to protect @values
|
||||
@sync = Sync.new
|
||||
|
||||
# Keep track of set values.
|
||||
@values = Hash.new { |hash, key| hash[key] = {} }
|
||||
|
||||
|
@ -310,7 +308,10 @@ class Puppet::Util::Settings
|
|||
end
|
||||
searchpath.each do |source|
|
||||
next if source == :name
|
||||
break if @name = @values[source][:name]
|
||||
@sync.synchronize do
|
||||
@name = @values[source][:name]
|
||||
end
|
||||
break if @name
|
||||
end
|
||||
unless @name
|
||||
@name = convert(@config[:name].default).intern
|
||||
|
@ -333,14 +334,24 @@ class Puppet::Util::Settings
|
|||
end
|
||||
end
|
||||
|
||||
# Parse the configuration file.
|
||||
# Parse the configuration file. Just provides
|
||||
# thread safety.
|
||||
def parse(file)
|
||||
# We have to clear outside of the sync, because it's
|
||||
# also using synchronize().
|
||||
clear(true)
|
||||
|
||||
@sync.synchronize do
|
||||
unsafe_parse(file)
|
||||
end
|
||||
end
|
||||
|
||||
# Unsafely parse the file -- this isn't thread-safe and causes plenty of problems if used directly.
|
||||
def unsafe_parse(file)
|
||||
parse_file(file).each do |area, values|
|
||||
@values[area] = values
|
||||
end
|
||||
|
||||
|
||||
# Determine our environment, if we have one.
|
||||
if @config[:environment]
|
||||
env = self.value(:environment).to_sym
|
||||
|
@ -377,12 +388,11 @@ class Puppet::Util::Settings
|
|||
# what kind of element we're creating, but the value itself might be either
|
||||
# a default or a value, so we can't actually assign it.
|
||||
def newelement(hash)
|
||||
value = hash[:value] || hash[:default]
|
||||
klass = nil
|
||||
if hash[:section]
|
||||
hash[:section] = symbolize(hash[:section])
|
||||
end
|
||||
case value
|
||||
case hash[:default]
|
||||
when true, false, "true", "false":
|
||||
klass = CBoolean
|
||||
when /^\$\w+\//, /^\//:
|
||||
|
@ -392,7 +402,7 @@ class Puppet::Util::Settings
|
|||
else
|
||||
raise Puppet::Error, "Invalid value '%s' for %s" % [value.inspect, hash[:name]]
|
||||
end
|
||||
hash[:parent] = self
|
||||
hash[:settings] = self
|
||||
element = klass.new(hash)
|
||||
|
||||
return element
|
||||
|
@ -415,7 +425,7 @@ class Puppet::Util::Settings
|
|||
def reparse
|
||||
if defined? @file and @file.changed?
|
||||
Puppet.notice "Reparsing %s" % @file.file
|
||||
@@sync.synchronize do
|
||||
@sync.synchronize do
|
||||
parse(@file)
|
||||
end
|
||||
reuse()
|
||||
|
@ -424,7 +434,7 @@ class Puppet::Util::Settings
|
|||
|
||||
def reuse
|
||||
return unless defined? @used
|
||||
@@sync.synchronize do # yay, thread-safe
|
||||
@sync.synchronize do # yay, thread-safe
|
||||
@used.each do |section|
|
||||
@used.delete(section)
|
||||
self.use(section)
|
||||
|
@ -508,7 +518,6 @@ class Puppet::Util::Settings
|
|||
name = symbolize(name)
|
||||
hash[:name] = name
|
||||
hash[:section] = section
|
||||
name = hash[:name]
|
||||
if @config.include?(name)
|
||||
raise ArgumentError, "Parameter %s is already defined" % name
|
||||
end
|
||||
|
@ -621,7 +630,7 @@ Generated on #{Time.now}.
|
|||
# Create the necessary objects to use a section. This is idempotent;
|
||||
# you can 'use' a section as many times as you want.
|
||||
def use(*sections)
|
||||
@@sync.synchronize do # yay, thread-safe
|
||||
@sync.synchronize do # yay, thread-safe
|
||||
sections = sections.reject { |s| @used.include?(s.to_sym) }
|
||||
|
||||
return if sections.empty?
|
||||
|
@ -680,16 +689,19 @@ Generated on #{Time.now}.
|
|||
end
|
||||
|
||||
# See if we can find it within our searchable list of values
|
||||
val = nil
|
||||
each_source(environment) do |source|
|
||||
# Look for the value. We have to test the hash for whether
|
||||
# it exists, because the value might be false.
|
||||
if @values[source].include?(param)
|
||||
val = @values[source][param]
|
||||
break
|
||||
val = catch :foundval do
|
||||
each_source(environment) do |source|
|
||||
# Look for the value. We have to test the hash for whether
|
||||
# it exists, because the value might be false.
|
||||
@sync.synchronize do
|
||||
if @values[source].include?(param)
|
||||
throw :foundval, @values[source][param]
|
||||
end
|
||||
end
|
||||
end
|
||||
throw :foundval, nil
|
||||
end
|
||||
|
||||
|
||||
# If we didn't get a value, use the default
|
||||
val = @config[param].default if val.nil?
|
||||
|
||||
|
@ -975,14 +987,9 @@ Generated on #{Time.now}.
|
|||
|
||||
# The base element type.
|
||||
class CElement
|
||||
attr_accessor :name, :section, :default, :parent, :setbycli, :call_on_define
|
||||
attr_accessor :name, :section, :default, :setbycli, :call_on_define
|
||||
attr_reader :desc, :short
|
||||
|
||||
# Unset any set value.
|
||||
def clear
|
||||
@value = nil
|
||||
end
|
||||
|
||||
def desc=(value)
|
||||
@desc = value.gsub(/^\s*/, '')
|
||||
end
|
||||
|
@ -1002,10 +1009,9 @@ Generated on #{Time.now}.
|
|||
|
||||
# Create the new element. Pretty much just sets the name.
|
||||
def initialize(args = {})
|
||||
if args.include?(:parent)
|
||||
self.parent = args[:parent]
|
||||
args.delete(:parent)
|
||||
end
|
||||
@settings = args.delete(:settings)
|
||||
raise ArgumentError.new("You must refer to a settings object") if @settings.nil? or !@settings.is_a?(Puppet::Util::Settings)
|
||||
|
||||
args.each do |param, value|
|
||||
method = param.to_s + "="
|
||||
unless self.respond_to? method
|
||||
|
@ -1060,7 +1066,7 @@ Generated on #{Time.now}.
|
|||
# If the value has not been overridden, then print it out commented
|
||||
# and unconverted, so it's clear that that's the default and how it
|
||||
# works.
|
||||
value = @parent.value(self.name)
|
||||
value = @settings.value(self.name)
|
||||
|
||||
if value != @default
|
||||
line = "%s = %s" % [@name, value]
|
||||
|
@ -1075,7 +1081,7 @@ Generated on #{Time.now}.
|
|||
|
||||
# Retrieves the value, or if it's not set, retrieves the default.
|
||||
def value
|
||||
@parent.value(self.name)
|
||||
@settings.value(self.name)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1086,7 +1092,7 @@ Generated on #{Time.now}.
|
|||
|
||||
def group
|
||||
if defined? @group
|
||||
return @parent.convert(@group)
|
||||
return @settings.convert(@group)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
@ -1094,7 +1100,7 @@ Generated on #{Time.now}.
|
|||
|
||||
def owner
|
||||
if defined? @owner
|
||||
return @parent.convert(@owner)
|
||||
return @settings.convert(@owner)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
@ -1117,7 +1123,7 @@ Generated on #{Time.now}.
|
|||
|
||||
# Return the appropriate type.
|
||||
def type
|
||||
value = @parent.value(self.name)
|
||||
value = @settings.value(self.name)
|
||||
if @name.to_s =~ /dir/
|
||||
return :directory
|
||||
elsif value.to_s =~ /\/$/
|
||||
|
@ -1187,7 +1193,7 @@ Generated on #{Time.now}.
|
|||
return true unless value.is_a? String
|
||||
value.scan(/\$(\w+)/) { |name|
|
||||
name = $1
|
||||
unless @parent.include?(name)
|
||||
unless @settings.include?(name)
|
||||
raise ArgumentError,
|
||||
"Settings parameter '%s' is undefined" %
|
||||
name
|
||||
|
|
|
@ -7,6 +7,49 @@ puppetlibdir = File.join(basedir, "../lib")
|
|||
puppettestlibdir = File.join(basedir, "../test/lib")
|
||||
speclibdir = File.join(basedir, "lib")
|
||||
|
||||
require 'find'
|
||||
|
||||
include Find
|
||||
include FileTest
|
||||
|
||||
$exclusions = %W(lib)
|
||||
|
||||
filemap = Hash.new { |hash, key| hash[key] = [] }
|
||||
|
||||
allfiles = []
|
||||
|
||||
# First collect the entire file list.
|
||||
find(".") do |f|
|
||||
# Get rid of the leading ./
|
||||
f = f.sub(/^\.\//, '')
|
||||
|
||||
file = File.basename(f)
|
||||
dir = File.dirname(f)
|
||||
|
||||
# Prune . directories and excluded dirs
|
||||
if (file =~ /^\./ and f != ".") or $exclusions.include?(File.basename(file))
|
||||
prune
|
||||
next
|
||||
end
|
||||
next if f == "."
|
||||
next if dir == "."
|
||||
|
||||
# If we're a ruby script, then add it to the list of files for that dir
|
||||
if file =~ /\.rb$/
|
||||
allfiles << f
|
||||
# Add it to all of the parent dirs, not just our own
|
||||
parts = File.split(dir)
|
||||
if parts[0] == "."
|
||||
parts.shift
|
||||
end
|
||||
parts.each_with_index { |part, i|
|
||||
path = File.join(parts[0..i])
|
||||
filemap[path] << f
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
libs = [puppetlibdir, puppettestlibdir, speclibdir]
|
||||
desc "Run all specs"
|
||||
Spec::Rake::SpecTask.new('all') do |t|
|
||||
|
@ -16,3 +59,33 @@ Spec::Rake::SpecTask.new('all') do |t|
|
|||
end
|
||||
|
||||
task :default => [:all]
|
||||
|
||||
# Now create a task for every directory
|
||||
filemap.each do |dir, files|
|
||||
ns = dir.gsub "/", ":"
|
||||
|
||||
# First create a separate task for each file in the namespace.
|
||||
namespace ns do
|
||||
files.each do |file|
|
||||
Spec::Rake::SpecTask.new(file) do |t|
|
||||
t.spec_files = File.basename(file, '.rb')
|
||||
t.libs = libs
|
||||
t.spec_opts = ['--options', 'spec.opts']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Then create a task that matches the directory itself.
|
||||
Spec::Rake::SpecTask.new(dir) do |t|
|
||||
if ENV["TESTFILES"]
|
||||
t.spec_files = ENV["TESTFILES"].split(/\s+/)
|
||||
else
|
||||
t.spec_files = files.sort
|
||||
end
|
||||
t.libs = libs
|
||||
t.spec_opts = ['--options', 'spec.opts']
|
||||
end
|
||||
|
||||
# And alias it with a slash on the end
|
||||
task(dir + "/" => dir)
|
||||
end
|
||||
|
|
|
@ -114,6 +114,12 @@ describe Puppet::Module, " when searching for templates" do
|
|||
Puppet::Module.find_template("mytemplate").should == "/my/templates/mytemplate"
|
||||
end
|
||||
|
||||
it "should accept relative templatedirs" do
|
||||
Puppet[:templatedir] = "my/templates"
|
||||
File.expects(:directory?).with(File.join(Dir.getwd,"my/templates")).returns(true)
|
||||
Puppet::Module.find_template("mytemplate").should == File.join(Dir.getwd,"my/templates/mytemplate")
|
||||
end
|
||||
|
||||
it "should use the environment templatedir if no module is found and an environment is specified" do
|
||||
Puppet::Module.stubs(:templatepath).with("myenv").returns(["/myenv/templates"])
|
||||
Puppet::Module.expects(:find).with("mymod", "myenv").returns(nil)
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче