diff --git a/CHANGELOG b/CHANGELOG index 635aabf25..3f62a6367 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,953 +1,957 @@ + Fixed #968 again, this time with tests -- parseonly works, + including not compiling the configurations, and also storeconfigs + is no longer required during parse-testing. + Fixed #1021 -- the problem was that my method of determining the in-degree sometimes resulted in a lower number than the number of in-edges. Fixed #997 -- virtual defined types are no longer evaluated. NOTE: This introduces a behaviour change, in that you previously could realize a resource within a virtual defined resource, and now you must realize the entire defined resource, rather than just the contained resource. Fixed #1030 - class and definition evaluation has been significantly refactored, fixing this problem and making the whole interplay between the classes, definitions, and nodes, and the Compile class much cleaner. Exec resources must now have unique names. This is easily accomplished by just specifying a unique name with whatever (unique or otherwise) command you need. Fixed #989 -- missing CRL files are correctly ignored, and the value should be set to 'false' to explicitly not look for these files. Fixed #1017 -- environment-specific modulepath is no longer ignored. Fixing #794 -- consolidating the gentoo configuration files. Fixing #976 -- both the full name of qualified classes and the class parts are now added as tags. I've also created a Tagging module that we should push throughout the rest of the system that uses tags. Fixing #995 -- puppetd no longer dies at startup if the server is not running. Fixing #977 -- the rundir is again set to 1777. Fixed #971 -- classes can once again be included multiple times. Added builtin support for Nagios types using Naginator to parse and generate the files. 0.24.1 Updated vim filetype detection. (#900 and #963) Default resources like schedules no longer conflict with managed resources. (#965) Removing the ability to disable http keep-alive, since it didn't really work anyway and it should no longer be necessary. Refactored http keep-alive so it actually works again. This should be sufficient enough that we no longer need the ability to disable keep-alive. There is now a central module responsible for managing HTTP instances, along with all certificates in those instances. Fixed a backward compatibility issue when running 0.23.x clients against 0.24.0 servers -- relationships would consistently not work. (#967) Closing existing http connections when opening a new one, and closing all connections after each run. (#961) Removed warning about deprecated explicit plugins mounts. 0.24.0 (misspiggy) Modifying the behaviour of the certdnsnames setting. It now defaults to an empty string, and will only be used if it is set to something else. If it is set, then the host's FQDN will also be added as an alias. The default behaviour is now to add 'puppet' and 'puppet.$domain' as DNS aliases when the name for the cert being signed is equal to the signing machine's name, which will only be the case for CA servers. This should result in servers always having the alias set up and no one else, but you can still override the aliases if you want. External node support now requires that you set the 'node_terminus' setting to 'exec'. See the IndirectionReference on the wiki for more information. http_enable_post_connection_check added as a configuration option for puppetd. This defaults to true, which validates the server SSL certificate against the requested host name in new versions of ruby. See #896 for more information. Mounts no longer remount swap filesystems. Slightly modifying how services manage their list of paths (and adding documention for it). Services now default to the paths specified by the provider classes. Removed 'type' as a valid attribute for services, since it's been deprecated since the creation of providers. Removed 'running' as a valid attribute for services, since it's been deprecated since February 2006. Added modified patch by Matt Palmer which adds a 'plugins' mount, fixing #891. See PluginsInModules on the wiki for information on usage. Empty dbserver and dbpassword settings will now be ignored when initializing Rails connections (patch by womble). Configuration settings can now be blank (patch by womble). Added calls to endpwent/endgrent when searching for user and group IDs, which fixes #791. Obviated 'target' in interfaces, as all file paths were automatically calculated anyway. The parameter is still there, but it's not used and just generates a warning. Fixing some of the problems with interface management on Red Hat. Puppet now uses the :netmask property and does not try to set the bootproto (#762). You now must specify an environment and you are required to specify the valid environments for your site. (#911) Certificates now always specify a subjectAltName, but it defaults to '*', meaning that it doesn't require DNS names to match. You can override that behaviour by specifying a value for 'certdnsnames', which will then require that hostname as a match (#896). Relationship metaparams (:notify, :require, :subscribe, and :before) now stack when they are collecting metaparam values from their containers (#446). For instance, if a resource inside a definition has a value set for 'require', and you call the definition with 'require', the resource gets both requires, where before it would only retain its initial value. Changed the behavior of --debug to include Mongrel client debugging information. Mongrel output will be written to the terminal only, not to the puppet debug log. This should help anyone working with reverse HTTP SSL proxies. (#905) Fixed #800 -- invalid configurations are no longer cached. This was done partially by adding a relationship validation step once the entire configuration is created, but it also required the previously-mentioned changes to how the configuration retrieval process works. Removed some functionality from the Master client, since the local functionality has been replaced with the Indirector already, and rearranging how configuration retrieval is done to fix ordering and caching bugs. The node scope is now above all other scopes besides the 'main' scope, which should help make its variables visible to other classes, assuming those classes were not included in the node's parent. Replaced GRATR::Digraph with Puppet::SimpleGraph as the base class for Puppet's graphing. Functionality should be equivalent but with dramatically better performance. The --use-nodes and --no-nodes options are now obsolete. Puppet automatically detects when nodes are defined, and if they are defined it will require that a node be found, else it will not look for a node nor will it fail if it fails to find one. Fixed #832. Added the '--no-daemonize' option to puppetd and puppetmasterd. NOTE: The default behavior of 'verbose' and 'debug' no longer cause puppetd and puppetmasterd to not daemonize. Added k5login type. (#759) Fixed CA race condition. (#693) Added shortname support to config.rb and refactored addargs 0.23.2 Fixed the problem in cron jobs where environment settings tended to multiple. (#749) Collection of resources now correctly only collects exported resources again. This was broken in 0.23.0. (#731) 'gen_config' now generates a configuration with all parameters under a heading that matches the process name, rather than keeping section headings. Refactored how the parser and interpreter relate, so parsing is now effectively an atomic process (thus fixing #314 and #729). This makes the interpreter less prone to error and less prone to show the error to the clients. Note that this means that if a configuration fails to parse, then the previous, parseable configuration will be used instead, so the client will not know that the configuration failed to parse. Added support for managing interfaces, thanks to work by Paul Rose. Fixed #652, thanks to a patch by emerose; --fqdn again works with puppetd. Added an extra check to the Mongrel support so that Apache can be used with optional cert checking, instead of mandatory, thus allowing Mongrel to function as the CA. This is thanks to work done by Marcin Owsiany. 0.23.1 (beaker) You can now specify relationships to classes, which work exactly like relationships to defined types: require => Class[myclass] This works with qualified classes, too. You can now do simple queries in a collection of exported resources. You still cannot do multi-condition queries, though. (#703) puppetca now exits with a non-zero code if it cannot find any host certificates to clean. (Patch by Dean Wilson.) Fully-qualified resources can now have defaults. (#589) Resource references can now be fully-qualified names, meaning you can list definitions with a namespace as dependencies. (#468) Files modified using a FileType instance, as ParsedFile does, will now automatically get backed up to the filebucket named "puppet". Added a 'maillist' type for managing mailing lists. Added a 'mailalias' type for managing mail aliases. Added patch by Valentin Vidic that adds the '+>' syntax to resources, so parameter values can be added to. The configuration client now pulls libraries down to $libdir, and all autoloading is done from there with full support for any reloadable file, such as types and providers. (#621) Note that this is not backward compatible -- if you're using pluginsync right now, you'll need to disable it on your clients until you can upgrade them. The Rails log level can now be set via (shockingly!) the 'rails_loglevel' parameter (#710). Note that this isn't exactly the feature asked for, but I could not find a way to directly copy ActiveRecord's concept of an environment. External node sources can now return undefined classes (#687). Puppet clients now have http proxy support (#701). The parser now throws an error when a resource reference is created for an unknown type. Also, resource references look up defined types and translate their type accordingly. (#706) Hostnames can now be double quoted. Adding module autoloading (#596) -- you can now 'include' classes from modules without ever needing to specifically load them. Class names and node names now conflict (#620). 0.23.0 Modified the fileserver to cache file information, so that each file isn't being read on every connection. Also, added londo's patch from #678 to avoid reading entire files into memory. Fixed environment handling in the crontab provider (#669). Added patch by trombik in #572, supporting old-style freebsd init scripts with '.sh' endings. Added fink package provider (#642), as provided by 'do'. Marked the dpkg package provider as versionable (#647). Applied patches by trombik to fix FreeBSD ports (#624 and #628). Fixed the CA server so that it refuses to send back a certificate whose public key doesn't match the CSR. Instead, it tells the user to run 'puppetca --clean'. Invalid certificates are no longer written to disk (#578). Added a package provider (appdmg) able to install .app packages on .dmg files on OS X (#641). Applied the patch from #667 to hopefully kill the client hanging problems (permanently, this time). Fixed functions so that they accept most other rvalues as valid values (#548). COMPATIBILITY ALERT: Significantly reworked external node support, in a way that's NOT backward-compatible: Only ONE node source can be used -- you can use LDAP, code, or an external node program, but not more than one. LDAP node support has two changes: First, the "ldapattrs" attribute is now used for setting the attributes to retrieve from the server (in addition to required attriutes), and second, all retrieved attributes are set as variables in the top scope. This means you can set attributes on your LDAP nodes and they will automatically appear as variables in your configurations. External node support has been completely rewritten. These programs must now generate a YAML dump of a hash, with "classes" and "parameters" keys. The classes should be an array, and the parameters should be a hash. The external node program has no support for parent nodes -- the script must handle that on its own. Reworked the database schema used to store configurations with the storeconfigs option. Replaced the obsolete RRD ruby library with the maintained RubyRRDtool library (which requires rrdtool2) (#659). The Portage package provider now calls eix-update automatically when eix's database is absent or out of sync (#666). Mounts now correctly handle existing fstabs with no pass or dump values (#550). Mounts now default to 0 for pass and dump (#112). Added urpmi support (#592). Finishing up the type => provider interface work. Basically, package providers now return lists of provider instances. In the proces, I rewrote the interface between package types and providers, and also enabled prefetching on all packages. This should significantly speed up most package operations. Hopefully fixing the file descriptor/open port problems, with patches from Valentin Vidic. Significantly reworked the type => provider interface with respect to listing existing provider instances. The class method on both class heirarchies has been renamed to 'instances', to start. Providers are now expected to return provider instances, instead of creating resources, and the resource's 'instances' method is expected to find the matching resource, if any, and set the resource's provider appropriately. This *significantly* reduces the reliance on effectively global state (resource references in the resource classes). This global state will go away soon. Along with this change, the 'prefetch' class method on providers now accepts the list of resources for prefetching. This again reduces reliance on global state, and makes the execution path much easier to follow. Fixed #532 -- reparsing config files now longer throws an exception. Added some warnings and logs to the service type so users will be encouraged to specify either "ensure" or "enabled" and added debugging to indicate why restarting is skipped when it is. Changed the location of the classes.txt to the state directory. Added better error reporting on unmatched brackets. Moved puppetd and puppetmasterd to sbin in svn and fixed install.rb to copy them into sbin on the local system appropriately. (#323) Added a splay option (#501). It's disabled when running under --test in puppetd. The value is random but cached. It defaults to the runinterval but can be tuned with --splaylimit Changing the notify type so that it always uses the loglevel. Fixing #568 - nodes can inherit from quoted node names. Tags (and thus definitions and classes) can now be a single character. (#566) Added an 'undef' keyword (#629), which will evaluate to "" within strings but when used as a resource parameter value will cause that parameter to be evaluated as undefined. Changed the topological sort algorithm (#507) so it will always fail on cycles. Added a 'dynamicfacts' configuration option; any facts in that comma-separated list will be ignored when comparing facts to see if they have changed and thus whether a recompile is necessary. Renamed some poorly named internal variables: @models in providers are now either @resource or @resource_type (#605). @children is no longer used except by components (#606). @parent is now @resource within parameters (#607). The old variables are still set for backward compatibility. Significantly reworking configuration parsing. Executables all now look for 'puppet.conf' (#206), although they will parse the old-style configuration files if they are present, although they throw a deprecation warning. Also, file parameters (owner, mode, group) are now set on the same line as the parameter, in brackets. (#422) Added transaction summaries (available with the --summarize option), useful for getting a quick idea of what happened in a transaction. Currently only useful on the client or with the puppet interpreter. Changed the interal workings for retrieve and removed the :is attribute from Property. The retrieve methods now return the current value of the property for the system. Removed acts_as_taggable from the rails models. 0.22.4 Execs now autorequire the user they run as, as long as the user is specified by name. (#430) Files on the local machine but not on the remote server during a source copy are now purged if purge => true. (#594) Providers can now specify that some commands are optional (#585). Also, the 'command' method returns nil on missing commands, rather than throwing an error, so the presence of commands be tested. The 'useradd' provider for Users can now manage passwords. No other providers can, at this point. Parameters can now declare a dependency on specific features, and parameters that require missing features will not be instantiated. This is most useful for properties. FileParsing classes can now use instance_eval to add many methods at once to a record type. Modules no longer return directories in the list of found manifests (#588). The crontab provider now defaults to root when there is no USER set in the environment. Puppetd once again correctly responds to HUP. Added a syntax for referring to variables defined in other classes (e.g., $puppet::server). STDIN, STDOUT, STDERR are now redirected to /dev/null in service providers descending from base. Certificates are now valid starting one day before they are created, to help handle small amounts of clock skew. Files are no longer considered out of sync if some properties are out of sync but they have no properties that can create the file. 0.22.3 Fixed backward compatibility for logs and metrics from older clients. Fixed the location of the authconfig parameters so there aren't loading order issues. Enabling attribute validation on the providers that subclass 'nameservice', so we can verify that an integer is passed to UID and GID. Added a stand-alone filebucket client, named 'filebucket'. Fixed the new nested paths for filebuckets; the entire md5 sum was not being stored. Fixing #553; -M is no longer added when home directories are being managed on Red Hat. 0.22.2 (grover) Users can now manage their home directories, using the managehome parameter, partially using patches provided by Tim Stoop and Matt Palmer. (#432) Added 'ralsh' (formerly x2puppet) to the svn tree. When possible it should be added to the packages. The 'notify' type now defaults to its message being the same as its name. Reopening $stdin to read from /dev/null during execution, in hopes that init scripts will stop hanging. Changed the 'servername' fact set on the server to use the server's fqdn, instead of the short-name. Changing the location of the configuration cache. It now defaults to being in the state directory, rather than in the configuration directory. All parameter instances are stored in a single @parameters instance variable hash within resource type instances. We used to use separate hashes for each parameter type. Added the concept of provider features. Eventually these should be able to express the full range of provider functionality, but for now they can test a provider to see what methods it has set and determine what features it provides as a result. These features are integrated into the doc generation system so that you get feature documentation automatically. Switched apt/aptitide to using "apt-cache policy" instead of "apt-cache showpkg" for determining the latest available version. (#487) FileBuckets now use a deeply nested structure for storing files, so you do not end up with hundreds or thousands of files in the same directory. (#447) Facts are now cached in the state file, and when they change the configuration is always recompiled. (#519) Added 'ignoreimport' setting for use in commit hooks. This causes the parser to ignore import statements so a single file can be parse-checked. (#544) Import statements can now specify multiple comma-separated arguments. Definitions now support both 'name' and 'title', just like any other resource type. (#539) Added a generate() command, which sets values to the result of an external command. (#541) Added a file() command to read in files with no interpolation. The first found file has its content returned. puppetd now exits if no cert is present in onetime mode. (#533) The client configuration cache can be safely removed and the client will correctly realize the client is not in sync. Resources can now be freely deleted, thus fixing many problems introduced when deletion of required resources was forbidden when purging was introduced. Only resources being purged will not be deleted. Facts and plugins now download even in noop mode (#540). Resources in noop mode now log when they would have responded to an event (#542). Refactored cron support entirely. Cron now uses providers, and there is a single 'crontab' provider that handles user crontabs. While this refactor does not include providers for /etc/crontab or cron.d, it should now be straightforward to write those providers. Changed the parameter sorting so that the provider parameter comes right after name, so the provider is available when the other parameters and properties are being created. Redid some of the internals of the ParsedFile provider base class. It now passes a FileRecord around instead of a hash. Fixing a bug related to link recursion that caused link directories to always be considered out of sync. The bind address for puppetmasterd can now be specified with --bindaddress. Added (probably experimental) mongrel support. At this point you're still responsible for starting each individual process, and you have to set up a proxy in front of it. Redesigned the 'network' tree to support multiple web servers, including refactoring most of the structural code so it's much clearer and more reusable now. Set up the CA client to default to ca_server and ca_port, so you can easily run a separate CA. Supporting hosts with no domain name, thanks to a patch from Dennis Jacobfeuerborn. Added an 'ignorecache' option to tell puppetd to force a recompile, thanks to a patch by Chris McEniry. Made up2date the default for RHEL < 4 and yum the default for the rest. The yum provider now supports versions. Case statements correctly match when multiple values are provided, thanks to a patch by David Schmitt. Functions can now be called with no arguments. String escapes parse correctly in all cases now, thanks to a patch by cstorey. Subclasses again search parent classes for defaults. You can now purge apt and dpkg packages. When doing file recursion, 'ensure' only affects the top-level directory. States have been renamed to Properties. 0.22.1 (kermit) -- Mostly a bugfix release Compile times now persist between restarts of puppetd. Timeouts have been added to many parts of Puppet, reducing the likelihood if it hanging forever on broken scripts or servers. All of the documentation and recipes have been moved to the wiki by Peter Abrahamsen and Ben Kite has moved the FAQ to the wiki. Explicit relationships now override automatic relationships, allowing you to manually specify deletion order when removing resources. Resources with dependencies can now be deleted as long as all of their dependencies are also being deleted. Namespaces for both classes and definitions now work much more consistently. You should now be able to specify a class or definition with a namespace everywhere you would normally expect to be able to specify one without. Downcasing of facts can be selectively disabled. Cyclic dependency graphs are now checked for and forbidden. The netinfo mounts provider was commented out, because it really doesn't work at all. Stupid NetInfo stores mount information with the device as the key, which doesn't work with my current NetInfo code. Otherwise, lots and lots of bugfixes. Check the tickets associated with the 'kermit' milestone. 0.22.0 Integrated the GRATR graph library into Puppet, for handling resource relationships. Lots of bug-fixes (see bugs tickets associated with the 'minor' milestone). Added new 'resources' metatype, which currently only includes the ability to purge unmanaged resources. Added better ability to generate new resource objects during transactions (using 'generate' and 'eval_generate' methods). Rewrote all Rails support with a much better database design. Export/collect now works, although the database is incompatible with previous versions. Removed downcasing of facts and made most of the language case-insensitive. Added support for printing the graphs built during transactions. Reworked how paths are built for logging. Switched all providers to directly executing commands instead of going through a subshell, which removes the need to quote or escape arguments. 0.20.1 Mostly a bug-fix release, with the most important fix being the multiple-definition error. Completely rewrote the ParsedFile system; each provider is now much shorter and much more maintainable. However, fundamental problems were found with the 'port' type, so it was disabled. Also, added a NetInfo provider for 'host' and an experimental NetInfo provider for 'mount'. Made the RRDGraph report *much* better and added reference generation for reports and functions. 0.20.0 Significantly refactored the parser. Resource overrides now consistently work anywhere in a class hierarchy. The language was also modified somewhat. The previous export/collect syntax is now used for handling virtual objects, and export/collect (which is still experimental) now uses double sigils (@@ and <<| |>>). Resource references (e.g., File["/etc/passwd"]) now have to be capitalized, in fitting in with capitalizing type operations. As usual, lots of other smaller fixes, but most of the work was in the language. 0.19.3 Fixing a bug in server/master.rb that causes the hostname not to be available in locally-executed manifests. 0.19.2 Fixing a few smaller bugs, notably in the reports system. Refreshed objects now generate an event, which can result in further refreshes of other objects. 0.19.1 Fixing two critical bugs: User management works again and cron jobs are no longer added to all user accounts. 0.19.0 Added provider support. Added support for %h, %H, and %d expansion in fileserver.conf. Added Certificate Revocation support. Made dynamic loading pervasive -- nearly every aspect of Puppet will now automatically load new instances (e.g., types, providers, and reports). Added support for automatic distribution of facts and plugins (custom types). 0.18.4 Another bug-fix release. The most import bug fixed is that cronjobs again work even with initially empty crontabs. 0.18.3 Mostly a bug-fix release; fixed small bugs in the functionality added in 0.18.2. 0.18.2 Added templating support. Added reporting. Added gem and blastwave packaging support. 0.18.1 Added signal handlers for HUP, so both client and server deal correctly with it. Added signal handler for USR1, which triggers a run on the client. As usual, fixed many bugs. Significant fixes to puppetrun -- it should behave much more correctly now. Added "fail" function which throws a syntax error if it's encountered. Added plugin downloading from the central server to the client. It must be enabled with --pluginsync. Added support for FreeBSD's special "@daily" cron schedules. Correctly handling spaces in file sources. Moved documentation into svn tree. 0.18.0 Added support for a "default" node. When multiple nodes are specified, they must now be comma-separated (this introduces a language incompatibility). Failed dependencies cause dependent objects within the same transaction not to run. Many updates to puppetrun Many bug fixes Function names are no longer reserved words. Links can now replace files. 0.17.2 Added "puppetrun" application and associated runner server and client classes. Fixed cron support so it better supports valid values and environment settings. 0.17.1 Fixing a bug requiring rails on all Debian boxes Fixing a couple of other small bugs 0.17.0 Adding ActiveRecord integration on the server Adding export/collect functionality Fixing many bugs 0.16.5 Fixing a critical bug in importing classes from other files Fixing nodename handling to actually allow dashes 0.16.4 Fixing a critical bug in puppetd when acquiring a certificate for the first time 0.16.3 Some significant bug fixes Modified puppetd so that it can now function as an agent independent of a puppetmasterd process, e.g., using the PuppetShow web application. 0.16.2 Modified some of the AST classes so that class names, definition names, and node names are all set within the code being evaluated, so 'tagged(name)' returns true while evaluating 'name', for instance. Added '--clean' argument to puppetca to remove all traces of a given client. 0.16.1 Added 'tagged' and 'defined' functions. Moved all functions to a general framework that makes it very easy to add new functions. 0.16.0 Added 'tag' keyword/function. Added FreeBSD Ports support Added 'pelement' server for sending or receiving Puppet objects, although none of the executables use it yet. 0.15.3 Fixed many bugs in :exec, including adding support for arrays of checks Added autoloading for types and service variants (e.g., you can now just create a new type in the appropriate location and use it in Puppet, without modifying the core Puppet libs). 0.15.2 Added darwinport, Apple .pkg, and freebsd package types Added 'mount type Host facts are now set at the top scope (Bug #103) Added -e (inline exection) flag to 'puppet' executable Many small bug fixes 0.15.1 Fixed 'yum' installs so that they successfully upgrade packages. Fixed puppetmasterd.conf file so group settings take. 0.15.0 Upped the minor release because the File server is incompatible with 0.14, because it now handles links. The 'symlink' type is deprecated (but still present), in favor of using files with the 'target' parameter. Unset variables no longer throw an error, they just return an empty string You can now specify tags to restrict which objects run during a given run. You can also specify to skip running against the cached copy when there's a failure, which is useful for testing new configurations. RPMs and Sun packages can now install, as long as they specify a package location, and they'll automatically upgrade if you point them to a new file with an upgrade. Multiple bug fixes. 0.14.1 Fixed a couple of small logging bugs Fixed a bug with handling group ownership of links 0.14.0 Added some ability to selectively manage symlinks when doing file management Many bug fixes Variables can now be used as the test values in case statements and selectors Bumping a minor release number because 0.13.4 introduced a protocol incompatibility and should have had a minor rev bump 0.13.6 Many, many small bug fixes FreeBSD user/group support has been added The configuration system has been rewritten so that daemons can now generate and repair the files and directories they need. (Fixed bug #68.) Fixed the element override issues; now only subclasses can override values. 0.13.5 Fixed packages so types can be specified Added 'enable' state to services, although it does not work everywhere yet 0.13.4 A few important bug fixes, mostly in the parser. 0.13.3 Changed transactions to be one-stage instead of two Changed all types to use self[:name] instead of self.name, to support the symbolic naming implemented in 0.13.1 0.13.2 Changed package[answerfile] to package[adminfile], and added package[responsefile] Fixed a bunch of internal functions to behave more consistently and usefully 0.13.1 Fixed RPM spec files to create puppet user and group (lutter) Fixed crontab reading and writing (luke) Added symbolic naming in the language (luke) 0.13.0 Added support for configuration files. Even more bug fixes, including the infamous 'frozen object' bug, which was a problem with 'waitforcert'. David Lutterkort got RPM into good shape. 0.12.0 Added Scheduling, and many bug fixes, of course. 0.11.2 Fixed bugs related to specifying arrays of requirements Fixed a key bug in retrieving checksums Fixed lots of usability bugs Added 'fail' methods that automatically add file and line info when possible, and converted many errors to use that method 0.11.1 Fixed bug with recursive copying with 'ignore' set. Added OpenBSD package support. 0.11.0 Added 'ensure' state to many elements. Modified puppetdoc to correctly handle indentation and such. Significantly rewrote much of the builtin documentation to take advantage of the new features in puppetdoc, including many examples. 0.10.2 Added SMF support Added autorequire functionality, with specific support for exec and file Exec elements autorequire any mentioned files, including the scripts, along with their CWDs. Files autorequire any parent directories. Added 'alias' metaparam. Fixed dependencies so they don't depend on file order. 0.10.1 Added Solaris package support and changed puppetmasterd to run as a non-root user. 0.10.0 Significant refactoring of how types, states, and parameters work, including breaking out parameters into a separate class. This refactoring did not introduce much new functionality, but made extension of Puppet significantly easier Also, fixed the bug with 'waitforcert' in puppetd. 0.9.4 Small fix to wrap the StatusServer class in the checks for required classes. 0.9.3 Fixed some significant bugs in cron job management. 0.9.2 Second Public Beta 0.9.0 First Public Beta diff --git a/bin/puppet b/bin/puppet index f5d230a7a..952ffcbf8 100755 --- a/bin/puppet +++ b/bin/puppet @@ -1,220 +1,228 @@ #!/usr/bin/env ruby # # = Synopsis # # Run a stand-alone +puppet+ script. # # = Usage # # puppet [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose] # [-l|--logdest ] # # = Description # # This is the standalone puppet execution script; use it to execute # individual scripts that you write. If you need to execute site-wide # scripts, use +puppetd+ and +puppetmasterd+. # # = Options # # Note that any configuration parameter that's valid in the configuration file # is also a valid long argument. For example, 'ssldir' is a valid configuration # parameter, so you can specify '--ssldir ' as an argument. # # See the configuration file documentation at # http://reductivelabs.com/projects/puppet/reference/configref.html for # the full list of acceptable parameters. A commented list of all # configuration options can also be generated by running puppet with # '--genconfig'. # # debug:: # Enable full debugging. # # help:: # Print this help message # # loadclasses:: # Load any stored classes. +puppetd+ caches configured classes (usually at # /etc/puppet/classes.txt), and setting this option causes all of those classes # to be set in your +puppet+ manifest. # # logdest:: # Where to send messages. Choose between syslog, the console, and a log file. # Defaults to sending messages to the console. # # verbose:: # Print extra information. # # = Example # # puppet -l /tmp/script.log script.pp # # = Author # # Luke Kanies # # = Copyright # # Copyright (c) 2005 Reductive Labs, LLC # Licensed under the GNU Public License require 'puppet' require 'puppet/network/handler' require 'puppet/network/client' require 'getoptlong' options = [ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ], [ "--help", "-h", GetoptLong::NO_ARGUMENT ], [ "--logdest", "-l", GetoptLong::REQUIRED_ARGUMENT ], [ "--execute", "-e", GetoptLong::REQUIRED_ARGUMENT ], [ "--loadclasses", "-L", GetoptLong::NO_ARGUMENT ], [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ], [ "--use-nodes", GetoptLong::NO_ARGUMENT ], [ "--version", "-V", GetoptLong::NO_ARGUMENT ] ] # Add all of the config parameters as valid options. Puppet.settings.addargs(options) result = GetoptLong.new(*options) options = { :debug => false, :verbose => false, :noop => false, :logfile => false, :loadclasses => false, :logset => false, :code => nil } master = { :Local => true } begin result.each { |opt,arg| case opt when "--version" puts "%s" % Puppet.version exit when "--help" if Puppet.features.usage? RDoc::usage && exit else puts "No help available unless you have RDoc::usage installed" exit end when "--use-nodes" options[:UseNodes] = true when "--verbose" options[:verbose] = true when "--debug" options[:debug] = true when "--execute" options[:code] = arg when "--loadclasses" options[:loadclasses] = true when "--logdest" begin Puppet::Util::Log.newdestination(arg) options[:logset] = true rescue => detail $stderr.puts detail.to_s end else Puppet.settings.handlearg(opt, arg) end } rescue GetoptLong::InvalidOption => detail $stderr.puts "Try '#{$0} --help'" exit(1) end Puppet.parse_config # If noop is set, then also enable diffs if Puppet[:noop] Puppet[:show_diff] = true end unless options[:logset] Puppet::Util::Log.newdestination(:console) end client = nil server = nil Puppet.settraps if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] Puppet::Util::Log.level = :info end # Now parse the config if Puppet[:config] and File.exists? Puppet[:config] Puppet.settings.parse(Puppet[:config]) end Puppet.genconfig Puppet.genmanifest # Set our code or file to use. if options[:code] or ARGV.length == 0 Puppet[:code] = options[:code] || STDIN.read else Puppet[:manifest] = ARGV.shift end +if Puppet[:parseonly] + begin + Puppet::Parser::Interpreter.new.parser(Puppet[:environment]) + rescue => detail + Puppet.err detail + exit 1 + end + exit 0 +end + # Collect our facts. facts = Puppet::Node::Facts.find("me") facts.name = facts.values["hostname"] # Find our Node node = Puppet::Node.find_by_any_name(facts.name) # Merge in the facts. node.merge(facts.values) # Allow users to load the classes that puppetd creates. if options[:loadclasses] file = Puppet[:classfile] if FileTest.exists?(file) unless FileTest.readable?(file) $stderr.puts "%s is not readable" % file exit(63) end node.classes = File.read(file).split(/[\s\n]+/) end end begin # Compile our catalog catalog = Puppet::Node::Catalog.find(node) - exit(0) if Puppet[:parseonly] - # Translate it to a RAL catalog catalog = catalog.to_ral catalog.finalize # And apply it catalog.apply rescue => detail if Puppet[:trace] puts detail.backtrace end if detail.is_a?(XMLRPC::FaultException) $stderr.puts detail.message else $stderr.puts detail end exit(1) end diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra index 0fd644b3a..484efe83c 100644 --- a/lib/puppet/parser/grammar.ra +++ b/lib/puppet/parser/grammar.ra @@ -1,648 +1,648 @@ # vim: syntax=ruby # the parser class Puppet::Parser::Parser token LBRACK DQTEXT SQTEXT RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE token FALSE EQUALS LESSEQUAL NOTEQUAL DOT COLON LLCOLLECT RRCOLLECT token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN token IF ELSE IMPORT DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT CLASSNAME CLASSREF token NOT OR AND UNDEF PARROW rule program: statements { if val[0] # Make sure we always return an array. if val[0].is_a?(AST::ASTArray) if val[0].children.empty? result = nil else result = val[0] end else result = aryfy(val[0]) end else result = nil end } | nil statements: statement | statements statement { if val[0] and val[1] if val[0].instance_of?(AST::ASTArray) val[0].push(val[1]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[1]] end elsif obj = (val[0] || val[1]) result = obj else result = nil end } # The main list of valid statements statement: resource | virtualresource | collection | assignment | casestatement | ifstatement | import | fstatement | definition | hostclass | nodedef | resourceoverride fstatement: NAME LPAREN funcvalues RPAREN { args = aryfy(val[2]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :statement } | NAME LPAREN RPAREN { result = ast AST::Function, :name => val[0], :arguments => AST::ASTArray.new({}), :ftype => :statement } | NAME funcvalues { args = aryfy(val[1]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :statement } funcvalues: namestrings | resourcerefs namestrings: namestring | namestrings COMMA namestring { result = aryfy(val[0], val[2]) result.line = @lexer.line result.file = @lexer.file } # This is *almost* an rvalue, but I couldn't get a full # rvalue to work without scads of shift/reduce conflicts. namestring: name | variable | type | boolean | funcrvalue | selector | quotedtext | CLASSNAME { result = ast AST::Name, :value => val[0] } resourcerefs: resourceref | resourcerefs COMMA resourceref { unless val[0].is_a?(AST::ASTArray) val[0] = aryfy(val[0]) end val[0].push(val[2]) result = val[0] } resource: classname LBRACE resourceinstances endsemi RBRACE { array = val[2] if array.instance_of?(AST::ResourceInstance) array = [array] end result = ast AST::ASTArray # this iterates across each specified resourceinstance array.each { |instance| unless instance.instance_of?(AST::ResourceInstance) raise Puppet::Dev, "Got something that isn't an instance" end # now, i need to somehow differentiate between those things with # arrays in their names, and normal things result.push ast(AST::Resource, :type => val[0], :title => instance[0], :params => instance[1]) } } | classname LBRACE params endcomma RBRACE { # This is a deprecated syntax. error "All resource specifications require names" } | classref LBRACE params endcomma RBRACE { # a defaults setting for a type result = ast(AST::ResourceDefaults, :type => val[0], :params => val[2]) } # Override a value set elsewhere in the configuration. resourceoverride: resourceref LBRACE anyparams endcomma RBRACE { result = ast AST::ResourceOverride, :object => val[0], :params => val[2] } # Exported and virtual resources; these don't get sent to the client # unless they get collected elsewhere in the db. virtualresource: at resource { type = val[0] - if type == :exported and ! Puppet[:storeconfigs] + if (type == :exported and ! Puppet[:storeconfigs]) and ! Puppet[:parseonly] error "You cannot collect without storeconfigs being set" end if val[1].is_a? AST::ResourceDefaults error "Defaults are not virtualizable" end method = type.to_s + "=" # Just mark our resources as exported and pass them through. if val[1].instance_of?(AST::ASTArray) val[1].each do |obj| obj.send(method, true) end else val[1].send(method, true) end result = val[1] } at: AT { result = :virtual } | AT AT { result = :exported } # A collection statement. Currently supports no arguments at all, but eventually # will, I assume. collection: classref collectrhand { if val[0] =~ /^[a-z]/ Puppet.warning addcontext("Collection names must now be capitalized") end type = val[0].downcase args = {:type => type} if val[1].is_a?(AST::CollExpr) args[:query] = val[1] args[:query].type = type args[:form] = args[:query].form else args[:form] = val[1] end - if args[:form] == :exported and ! Puppet[:storeconfigs] + if args[:form] == :exported and ! Puppet[:storeconfigs] and ! Puppet[:parseonly] error "You cannot collect exported resources without storeconfigs being set" end result = ast AST::Collection, args } collectrhand: LCOLLECT collstatements RCOLLECT { if val[1] result = val[1] result.form = :virtual else result = :virtual end } | LLCOLLECT collstatements RRCOLLECT { if val[1] result = val[1] result.form = :exported else result = :exported end } # A mini-language for handling collection comparisons. This is organized # to avoid the need for precedence indications. collstatements: nil | collstatement | collstatements colljoin collstatement { result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] } collstatement: collexpr | LPAREN collstatements RPAREN { result = val[1] result.parens = true } colljoin: AND | OR collexpr: colllval ISEQUAL simplervalue { result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] #result = ast AST::CollExpr #result.push *val } | colllval NOTEQUAL simplervalue { result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] #result = ast AST::CollExpr #result.push *val } colllval: variable | name resourceinst: resourcename COLON params endcomma { result = ast AST::ResourceInstance, :children => [val[0],val[2]] } resourceinstances: resourceinst | resourceinstances SEMIC resourceinst { if val[0].instance_of?(AST::ResourceInstance) result = ast AST::ASTArray, :children => [val[0],val[2]] else val[0].push val[2] result = val[0] end } endsemi: # nothing | SEMIC undef: UNDEF { result = ast AST::Undef, :value => :undef } name: NAME { result = ast AST::Name, :value => val[0] } type: CLASSREF { result = ast AST::Type, :value => val[0] } resourcename: quotedtext | name | type | selector | variable | array assignment: VARIABLE EQUALS rvalue { if val[0] =~ /::/ raise Puppet::ParseError, "Cannot assign to variables in other namespaces" end # this is distinct from referencing a variable variable = ast AST::Name, :value => val[0] result = ast AST::VarDef, :name => variable, :value => val[2] } params: # nothing { result = ast AST::ASTArray } | param { result = val[0] } | params COMMA param { if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end } param: NAME FARROW rvalue { result = ast AST::ResourceParam, :param => val[0], :value => val[2] } addparam: NAME PARROW rvalue { result = ast AST::ResourceParam, :param => val[0], :value => val[2], :add => true } anyparam: param | addparam anyparams: # nothing { result = ast AST::ASTArray } | anyparam { result = val[0] } | anyparams COMMA anyparam { if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end } rvalues: rvalue | rvalues comma rvalue { if val[0].instance_of?(AST::ASTArray) result = val[0].push(val[2]) else result = ast AST::ASTArray, :children => [val[0],val[2]] end } simplervalue: quotedtext | name | type | boolean | selector | variable rvalue: quotedtext | name | type | boolean | selector | variable | array | resourceref | funcrvalue | undef # We currently require arguments in these functions. funcrvalue: NAME LPAREN funcvalues RPAREN { args = aryfy(val[2]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :rvalue } | NAME LPAREN RPAREN { result = ast AST::Function, :name => val[0], :arguments => AST::ASTArray.new({}), :ftype => :rvalue } quotedtext: DQTEXT { result = ast AST::String, :value => val[0] } | SQTEXT { result = ast AST::FlatString, :value => val[0] } boolean: BOOLEAN { result = ast AST::Boolean, :value => val[0] } resourceref: NAME LBRACK rvalue RBRACK { Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized") result = ast AST::ResourceReference, :type => val[0], :title => val[2] } | classref LBRACK rvalue RBRACK { result = ast AST::ResourceReference, :type => val[0], :title => val[2] } ifstatement: IF iftest LBRACE statements RBRACE else { args = { :test => val[1], :statements => val[3] } if val[5] args[:else] = val[5] end result = ast AST::IfStatement, args } else: # nothing | ELSE LBRACE statements RBRACE { result = ast AST::Else, :statements => val[2] } # Currently we only support a single value, but eventually one assumes # we'll support operators and such. iftest: rvalue casestatement: CASE rvalue LBRACE caseopts RBRACE { options = val[3] unless options.instance_of?(AST::ASTArray) options = ast AST::ASTArray, :children => [val[3]] end result = ast AST::CaseStatement, :test => val[1], :options => options } caseopts: caseopt | caseopts caseopt { if val[0].instance_of?(AST::ASTArray) val[0].push val[1] result = val[0] else result = ast AST::ASTArray, :children => [val[0], val[1]] end } caseopt: casevalues COLON LBRACE statements RBRACE { result = ast AST::CaseOpt, :value => val[0], :statements => val[3] } | casevalues COLON LBRACE RBRACE { result = ast(AST::CaseOpt, :value => val[0], :statements => ast(AST::ASTArray) ) } casevalues: selectlhand | casevalues COMMA selectlhand { if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end } selector: selectlhand QMARK svalues { result = ast AST::Selector, :param => val[0], :values => val[2] } svalues: selectval | LBRACE sintvalues endcomma RBRACE { result = val[1] } sintvalues: selectval | sintvalues comma selectval { if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end } selectval: selectlhand FARROW rvalue { result = ast AST::ResourceParam, :param => val[0], :value => val[2] } selectlhand: name | type | quotedtext | variable | funcrvalue | boolean | undef | DEFAULT { result = ast AST::Default, :value => val[0] } # These are only used for importing, and we don't interpolate there. qtexts: quotedtext { result = [val[0].value] } | qtexts COMMA quotedtext { results = val[0] << val[2].value } import: IMPORT qtexts { val[1].each do |file| import(file) end result = AST::ASTArray.new(:children => []) } # Disable definition inheritance for now. 8/27/06, luke #definition: DEFINE NAME argumentlist parent LBRACE statements RBRACE { definition: DEFINE classname argumentlist LBRACE statements RBRACE { 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 { newdefine classname(val[1]), :arguments => val[2] @lexer.indefine = false result = nil } #hostclass: CLASS NAME argumentlist parent LBRACE statements RBRACE { hostclass: CLASS classname classparent LBRACE statements RBRACE { # 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 { # Our class gets defined in the parent namespace, not our own. @lexer.namepop newclass classname(val[1]), :parent => val[2] result = nil } nodedef: NODE hostnames nodeparent LBRACE statements RBRACE { newnode val[1], :parent => val[2], :code => val[4] result = nil } | NODE hostnames nodeparent LBRACE RBRACE { newnode val[1], :parent => val[2] result = nil } classref: CLASSREF classname: NAME | CLASSNAME # Multiple hostnames, as used for node names. These are all literal # strings, not AST objects. hostnames: hostname | hostnames COMMA hostname { result = val[0] result = [result] unless result.is_a?(Array) result << val[2] } hostname: NAME | SQTEXT | DQTEXT | DEFAULT nil: { result = nil } nothing: { result = ast AST::ASTArray, :children => [] } argumentlist: nil | LPAREN nothing RPAREN { result = nil } | LPAREN arguments RPAREN { result = val[1] result = [result] unless result[0].is_a?(Array) } arguments: argument | arguments COMMA argument { result = val[0] result = [result] unless result[0].is_a?(Array) result << val[2] } argument: NAME EQUALS rvalue { Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0], val[2]] } | NAME { Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0]] } | VARIABLE EQUALS rvalue { result = [val[0], val[2]] } | VARIABLE { result = [val[0]] } nodeparent: nil | INHERITS hostname { result = val[1] } classparent: nil | INHERITS classnameordefault { result = val[1] } classnameordefault: classname | DEFAULT variable: VARIABLE { result = ast AST::Variable, :value => val[0] } array: LBRACK rvalues RBRACK { if val[1].instance_of?(AST::ASTArray) result = val[1] else result = ast AST::ASTArray, :children => [val[1]] end } | LBRACK RBRACK { result = ast AST::ASTArray } comma: FARROW | COMMA endcomma: # nothing | COMMA { result = nil } end ---- header ---- require 'puppet' require 'puppet/util/loadedfile' require 'puppet/parser/lexer' require 'puppet/parser/ast' #require 'puppet/parser/interpreter' module Puppet class ParseError < Puppet::Error; end class ImportError < Racc::ParseError; end class AlreadyImportedError < ImportError; end end Puppet[:typecheck] = true Puppet[:paramcheck] = true ---- inner ---- # It got too annoying having code in a file that needs to be compiled. require 'puppet/parser/parser_support' # Make emacs happy # Local Variables: # mode: ruby # End: # $Id$ diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb index 1d93193dd..d4b7449fb 100644 --- a/lib/puppet/parser/interpreter.rb +++ b/lib/puppet/parser/interpreter.rb @@ -1,90 +1,90 @@ require 'puppet' require 'timeout' require 'puppet/rails' require 'puppet/util/methodhelper' require 'puppet/parser/parser' require 'puppet/parser/compiler' require 'puppet/parser/scope' # The interpreter is a very simple entry-point class that # manages the existence of the parser (e.g., replacing it # when files are reparsed). You can feed it a node and # get the node's catalog back. class Puppet::Parser::Interpreter include Puppet::Util attr_accessor :usenodes include Puppet::Util::Errors # Determine the configuration version for a given node's environment. def configuration_version(node) parser(node.environment).version end # evaluate our whole tree def compile(node) raise Puppet::ParseError, "Could not parse configuration; cannot compile" unless env_parser = parser(node.environment) return Puppet::Parser::Compiler.new(node, env_parser).compile end # create our interpreter def initialize # The class won't always be defined during testing. if Puppet[:storeconfigs] if Puppet.features.rails? Puppet::Rails.init else raise Puppet::Error, "Rails is missing; cannot store configurations" end end @parsers = {} end + # Return the parser for a specific environment. + def parser(environment) + if ! @parsers[environment] or @parsers[environment].reparse? + # This will throw an exception if it does not succeed. We only + # want to get rid of the old parser if we successfully create a new + # one. + begin + tmp = create_parser(environment) + @parsers[environment].clear if @parsers[environment] + @parsers[environment] = tmp + rescue => detail + # If a parser already exists, than assume that we logged the + # exception elsewhere and reuse the parser. If one doesn't + # exist, then reraise. + raise detail unless @parsers[environment] + end + end + @parsers[environment] + end + private # Create a new parser object and pre-parse the configuration. def create_parser(environment) begin parser = Puppet::Parser::Parser.new(:environment => environment) if code = Puppet.settings.value(:code, environment) and code != "" parser.string = code else file = Puppet.settings.value(:manifest, environment) parser.file = file end parser.parse return parser rescue => detail msg = "Could not parse" if environment and environment != "" msg += " for environment %s" % environment end msg += ": %s" % detail.to_s error = Puppet::Error.new(msg) error.set_backtrace(detail.backtrace) raise error end end - - # Return the parser for a specific environment. - def parser(environment) - if ! @parsers[environment] or @parsers[environment].reparse? - # This will throw an exception if it does not succeed. We only - # want to get rid of the old parser if we successfully create a new - # one. - begin - tmp = create_parser(environment) - @parsers[environment].clear if @parsers[environment] - @parsers[environment] = tmp - rescue => detail - # If a parser already exists, than assume that we logged the - # exception elsewhere and reuse the parser. If one doesn't - # exist, then reraise. - raise detail unless @parsers[environment] - end - end - @parsers[environment] - end end diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb index 6661650ba..51026ea1b 100644 --- a/lib/puppet/parser/lexer.rb +++ b/lib/puppet/parser/lexer.rb @@ -1,437 +1,441 @@ # the scanner/lexer require 'strscan' require 'puppet' module Puppet class LexError < RuntimeError; end end module Puppet::Parser; end class Puppet::Parser::Lexer attr_reader :last, :file attr_accessor :line, :indefine # Our base token class. class Token attr_accessor :regex, :name, :string, :skip, :incr_line, :skip_text def initialize(regex, name) if regex.is_a?(String) @name, @string = name, regex @regex = Regexp.new(Regexp.escape(@string)) else @name, @regex = name, regex end end def skip? self.skip end def to_s - "Lexer token %s" % @name.to_s + if self.string + @string + else + @name.to_s + end end end # Maintain a list of tokens. class TokenList attr_reader :regex_tokens, :string_tokens def [](name) @tokens[name] end # Create a new token. def add_token(name, regex, options = {}, &block) token = Token.new(regex, name) raise(ArgumentError, "Token %s already exists" % name) if @tokens.include?(name) @tokens[token.name] = token if token.string @string_tokens << token @tokens_by_string[token.string] = token else @regex_tokens << token end options.each do |name, option| token.send(name.to_s + "=", option) end token.meta_def(:convert, &block) if block_given? token end def initialize @tokens = {} @regex_tokens = [] @string_tokens = [] @tokens_by_string = {} end # Look up a token by its value, rather than name. def lookup(string) @tokens_by_string[string] end # Define more tokens. def add_tokens(hash) hash.each do |regex, name| add_token(name, regex) end end # Sort our tokens by length, so we know once we match, we're done. # This helps us avoid the O(n^2) nature of token matching. def sort_tokens @string_tokens.sort! { |a, b| b.string.length <=> a.string.length } end end TOKENS = TokenList.new TOKENS.add_tokens( '[' => :LBRACK, ']' => :RBRACK, '{' => :LBRACE, '}' => :RBRACE, '(' => :LPAREN, ')' => :RPAREN, '=' => :EQUALS, '==' => :ISEQUAL, '>=' => :GREATEREQUAL, '>' => :GREATERTHAN, '<' => :LESSTHAN, '<=' => :LESSEQUAL, '!=' => :NOTEQUAL, '!' => :NOT, ',' => :COMMA, '.' => :DOT, ':' => :COLON, '@' => :AT, '<<|' => :LLCOLLECT, '|>>' => :RRCOLLECT, '<|' => :LCOLLECT, '|>' => :RCOLLECT, ';' => :SEMIC, '?' => :QMARK, '\\' => :BACKSLASH, '=>' => :FARROW, '+>' => :PARROW, %r{([a-z][-\w]*::)+[a-z][-\w]*} => :CLASSNAME, %r{((::){0,1}[A-Z][-\w]*)+} => :CLASSREF ) TOKENS.add_tokens "Whatever" => :DQTEXT, "Nomatter" => :SQTEXT, "alsonomatter" => :BOOLEAN TOKENS.add_token :NAME, %r{[a-z][-\w]*} do |lexer, value| string_token = self # we're looking for keywords here if tmp = KEYWORDS.lookup(value) string_token = tmp if [:TRUE, :FALSE].include?(string_token.name) value = eval(value) string_token = TOKENS[:BOOLEAN] end end [string_token, value] end TOKENS.add_token :NUMBER, %r{[0-9]+} do |lexer, value| [TOKENS[:NAME], value] end TOKENS.add_token :COMMENT, %r{#.*}, :skip => true TOKENS.add_token :RETURN, "\n", :skip => true, :incr_line => true, :skip_text => true TOKENS.add_token :SQUOTE, "'" do |lexer, value| value = lexer.slurpstring(value) [TOKENS[:SQTEXT], value] end TOKENS.add_token :DQUOTE, '"' do |lexer, value| value = lexer.slurpstring(value) [TOKENS[:DQTEXT], value] end TOKENS.add_token :VARIABLE, %r{\$(\w*::)*\w+} do |lexer, value| value = value.sub(/^\$/, '') [self, value] end TOKENS.sort_tokens @@pairs = { "{" => "}", "(" => ")", "[" => "]", "<|" => "|>", "<<|" => "|>>" } KEYWORDS = TokenList.new KEYWORDS.add_tokens( "case" => :CASE, "class" => :CLASS, "default" => :DEFAULT, "define" => :DEFINE, "import" => :IMPORT, "if" => :IF, "elsif" => :ELSIF, "else" => :ELSE, "inherits" => :INHERITS, "node" => :NODE, "and" => :AND, "or" => :OR, "undef" => :UNDEF, "false" => :FALSE, "true" => :TRUE ) def clear initvars end def expected return nil if @expected.empty? name = @expected[-1] raise "Could not find expected token %s" % name unless token = TOKENS.lookup(name) return token end # scan the whole file # basically just used for testing def fullscan array = [] self.scan { |token, str| # Ignore any definition nesting problems @indefine = false array.push([token,str]) } return array end # this is probably pretty damned inefficient... # it'd be nice not to have to load the whole file first... def file=(file) @file = file @line = 1 File.open(file) { |of| str = "" of.each { |line| str += line } @scanner = StringScanner.new(str) } end def find_string_token matched_token = value = nil # We know our longest string token is three chars, so try each size in turn # until we either match or run out of chars. This way our worst-case is three # tries, where it is otherwise the number of string chars we have. Also, # the lookups are optimized hash lookups, instead of regex scans. [3, 2, 1].each do |i| str = @scanner.peek(i) if matched_token = TOKENS.lookup(str) value = @scanner.scan(matched_token.regex) break end end return matched_token, value end # Find the next token that matches a regex. We look for these first. def find_regex_token @regex += 1 matched_token = nil value = "" length = 0 # I tried optimizing based on the first char, but it had # a slightly negative affect and was a good bit more complicated. TOKENS.regex_tokens.each do |token| next unless match_length = @scanner.match?(token.regex) # We've found a longer match if match_length > length value = @scanner.scan(token.regex) length = value.length matched_token = token end end return matched_token, value end # Find the next token, returning the string and the token. def find_token @find += 1 matched_token, value = find_regex_token unless matched_token matched_token, value = find_string_token end return matched_token, value end def indefine? if defined? @indefine @indefine else false end end def initialize @find = 0 @regex = 0 initvars() end def initvars @line = 1 @previous_token = nil @scanner = nil @file = nil # AAARRGGGG! okay, regexes in ruby are bloody annoying # no one else has "\n" =~ /\s/ @skip = %r{[ \t]+} @namestack = [] @indefine = false @expected = [] end # Make any necessary changes to the token and/or value. def munge_token(token, value) @line += 1 if token.incr_line skip() if token.skip_text return if token.skip token, value = token.convert(self, value) if token.respond_to?(:convert) return unless token return token, value end # Go up one in the namespace. def namepop @namestack.pop end # Collect the current namespace. def namespace @namestack.join("::") end # This value might have :: in it, but we don't care -- it'll be # handled normally when joining, and when popping we want to pop # this full value, however long the namespace is. def namestack(value) @namestack << value end def rest @scanner.rest end # this is the heart of the lexer def scan #Puppet.debug("entering scan") raise Puppet::LexError.new("Invalid or empty string") unless @scanner # Skip any initial whitespace. skip() until @scanner.eos? do yielded = false matched_token, value = find_token # error out if we didn't match anything at all if matched_token.nil? nword = nil # Try to pull a 'word' out of the remaining string. if @scanner.rest =~ /^(\S+)/ nword = $1 elsif @scanner.rest =~ /^(\s+)/ nword = $1 else nword = @scanner.rest end raise "Could not match '%s'" % nword end final_token, value = munge_token(matched_token, value) next unless final_token if match = @@pairs[value] and final_token.name != :DQUOTE and final_token.name != :SQUOTE @expected << match elsif exp = @expected[-1] and exp == value and final_token.name != :DQUOTE and final_token.name != :SQUOTE @expected.pop end yield [final_token.name, value] if @previous_token namestack(value) if @previous_token.name == :CLASS if @previous_token.name == :DEFINE if indefine? msg = "Cannot nest definition %s inside %s" % [value, @indefine] self.indefine = false raise Puppet::ParseError, msg end @indefine = value end end @previous_token = final_token skip() end @scanner = nil # This indicates that we're done parsing. yield [false,false] end # Skip any skipchars in our remaining string. def skip @scanner.skip(@skip) end # we've encountered an opening quote... # slurp in the rest of the string and return it def slurpstring(quote) # we search for the next quote that isn't preceded by a # backslash; the caret is there to match empty strings str = @scanner.scan_until(/([^\\]|^)#{quote}/) if str.nil? raise Puppet::LexError.new("Unclosed quote after '%s' in '%s'" % [self.last,self.rest]) else str.sub!(/#{quote}\Z/,"") str.gsub!(/\\#{quote}/,quote) end return str end # just parse a string, not a whole file def string=(string) @scanner = StringScanner.new(string) end end diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb index c3279d4e7..e27a209fc 100644 --- a/lib/puppet/parser/parser.rb +++ b/lib/puppet/parser/parser.rb @@ -1,1721 +1,1722 @@ # # DO NOT MODIFY!!!! # This file is automatically generated by racc 1.4.5 # from racc grammer file "grammar.ra". # require 'racc/parser' require 'puppet' require 'puppet/util/loadedfile' require 'puppet/parser/lexer' require 'puppet/parser/ast' #require 'puppet/parser/interpreter' module Puppet class ParseError < Puppet::Error; end class ImportError < Racc::ParseError; end class AlreadyImportedError < ImportError; end end Puppet[:typecheck] = true Puppet[:paramcheck] = true module Puppet module Parser class Parser < Racc::Parser -module_eval <<'..end grammar.ra modeval..id9134b179f4', 'grammar.ra', 638 +module_eval <<'..end grammar.ra modeval..idfef5d70c9f', 'grammar.ra', 638 # It got too annoying having code in a file that needs to be compiled. require 'puppet/parser/parser_support' # Make emacs happy # Local Variables: # mode: ruby # End: +# $Id$ -..end grammar.ra modeval..id9134b179f4 +..end grammar.ra modeval..idfef5d70c9f ##### racc 1.4.5 generates ### racc_reduce_table = [ 0, 0, :racc_error, 1, 52, :_reduce_1, 1, 52, :_reduce_none, 1, 53, :_reduce_none, 2, 53, :_reduce_4, 1, 55, :_reduce_none, 1, 55, :_reduce_none, 1, 55, :_reduce_none, 1, 55, :_reduce_none, 1, 55, :_reduce_none, 1, 55, :_reduce_none, 1, 55, :_reduce_none, 1, 55, :_reduce_none, 1, 55, :_reduce_none, 1, 55, :_reduce_none, 1, 55, :_reduce_none, 1, 55, :_reduce_none, 4, 63, :_reduce_17, 3, 63, :_reduce_18, 2, 63, :_reduce_19, 1, 68, :_reduce_none, 1, 68, :_reduce_none, 1, 69, :_reduce_none, 3, 69, :_reduce_23, 1, 71, :_reduce_none, 1, 71, :_reduce_none, 1, 71, :_reduce_none, 1, 71, :_reduce_none, 1, 71, :_reduce_none, 1, 71, :_reduce_none, 1, 71, :_reduce_none, 1, 71, :_reduce_31, 1, 70, :_reduce_none, 3, 70, :_reduce_33, 5, 56, :_reduce_34, 5, 56, :_reduce_35, 5, 56, :_reduce_36, 5, 67, :_reduce_37, 2, 57, :_reduce_38, 1, 87, :_reduce_39, 2, 87, :_reduce_40, 2, 58, :_reduce_41, 3, 88, :_reduce_42, 3, 88, :_reduce_43, 1, 89, :_reduce_none, 1, 89, :_reduce_none, 3, 89, :_reduce_46, 1, 90, :_reduce_none, 3, 90, :_reduce_48, 1, 91, :_reduce_none, 1, 91, :_reduce_none, 3, 92, :_reduce_51, 3, 92, :_reduce_52, 1, 93, :_reduce_none, 1, 93, :_reduce_none, 4, 95, :_reduce_55, 1, 81, :_reduce_none, 3, 81, :_reduce_57, 0, 82, :_reduce_none, 1, 82, :_reduce_none, 1, 97, :_reduce_60, 1, 72, :_reduce_61, 1, 74, :_reduce_62, 1, 96, :_reduce_none, 1, 96, :_reduce_none, 1, 96, :_reduce_none, 1, 96, :_reduce_none, 1, 96, :_reduce_none, 1, 96, :_reduce_none, 3, 59, :_reduce_69, 0, 83, :_reduce_70, 1, 83, :_reduce_71, 3, 83, :_reduce_72, 3, 100, :_reduce_73, 3, 101, :_reduce_74, 1, 102, :_reduce_none, 1, 102, :_reduce_none, 0, 86, :_reduce_77, 1, 86, :_reduce_78, 3, 86, :_reduce_79, 1, 103, :_reduce_none, 3, 103, :_reduce_81, 1, 94, :_reduce_none, 1, 94, :_reduce_none, 1, 94, :_reduce_none, 1, 94, :_reduce_none, 1, 94, :_reduce_none, 1, 94, :_reduce_none, 1, 99, :_reduce_none, 1, 99, :_reduce_none, 1, 99, :_reduce_none, 1, 99, :_reduce_none, 1, 99, :_reduce_none, 1, 99, :_reduce_none, 1, 99, :_reduce_none, 1, 99, :_reduce_none, 1, 99, :_reduce_none, 1, 99, :_reduce_none, 4, 76, :_reduce_98, 3, 76, :_reduce_99, 1, 78, :_reduce_100, 1, 78, :_reduce_101, 1, 75, :_reduce_102, 4, 79, :_reduce_103, 4, 79, :_reduce_104, 6, 61, :_reduce_105, 0, 106, :_reduce_none, 4, 106, :_reduce_107, 1, 105, :_reduce_none, 5, 60, :_reduce_109, 1, 107, :_reduce_none, 2, 107, :_reduce_111, 5, 108, :_reduce_112, 4, 108, :_reduce_113, 1, 109, :_reduce_none, 3, 109, :_reduce_115, 3, 77, :_reduce_116, 1, 111, :_reduce_none, 4, 111, :_reduce_118, 1, 113, :_reduce_none, 3, 113, :_reduce_120, 3, 112, :_reduce_121, 1, 110, :_reduce_none, 1, 110, :_reduce_none, 1, 110, :_reduce_none, 1, 110, :_reduce_none, 1, 110, :_reduce_none, 1, 110, :_reduce_none, 1, 110, :_reduce_none, 1, 110, :_reduce_129, 1, 114, :_reduce_130, 3, 114, :_reduce_131, 2, 62, :_reduce_132, 6, 64, :_reduce_133, 5, 64, :_reduce_134, 6, 65, :_reduce_135, 5, 65, :_reduce_136, 6, 66, :_reduce_137, 5, 66, :_reduce_138, 1, 85, :_reduce_none, 1, 80, :_reduce_none, 1, 80, :_reduce_none, 1, 117, :_reduce_none, 3, 117, :_reduce_143, 1, 119, :_reduce_none, 1, 119, :_reduce_none, 1, 119, :_reduce_none, 1, 119, :_reduce_none, 0, 54, :_reduce_148, 0, 120, :_reduce_149, 1, 115, :_reduce_none, 3, 115, :_reduce_151, 3, 115, :_reduce_152, 1, 121, :_reduce_none, 3, 121, :_reduce_154, 3, 122, :_reduce_155, 1, 122, :_reduce_156, 3, 122, :_reduce_157, 1, 122, :_reduce_158, 1, 118, :_reduce_none, 2, 118, :_reduce_160, 1, 116, :_reduce_none, 2, 116, :_reduce_162, 1, 123, :_reduce_none, 1, 123, :_reduce_none, 1, 73, :_reduce_165, 3, 98, :_reduce_166, 2, 98, :_reduce_167, 1, 104, :_reduce_none, 1, 104, :_reduce_none, 0, 84, :_reduce_none, 1, 84, :_reduce_171 ] racc_reduce_n = 172 racc_shift_n = 276 racc_action_table = [ 71, 51, 53, 184, 162, 102, 71, 51, 53, 132, 149, 5, 209, 47, -127, 87, 71, 51, 53, 88, 208, 65, 71, 51, 53, 162, 144, 34, 35, 99, 65, 89, 126, -127, 50, 54, 65, 126, 59, -123, 50, 54, 207, 45, 59, 161, 65, 56, -126, 45, 50, 54, 65, 56, 59, 90, 50, 54, 185, 45, 59, 36, 169, 56, 37, 45, 71, 51, 53, 56, 204, 206, 47, 51, 53, 200, 149, 51, 53, 33, 199, -122, 71, 51, 53, 238, 1, 65, 71, 51, 53, 67, 144, 34, 35, 87, 65, 237, 234, -123, 50, 54, 65, 200, 59, 233, 50, 54, 199, 45, 59, 252, 65, 56, 43, 45, 50, 54, 65, 56, 59, 33, 50, 54, 156, 45, 59, 36, 1, 56, 37, 45, 71, 51, 53, 56, 204, 206, 71, 51, 53, 149, 149, 34, 35, 188, 259, 196, 71, 51, 53, -125, 65, 65, 71, 51, 53, 144, 144, 51, 53, 202, 65, 162, 33, 168, 50, 54, 65, 137, 59, 1, 50, 54, 168, 45, 59, 36, 65, 56, 37, 45, 50, 174, 65, 56, 59, -122, 50, 54, 165, 111, 59, -125, -122, 56, 127, 45, 71, 51, 53, 56, 33, 211, 71, 51, 53, 204, 206, 1, 5, 135, 218, 186, 71, 51, 53, 188, 189, 106, 125, 51, 53, 221, 222, 231, 140, 140, 65, 225, 126, -124, 50, 54, 65, 228, 59, 47, 50, 54, -139, 45, 59, 123, 65, 56, 106, 45, 50, 112, 65, 56, 59, -124, 50, 174, 134, 111, 59, 51, 53, 56, 164, 111, 239, 51, 53, 56, 240, 241, 242, -124, 96, 140, -126, 51, 53, 93, 179, 168, 91, 51, 53, 159, 181, 260, 262, 85, 65, 157, 88, 133, 50, 174, 65, 41, 59, -124, 50, 54, 128, 111, 59, -122, 65, 56, 43, 45, 50, 174, 65, 56, 59, -125, 50, 54, -123, 111, 59, 51, 53, 56, 43, 45, -127, 51, 53, 56, 176, 40, 269, -171, 270, -128, -123, 51, 53, 152, -125, nil, nil, 51, 53, nil, nil, nil, nil, nil, 65, nil, nil, nil, 50, 174, 65, nil, 59, nil, 50, 174, 43, 111, 59, nil, 65, 56, nil, 111, 50, 174, 65, 56, 59, 254, 50, 174, nil, 111, 59, 51, 53, 56, nil, 111, nil, 51, 53, 56, nil, nil, nil, nil, nil, 15, nil, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, 65, nil, 1, 5, 50, 174, 65, 264, 59, nil, 50, 174, nil, 111, 59, nil, nil, 56, nil, 111, nil, nil, nil, 56, nil, nil, 271, 15, nil, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, 15, 212, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, nil, 230, 15, nil, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, 15, 253, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, nil, 214, 15, nil, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, 15, 274, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, nil, 235, 15, nil, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, 15, 275, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, nil, nil, 15, nil, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, 15, nil, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, 15, nil, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, 15, nil, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5, 15, nil, 19, 21, nil, 28, 2, nil, 8, nil, 13, nil, 17, nil, 22, nil, nil, 1, 5 ] racc_action_check = [ 93, 93, 93, 127, 106, 38, 71, 71, 71, 71, 90, 127, 146, 54, 78, 23, 87, 87, 87, 23, 146, 90, 161, 161, 161, 112, 90, 102, 102, 38, 93, 23, 54, 64, 93, 93, 71, 112, 93, 62, 71, 71, 142, 93, 71, 106, 87, 93, 66, 71, 87, 87, 161, 71, 87, 23, 161, 161, 129, 87, 161, 102, 119, 87, 102, 161, 240, 240, 240, 161, 142, 142, 13, 13, 13, 238, 149, 135, 135, 2, 238, 74, 15, 15, 15, 197, 2, 149, 17, 17, 17, 13, 149, 99, 99, 61, 240, 197, 194, 118, 240, 240, 13, 137, 240, 194, 13, 13, 137, 240, 13, 210, 15, 240, 13, 13, 15, 15, 17, 13, 15, 96, 17, 17, 96, 15, 17, 99, 96, 15, 99, 17, 239, 239, 239, 17, 210, 210, 162, 162, 162, 89, 205, 8, 8, 227, 227, 136, 165, 165, 165, 116, 89, 205, 47, 47, 47, 89, 205, 19, 19, 139, 239, 140, 21, 141, 239, 239, 162, 84, 239, 21, 162, 162, 115, 239, 162, 8, 165, 239, 8, 162, 165, 165, 47, 162, 165, 114, 47, 47, 113, 165, 47, 60, 58, 165, 55, 47, 225, 225, 225, 47, 29, 151, 187, 187, 187, 151, 151, 29, 29, 83, 163, 131, 41, 41, 41, 131, 131, 164, 52, 192, 192, 166, 167, 192, 168, 169, 225, 170, 174, 46, 225, 225, 187, 182, 225, 184, 187, 187, 45, 225, 187, 42, 41, 225, 40, 187, 41, 41, 192, 187, 41, 110, 192, 192, 81, 41, 192, 208, 208, 41, 107, 192, 199, 126, 126, 192, 200, 201, 203, 70, 32, 88, 79, 209, 209, 28, 124, 224, 25, 67, 67, 100, 126, 230, 233, 22, 208, 97, 95, 73, 208, 208, 126, 11, 208, 245, 126, 126, 67, 208, 126, 246, 209, 208, 126, 126, 209, 209, 67, 126, 209, 248, 67, 67, 249, 209, 67, 125, 125, 209, 67, 67, 250, 123, 123, 67, 123, 9, 258, 259, 260, 77, 76, 176, 176, 91, 75, nil, nil, 134, 134, nil, nil, nil, nil, nil, 125, nil, nil, nil, 125, 125, 123, nil, 125, nil, 123, 123, 125, 125, 123, nil, 176, 125, nil, 123, 176, 176, 134, 123, 176, 215, 134, 134, nil, 176, 134, 234, 234, 176, nil, 134, nil, 257, 257, 134, nil, nil, nil, nil, nil, 215, nil, 215, 215, nil, 215, 215, nil, 215, nil, 215, nil, 215, nil, 215, 234, nil, 215, 215, 234, 234, 257, 236, 234, nil, 257, 257, nil, 234, 257, nil, nil, 234, nil, 257, nil, nil, nil, 257, nil, nil, 262, 236, nil, 236, 236, nil, 236, 236, nil, 236, nil, 236, nil, 236, nil, 236, nil, nil, 236, 236, 262, 157, 262, 262, nil, 262, 262, nil, 262, nil, 262, nil, 262, nil, 262, nil, nil, 262, 262, nil, 190, 157, nil, 157, 157, nil, 157, 157, nil, 157, nil, 157, nil, 157, nil, 157, nil, nil, 157, 157, 190, 213, 190, 190, nil, 190, 190, nil, 190, nil, 190, nil, 190, nil, 190, nil, nil, 190, 190, nil, 159, 213, nil, 213, 213, nil, 213, 213, nil, 213, nil, 213, nil, 213, nil, 213, nil, nil, 213, 213, 159, 272, 159, 159, nil, 159, 159, nil, 159, nil, 159, nil, 159, nil, 159, nil, nil, 159, 159, nil, 196, 272, nil, 272, 272, nil, 272, 272, nil, 272, nil, 272, nil, 272, nil, 272, nil, nil, 272, 272, 196, 273, 196, 196, nil, 196, 196, nil, 196, nil, 196, nil, 196, nil, 196, nil, nil, 196, 196, nil, nil, 273, nil, 273, 273, nil, 273, 273, nil, 273, nil, 273, nil, 273, nil, 273, nil, nil, 273, 273, 133, nil, 133, 133, nil, 133, 133, nil, 133, nil, 133, nil, 133, nil, 133, nil, nil, 133, 133, 270, nil, 270, 270, nil, 270, 270, nil, 270, nil, 270, nil, 270, nil, 270, nil, nil, 270, 270, 27, nil, 27, 27, nil, 27, 27, nil, 27, nil, 27, nil, 27, nil, 27, nil, nil, 27, 27, 0, nil, 0, 0, nil, 0, 0, nil, 0, nil, 0, nil, 0, nil, 0, nil, nil, 0, 0 ] racc_action_pointer = [ 644, nil, 42, nil, nil, nil, nil, nil, 140, 323, nil, 289, nil, 70, nil, 80, nil, 86, nil, 156, nil, 127, 246, 13, nil, 280, nil, 625, 264, 165, nil, nil, 238, nil, nil, nil, nil, nil, -5, nil, 209, 212, 223, nil, nil, 238, 211, 152, nil, nil, nil, nil, 210, nil, 11, 186, nil, nil, 174, nil, 173, 93, 19, nil, 13, nil, 28, 278, nil, nil, 251, 4, nil, 285, 61, 318, 314, 313, -6, 254, nil, 250, nil, 201, 148, nil, nil, 14, 236, 120, -11, 337, nil, -2, nil, 284, 84, 283, nil, 90, 277, nil, 24, nil, nil, nil, -5, 252, nil, nil, 233, nil, 16, 152, 167, 164, 131, nil, 79, 45, nil, nil, nil, 322, 273, 316, 262, -34, nil, 36, nil, 208, nil, 587, 338, 74, 141, 71, nil, 156, 154, 155, 23, nil, nil, nil, -3, nil, nil, 55, nil, 160, nil, nil, nil, nil, nil, 448, nil, 507, nil, 20, 136, 205, 182, 146, 216, 217, 189, 190, 220, nil, nil, nil, 209, nil, 332, nil, nil, nil, nil, nil, 213, nil, 235, nil, nil, 202, nil, nil, 467, nil, 218, nil, 88, nil, 547, 75, nil, 251, 255, 247, nil, 263, nil, 121, nil, nil, 256, 272, 89, nil, nil, 488, nil, 366, nil, nil, nil, nil, nil, nil, nil, nil, 269, 196, nil, 136, nil, nil, 257, nil, nil, 280, 376, nil, 408, nil, 43, 130, 64, nil, nil, nil, nil, 277, 283, nil, 293, 296, 304, nil, nil, nil, nil, nil, nil, 382, 323, 324, 326, nil, 427, nil, nil, nil, nil, nil, nil, nil, 606, nil, 528, 568, nil, nil ] racc_action_default = [ -148, -141, -172, -15, -3, -139, -16, -5, -172, -172, -6, -172, -7, -140, -8, -172, -9, -172, -10, -172, -11, -172, -39, -172, -12, -172, -13, -1, -172, -172, -14, -2, -148, -140, -146, -145, -144, -147, -148, -142, -77, -70, -172, -31, -29, -62, -30, -172, -32, -19, -102, -100, -20, -101, -61, -21, -60, -22, -24, -129, -25, -172, -26, -128, -27, -165, -28, -172, -108, -92, -88, -172, -95, -172, -89, -93, -90, -97, -91, -96, -94, -172, -130, -132, -148, -40, -41, -172, -70, -148, -148, -172, -4, -172, -38, -172, -172, -172, -161, -172, -172, -159, -172, -75, -76, -78, -172, -170, -66, -71, -63, -62, -61, -58, -64, -170, -67, -56, -65, -172, -127, -126, -68, -172, -172, -172, -172, -172, -18, -172, -80, -172, -167, -172, -172, -172, -172, -149, -150, -172, -172, -170, -172, -45, -61, -47, -172, -54, -53, -148, -44, -172, 276, -69, -162, -163, -164, -172, -160, -172, -143, -172, -172, -172, -171, -59, -172, -172, -171, -70, -172, -124, -116, -117, -61, -122, -172, -125, -123, -103, -23, -99, -172, -33, -172, -17, -166, -172, -168, -169, -172, -114, -172, -110, -172, -131, -172, -172, -153, -156, -158, -172, -104, -172, -50, -172, -49, -43, -172, -172, -172, -42, -136, -172, -138, -172, -74, -73, -37, -79, -57, -34, -35, -72, -170, -172, -119, -170, -98, -81, -106, -109, -111, -172, -172, -134, -172, -152, -172, -172, -172, -151, -36, -46, -86, -82, -83, -51, -87, -84, -85, -52, -48, -135, -137, -55, -121, -172, -172, -169, -172, -105, -172, -115, -133, -154, -155, -157, -120, -118, -172, -113, -172, -172, -112, -107 ] racc_goto_table = [ 23, 27, 122, 92, 173, 105, 198, 49, 117, 46, 187, 39, 31, 170, 32, 82, 193, 103, 115, 25, 142, 151, 243, 44, 191, 247, 251, 23, 131, 95, 64, 86, 73, 84, 261, 192, 107, 110, 166, 172, 113, 227, 83, 62, 98, 163, 136, 97, 38, 100, 101, 108, 180, 167, 201, 197, 94, 226, 120, 154, nil, 129, nil, 46, 66, 141, 170, nil, nil, nil, nil, 118, nil, nil, 232, nil, nil, 44, nil, 203, 210, nil, 191, nil, 64, nil, nil, nil, nil, nil, nil, nil, 121, nil, nil, nil, 138, 62, nil, nil, nil, nil, 158, nil, nil, 160, 257, 265, 155, nil, nil, nil, nil, nil, nil, nil, nil, nil, 66, 171, 182, 46, 46, nil, 263, nil, 122, nil, nil, 219, 171, 195, 220, 23, 190, 44, 44, nil, 268, nil, 120, 103, 64, 64, nil, 223, 224, 170, nil, nil, nil, 120, nil, 178, nil, 62, 62, 23, 213, 23, 215, 110, 255, nil, 178, 258, 92, nil, nil, nil, 60, nil, 171, nil, 121, 108, 66, 66, nil, nil, 58, nil, 120, nil, nil, 121, nil, nil, 171, 92, 23, 92, nil, 120, nil, 118, 23, 236, 116, nil, nil, nil, nil, nil, 245, 245, 178, nil, 114, 120, nil, nil, 92, 23, nil, 23, 121, nil, 244, 244, nil, nil, 178, nil, 60, 250, 250, 121, nil, nil, 171, nil, nil, nil, 58, nil, 23, nil, 249, 249, nil, nil, nil, 121, nil, nil, 148, 148, 92, 92, nil, 120, nil, 171, nil, nil, 147, 147, nil, 121, 121, nil, 23, 272, 178, nil, nil, nil, nil, nil, 23, 273, 23, 23, 120, 68, nil, 81, nil, 77, 177, 77, 60, 60, 48, 121, 72, 178, 72, nil, 175, 177, 58, 58, nil, nil, nil, nil, nil, nil, nil, 175, nil, nil, nil, nil, 148, 124, 121, nil, nil, 77, nil, nil, nil, nil, 147, nil, 72, nil, nil, nil, 116, nil, nil, nil, nil, nil, nil, nil, nil, 130, 114, 177, nil, 77, nil, nil, 48, nil, nil, nil, 72, 175, nil, nil, nil, 139, nil, 177, nil, 77, nil, 153, nil, nil, nil, 77, 72, 175, nil, nil, 148, nil, 72, 248, 248, nil, nil, nil, nil, nil, 147, nil, nil, 246, 246, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 177, nil, nil, nil, nil, nil, 48, 183, nil, nil, 175, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 177, nil, nil, nil, nil, nil, nil, 216, 217, nil, 175, 77, 77, nil, nil, nil, nil, nil, 72, 72, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 229, nil, nil, nil, 77, nil, nil, nil, nil, nil, nil, 72, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 256, nil, nil, nil, 77, nil, nil, nil, nil, nil, nil, 72, nil, nil, 266, 267, nil, nil, 77, 77, nil, nil, nil, nil, nil, 72, 72 ] racc_goto_check = [ 34, 2, 47, 4, 61, 51, 71, 17, 44, 27, 53, 68, 3, 59, 29, 27, 57, 49, 32, 1, 38, 38, 39, 26, 59, 43, 43, 34, 52, 34, 24, 37, 54, 29, 55, 56, 35, 27, 31, 60, 30, 62, 63, 23, 3, 33, 64, 65, 66, 67, 3, 26, 20, 33, 69, 70, 5, 61, 24, 72, nil, 17, nil, 27, 25, 32, 59, nil, nil, nil, nil, 23, nil, nil, 57, nil, nil, 26, nil, 33, 38, nil, 59, nil, 24, nil, nil, nil, nil, nil, nil, nil, 25, nil, nil, nil, 3, 23, nil, nil, nil, nil, 68, nil, nil, 68, 53, 71, 29, nil, nil, nil, nil, nil, nil, nil, nil, nil, 25, 27, 17, 27, 27, nil, 59, nil, 47, nil, nil, 51, 27, 27, 44, 34, 2, 26, 26, nil, 61, nil, 24, 49, 24, 24, nil, 49, 32, 59, nil, nil, nil, 24, nil, 23, nil, 23, 23, 34, 2, 34, 2, 27, 33, nil, 23, 33, 4, nil, nil, nil, 22, nil, 27, nil, 25, 26, 25, 25, nil, nil, 21, nil, 24, nil, nil, 25, nil, nil, 27, 4, 34, 4, nil, 24, nil, 23, 34, 2, 22, nil, nil, nil, nil, nil, 27, 27, 23, nil, 21, 24, nil, nil, 4, 34, nil, 34, 25, nil, 26, 26, nil, nil, 23, nil, 22, 24, 24, 25, nil, nil, 27, nil, nil, nil, 21, nil, 34, nil, 23, 23, nil, nil, nil, 25, nil, nil, 22, 22, 4, 4, nil, 24, nil, 27, nil, nil, 21, 21, nil, 25, 25, nil, 34, 2, 23, nil, nil, nil, nil, nil, 34, 2, 34, 34, 24, 48, nil, 48, nil, 46, 22, 46, 22, 22, 28, 25, 28, 23, 28, nil, 21, 22, 21, 21, nil, nil, nil, nil, nil, nil, nil, 21, nil, nil, nil, nil, 22, 48, 25, nil, nil, 46, nil, nil, nil, nil, 21, nil, 28, nil, nil, nil, 22, nil, nil, nil, nil, nil, nil, nil, nil, 48, 21, 22, nil, 46, nil, nil, 28, nil, nil, nil, 28, 21, nil, nil, nil, 48, nil, 22, nil, 46, nil, 48, nil, nil, nil, 46, 28, 21, nil, nil, 22, nil, 28, 22, 22, nil, nil, nil, nil, nil, 21, nil, nil, 21, 21, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 22, nil, nil, nil, nil, nil, 28, 28, nil, nil, 21, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 22, nil, nil, nil, nil, nil, nil, 48, 48, nil, 21, 46, 46, nil, nil, nil, nil, nil, 28, 28, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 48, nil, nil, nil, 46, nil, nil, nil, nil, nil, nil, 28, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 48, nil, nil, nil, 46, nil, nil, nil, nil, nil, nil, 28, nil, nil, 48, 48, nil, nil, 46, 46, nil, nil, nil, nil, nil, 28, 28 ] racc_goto_pointer = [ nil, 19, 1, 12, -24, 27, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, -6, nil, nil, -73, 167, 157, 30, 17, 51, 10, -4, 271, 12, -1, -75, -23, -62, 0, -4, nil, 8, -69, -183, nil, nil, nil, -183, -33, nil, 264, -39, 260, -23, nil, -35, -43, -121, 17, -196, -99, -118, nil, -110, -84, -119, -135, 23, -38, 15, 40, 11, 3, -83, -82, -131, -37 ] racc_goto_default = [ nil, nil, nil, 150, 4, 7, 10, 12, 14, 16, 18, 20, 24, 26, 30, 3, 6, nil, 52, 55, 57, 74, 75, 76, 78, 79, 69, 70, 9, 11, nil, nil, nil, nil, 61, nil, 29, nil, nil, 143, 205, 145, 146, nil, nil, 119, 63, 80, nil, 109, 104, nil, nil, nil, nil, nil, nil, nil, 194, 42, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil ] racc_token_table = { false => 0, Object.new => 1, :LBRACK => 2, :DQTEXT => 3, :SQTEXT => 4, :RBRACK => 5, :LBRACE => 6, :RBRACE => 7, :SYMBOL => 8, :FARROW => 9, :COMMA => 10, :TRUE => 11, :FALSE => 12, :EQUALS => 13, :LESSEQUAL => 14, :NOTEQUAL => 15, :DOT => 16, :COLON => 17, :LLCOLLECT => 18, :RRCOLLECT => 19, :QMARK => 20, :LPAREN => 21, :RPAREN => 22, :ISEQUAL => 23, :GREATEREQUAL => 24, :GREATERTHAN => 25, :LESSTHAN => 26, :IF => 27, :ELSE => 28, :IMPORT => 29, :DEFINE => 30, :ELSIF => 31, :VARIABLE => 32, :CLASS => 33, :INHERITS => 34, :NODE => 35, :BOOLEAN => 36, :NAME => 37, :SEMIC => 38, :CASE => 39, :DEFAULT => 40, :AT => 41, :LCOLLECT => 42, :RCOLLECT => 43, :CLASSNAME => 44, :CLASSREF => 45, :NOT => 46, :OR => 47, :AND => 48, :UNDEF => 49, :PARROW => 50 } racc_use_result_var = true racc_nt_base = 51 Racc_arg = [ racc_action_table, racc_action_check, racc_action_default, racc_action_pointer, racc_goto_table, racc_goto_check, racc_goto_default, racc_goto_pointer, racc_nt_base, racc_reduce_table, racc_token_table, racc_shift_n, racc_reduce_n, racc_use_result_var ] Racc_token_to_s_table = [ '$end', 'error', 'LBRACK', 'DQTEXT', 'SQTEXT', 'RBRACK', 'LBRACE', 'RBRACE', 'SYMBOL', 'FARROW', 'COMMA', 'TRUE', 'FALSE', 'EQUALS', 'LESSEQUAL', 'NOTEQUAL', 'DOT', 'COLON', 'LLCOLLECT', 'RRCOLLECT', 'QMARK', 'LPAREN', 'RPAREN', 'ISEQUAL', 'GREATEREQUAL', 'GREATERTHAN', 'LESSTHAN', 'IF', 'ELSE', 'IMPORT', 'DEFINE', 'ELSIF', 'VARIABLE', 'CLASS', 'INHERITS', 'NODE', 'BOOLEAN', 'NAME', 'SEMIC', 'CASE', 'DEFAULT', 'AT', 'LCOLLECT', 'RCOLLECT', 'CLASSNAME', 'CLASSREF', 'NOT', 'OR', 'AND', 'UNDEF', 'PARROW', '$start', 'program', 'statements', 'nil', 'statement', 'resource', 'virtualresource', 'collection', 'assignment', 'casestatement', 'ifstatement', 'import', 'fstatement', 'definition', 'hostclass', 'nodedef', 'resourceoverride', 'funcvalues', 'namestrings', 'resourcerefs', 'namestring', 'name', 'variable', 'type', 'boolean', 'funcrvalue', 'selector', 'quotedtext', 'resourceref', 'classname', 'resourceinstances', 'endsemi', 'params', 'endcomma', 'classref', 'anyparams', 'at', 'collectrhand', 'collstatements', 'collstatement', 'colljoin', 'collexpr', 'colllval', 'simplervalue', 'resourceinst', 'resourcename', 'undef', 'array', 'rvalue', 'param', 'addparam', 'anyparam', 'rvalues', 'comma', 'iftest', 'else', 'caseopts', 'caseopt', 'casevalues', 'selectlhand', 'svalues', 'selectval', 'sintvalues', 'qtexts', 'argumentlist', 'classparent', 'hostnames', 'nodeparent', 'hostname', 'nothing', 'arguments', 'argument', 'classnameordefault'] Racc_debug_parser = false ##### racc system variables end ##### # reduce 0 omitted module_eval <<'.,.,', 'grammar.ra', 30 def _reduce_1( val, _values, result ) if val[0] # Make sure we always return an array. if val[0].is_a?(AST::ASTArray) if val[0].children.empty? result = nil else result = val[0] end else result = aryfy(val[0]) end else result = nil end result end .,., # reduce 2 omitted # reduce 3 omitted module_eval <<'.,.,', 'grammar.ra', 46 def _reduce_4( val, _values, result ) if val[0] and val[1] if val[0].instance_of?(AST::ASTArray) val[0].push(val[1]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[1]] end elsif obj = (val[0] || val[1]) result = obj else result = nil end result end .,., # reduce 5 omitted # reduce 6 omitted # reduce 7 omitted # reduce 8 omitted # reduce 9 omitted # reduce 10 omitted # reduce 11 omitted # reduce 12 omitted # reduce 13 omitted # reduce 14 omitted # reduce 15 omitted # reduce 16 omitted module_eval <<'.,.,', 'grammar.ra', 68 def _reduce_17( val, _values, result ) args = aryfy(val[2]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :statement result end .,., module_eval <<'.,.,', 'grammar.ra', 74 def _reduce_18( val, _values, result ) result = ast AST::Function, :name => val[0], :arguments => AST::ASTArray.new({}), :ftype => :statement result end .,., module_eval <<'.,.,', 'grammar.ra', 81 def _reduce_19( val, _values, result ) args = aryfy(val[1]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :statement result end .,., # reduce 20 omitted # reduce 21 omitted # reduce 22 omitted module_eval <<'.,.,', 'grammar.ra', 91 def _reduce_23( val, _values, result ) result = aryfy(val[0], val[2]) result.line = @lexer.line result.file = @lexer.file result end .,., # reduce 24 omitted # reduce 25 omitted # reduce 26 omitted # reduce 27 omitted # reduce 28 omitted # reduce 29 omitted # reduce 30 omitted module_eval <<'.,.,', 'grammar.ra', 104 def _reduce_31( val, _values, result ) result = ast AST::Name, :value => val[0] result end .,., # reduce 32 omitted module_eval <<'.,.,', 'grammar.ra', 115 def _reduce_33( val, _values, result ) unless val[0].is_a?(AST::ASTArray) val[0] = aryfy(val[0]) end val[0].push(val[2]) result = val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 136 def _reduce_34( val, _values, result ) array = val[2] if array.instance_of?(AST::ResourceInstance) array = [array] end result = ast AST::ASTArray # this iterates across each specified resourceinstance array.each { |instance| unless instance.instance_of?(AST::ResourceInstance) raise Puppet::Dev, "Got something that isn't an instance" end # now, i need to somehow differentiate between those things with # arrays in their names, and normal things result.push ast(AST::Resource, :type => val[0], :title => instance[0], :params => instance[1]) } result end .,., module_eval <<'.,.,', 'grammar.ra', 139 def _reduce_35( val, _values, result ) # This is a deprecated syntax. error "All resource specifications require names" result end .,., module_eval <<'.,.,', 'grammar.ra', 142 def _reduce_36( val, _values, result ) # a defaults setting for a type result = ast(AST::ResourceDefaults, :type => val[0], :params => val[2]) result end .,., module_eval <<'.,.,', 'grammar.ra', 147 def _reduce_37( val, _values, result ) result = ast AST::ResourceOverride, :object => val[0], :params => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 174 def _reduce_38( val, _values, result ) type = val[0] - if type == :exported and ! Puppet[:storeconfigs] + if (type == :exported and ! Puppet[:storeconfigs]) and ! Puppet[:parseonly] error "You cannot collect without storeconfigs being set" end if val[1].is_a? AST::ResourceDefaults error "Defaults are not virtualizable" end method = type.to_s + "=" # Just mark our resources as exported and pass them through. if val[1].instance_of?(AST::ASTArray) val[1].each do |obj| obj.send(method, true) end else val[1].send(method, true) end result = val[1] result end .,., module_eval <<'.,.,', 'grammar.ra', 175 def _reduce_39( val, _values, result ) result = :virtual result end .,., module_eval <<'.,.,', 'grammar.ra', 176 def _reduce_40( val, _values, result ) result = :exported result end .,., module_eval <<'.,.,', 'grammar.ra', 199 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} if val[1].is_a?(AST::CollExpr) args[:query] = val[1] args[:query].type = type args[:form] = args[:query].form else args[:form] = val[1] end - if args[:form] == :exported and ! Puppet[:storeconfigs] + if args[:form] == :exported and ! Puppet[:storeconfigs] and ! Puppet[:parseonly] error "You cannot collect exported resources without storeconfigs being set" end result = ast AST::Collection, args result end .,., module_eval <<'.,.,', 'grammar.ra', 209 def _reduce_42( val, _values, result ) if val[1] result = val[1] result.form = :virtual else result = :virtual end result end .,., module_eval <<'.,.,', 'grammar.ra', 217 def _reduce_43( val, _values, result ) if val[1] result = val[1] result.form = :exported else result = :exported end result end .,., # reduce 44 omitted # reduce 45 omitted module_eval <<'.,.,', 'grammar.ra', 225 def _reduce_46( val, _values, result ) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] result end .,., # reduce 47 omitted module_eval <<'.,.,', 'grammar.ra', 231 def _reduce_48( val, _values, result ) result = val[1] result.parens = true result end .,., # reduce 49 omitted # reduce 50 omitted module_eval <<'.,.,', 'grammar.ra', 239 def _reduce_51( val, _values, result ) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] #result = ast AST::CollExpr #result.push *val result end .,., module_eval <<'.,.,', 'grammar.ra', 244 def _reduce_52( val, _values, result ) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] #result = ast AST::CollExpr #result.push *val result end .,., # reduce 53 omitted # reduce 54 omitted module_eval <<'.,.,', 'grammar.ra', 251 def _reduce_55( val, _values, result ) result = ast AST::ResourceInstance, :children => [val[0],val[2]] result end .,., # reduce 56 omitted module_eval <<'.,.,', 'grammar.ra', 261 def _reduce_57( val, _values, result ) if val[0].instance_of?(AST::ResourceInstance) result = ast AST::ASTArray, :children => [val[0],val[2]] else val[0].push val[2] result = val[0] end result end .,., # reduce 58 omitted # reduce 59 omitted module_eval <<'.,.,', 'grammar.ra', 268 def _reduce_60( val, _values, result ) result = ast AST::Undef, :value => :undef result end .,., module_eval <<'.,.,', 'grammar.ra', 272 def _reduce_61( val, _values, result ) result = ast AST::Name, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 276 def _reduce_62( val, _values, result ) result = ast AST::Type, :value => val[0] result end .,., # reduce 63 omitted # reduce 64 omitted # reduce 65 omitted # reduce 66 omitted # reduce 67 omitted # reduce 68 omitted module_eval <<'.,.,', 'grammar.ra', 292 def _reduce_69( val, _values, result ) if val[0] =~ /::/ raise Puppet::ParseError, "Cannot assign to variables in other namespaces" end # this is distinct from referencing a variable variable = ast AST::Name, :value => val[0] result = ast AST::VarDef, :name => variable, :value => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 297 def _reduce_70( val, _values, result ) result = ast AST::ASTArray result end .,., module_eval <<'.,.,', 'grammar.ra', 297 def _reduce_71( val, _values, result ) result = val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 306 def _reduce_72( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 310 def _reduce_73( val, _values, result ) result = ast AST::ResourceParam, :param => val[0], :value => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 315 def _reduce_74( val, _values, result ) result = ast AST::ResourceParam, :param => val[0], :value => val[2], :add => true result end .,., # reduce 75 omitted # reduce 76 omitted module_eval <<'.,.,', 'grammar.ra', 323 def _reduce_77( val, _values, result ) result = ast AST::ASTArray result end .,., module_eval <<'.,.,', 'grammar.ra', 323 def _reduce_78( val, _values, result ) result = val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 332 def _reduce_79( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end result end .,., # reduce 80 omitted module_eval <<'.,.,', 'grammar.ra', 341 def _reduce_81( val, _values, result ) if val[0].instance_of?(AST::ASTArray) result = val[0].push(val[2]) else result = ast AST::ASTArray, :children => [val[0],val[2]] end result end .,., # reduce 82 omitted # reduce 83 omitted # reduce 84 omitted # reduce 85 omitted # reduce 86 omitted # reduce 87 omitted # reduce 88 omitted # reduce 89 omitted # reduce 90 omitted # reduce 91 omitted # reduce 92 omitted # reduce 93 omitted # reduce 94 omitted # reduce 95 omitted # reduce 96 omitted # reduce 97 omitted module_eval <<'.,.,', 'grammar.ra', 368 def _reduce_98( val, _values, result ) args = aryfy(val[2]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :rvalue result end .,., module_eval <<'.,.,', 'grammar.ra', 373 def _reduce_99( val, _values, result ) result = ast AST::Function, :name => val[0], :arguments => AST::ASTArray.new({}), :ftype => :rvalue result end .,., module_eval <<'.,.,', 'grammar.ra', 377 def _reduce_100( val, _values, result ) result = ast AST::String, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 379 def _reduce_101( val, _values, result ) result = ast AST::FlatString, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 383 def _reduce_102( val, _values, result ) result = ast AST::Boolean, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 388 def _reduce_103( val, _values, result ) Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized") result = ast AST::ResourceReference, :type => val[0], :title => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 390 def _reduce_104( val, _values, result ) result = ast AST::ResourceReference, :type => val[0], :title => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 403 def _reduce_105( val, _values, result ) args = { :test => val[1], :statements => val[3] } if val[5] args[:else] = val[5] end result = ast AST::IfStatement, args result end .,., # reduce 106 omitted module_eval <<'.,.,', 'grammar.ra', 408 def _reduce_107( val, _values, result ) result = ast AST::Else, :statements => val[2] result end .,., # reduce 108 omitted module_eval <<'.,.,', 'grammar.ra', 420 def _reduce_109( val, _values, result ) options = val[3] unless options.instance_of?(AST::ASTArray) options = ast AST::ASTArray, :children => [val[3]] end result = ast AST::CaseStatement, :test => val[1], :options => options result end .,., # reduce 110 omitted module_eval <<'.,.,', 'grammar.ra', 430 def _reduce_111( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push val[1] result = val[0] else result = ast AST::ASTArray, :children => [val[0], val[1]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 434 def _reduce_112( val, _values, result ) result = ast AST::CaseOpt, :value => val[0], :statements => val[3] result end .,., module_eval <<'.,.,', 'grammar.ra', 439 def _reduce_113( val, _values, result ) result = ast(AST::CaseOpt, :value => val[0], :statements => ast(AST::ASTArray) ) result end .,., # reduce 114 omitted module_eval <<'.,.,', 'grammar.ra', 449 def _reduce_115( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 453 def _reduce_116( val, _values, result ) result = ast AST::Selector, :param => val[0], :values => val[2] result end .,., # reduce 117 omitted module_eval <<'.,.,', 'grammar.ra', 455 def _reduce_118( val, _values, result ) result = val[1] result end .,., # reduce 119 omitted module_eval <<'.,.,', 'grammar.ra', 466 def _reduce_120( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 470 def _reduce_121( val, _values, result ) result = ast AST::ResourceParam, :param => val[0], :value => val[2] result end .,., # reduce 122 omitted # reduce 123 omitted # reduce 124 omitted # reduce 125 omitted # reduce 126 omitted # reduce 127 omitted # reduce 128 omitted module_eval <<'.,.,', 'grammar.ra', 481 def _reduce_129( val, _values, result ) result = ast AST::Default, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 483 def _reduce_130( val, _values, result ) result = [val[0].value] result end .,., module_eval <<'.,.,', 'grammar.ra', 487 def _reduce_131( val, _values, result ) results = val[0] << val[2].value result end .,., module_eval <<'.,.,', 'grammar.ra', 495 def _reduce_132( val, _values, result ) val[1].each do |file| import(file) end result = AST::ASTArray.new(:children => []) result end .,., module_eval <<'.,.,', 'grammar.ra', 505 def _reduce_133( val, _values, result ) newdefine classname(val[1]), :arguments => val[2], :code => val[4] @lexer.indefine = false result = nil #} | DEFINE NAME argumentlist parent LBRACE RBRACE { result end .,., module_eval <<'.,.,', 'grammar.ra', 509 def _reduce_134( val, _values, result ) newdefine classname(val[1]), :arguments => val[2] @lexer.indefine = false result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 517 def _reduce_135( val, _values, result ) # 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 result end .,., module_eval <<'.,.,', 'grammar.ra', 522 def _reduce_136( val, _values, result ) # Our class gets defined in the parent namespace, not our own. @lexer.namepop newclass classname(val[1]), :parent => val[2] result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 527 def _reduce_137( val, _values, result ) newnode val[1], :parent => val[2], :code => val[4] result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 530 def _reduce_138( val, _values, result ) newnode val[1], :parent => val[2] result = nil result end .,., # reduce 139 omitted # reduce 140 omitted # reduce 141 omitted # reduce 142 omitted module_eval <<'.,.,', 'grammar.ra', 544 def _reduce_143( val, _values, result ) result = val[0] result = [result] unless result.is_a?(Array) result << val[2] result end .,., # reduce 144 omitted # reduce 145 omitted # reduce 146 omitted # reduce 147 omitted module_eval <<'.,.,', 'grammar.ra', 553 def _reduce_148( val, _values, result ) result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 557 def _reduce_149( val, _values, result ) result = ast AST::ASTArray, :children => [] result end .,., # reduce 150 omitted module_eval <<'.,.,', 'grammar.ra', 562 def _reduce_151( val, _values, result ) result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 566 def _reduce_152( val, _values, result ) result = val[1] result = [result] unless result[0].is_a?(Array) result end .,., # reduce 153 omitted module_eval <<'.,.,', 'grammar.ra', 573 def _reduce_154( val, _values, result ) result = val[0] result = [result] unless result[0].is_a?(Array) result << val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 578 def _reduce_155( val, _values, result ) Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0], val[2]] result end .,., module_eval <<'.,.,', 'grammar.ra', 582 def _reduce_156( val, _values, result ) Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0]] result end .,., module_eval <<'.,.,', 'grammar.ra', 584 def _reduce_157( val, _values, result ) result = [val[0], val[2]] result end .,., module_eval <<'.,.,', 'grammar.ra', 586 def _reduce_158( val, _values, result ) result = [val[0]] result end .,., # reduce 159 omitted module_eval <<'.,.,', 'grammar.ra', 591 def _reduce_160( val, _values, result ) result = val[1] result end .,., # reduce 161 omitted module_eval <<'.,.,', 'grammar.ra', 596 def _reduce_162( val, _values, result ) result = val[1] result end .,., # reduce 163 omitted # reduce 164 omitted module_eval <<'.,.,', 'grammar.ra', 602 def _reduce_165( val, _values, result ) result = ast AST::Variable, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 610 def _reduce_166( val, _values, result ) if val[1].instance_of?(AST::ASTArray) result = val[1] else result = ast AST::ASTArray, :children => [val[1]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 612 def _reduce_167( val, _values, result ) result = ast AST::ASTArray result end .,., # reduce 168 omitted # reduce 169 omitted # reduce 170 omitted module_eval <<'.,.,', 'grammar.ra', 617 def _reduce_171( val, _values, result ) result = nil result end .,., def _reduce_none( val, _values, result ) result end end # class Parser end # module Parser end # module Puppet diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index def9e44e4..c4e154d47 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -1,431 +1,431 @@ require 'puppet' require 'puppet/util/log' require 'puppet/event' require 'puppet/util/metric' require 'puppet/property' require 'puppet/parameter' require 'puppet/util' require 'puppet/util/autoload' require 'puppet/metatype/manager' require 'puppet/util/errors' require 'puppet/util/log_paths' require 'puppet/util/logging' require 'puppet/resource_reference' # see the bottom of the file for the rest of the inclusions module Puppet class Type include Puppet::Util include Puppet::Util::Errors include Puppet::Util::LogPaths include Puppet::Util::Logging # Nearly all of the code in this class is stored in files in the # metatype/ directory. This is a temporary measure until I get a chance # to refactor this class entirely. There's still more simplification to # do, but this works for now. require 'puppet/metatype/attributes' require 'puppet/metatype/closure' require 'puppet/metatype/container' require 'puppet/metatype/evaluation' require 'puppet/metatype/instances' require 'puppet/metatype/metaparams' require 'puppet/metatype/providers' require 'puppet/metatype/relationships' require 'puppet/metatype/schedules' require 'puppet/metatype/tags' # Types (which map to resources in the languages) are entirely composed of # attribute value pairs. Generally, Puppet calls any of these things an # 'attribute', but these attributes always take one of three specific # forms: parameters, metaparams, or properties. # In naming methods, I have tried to consistently name the method so # that it is clear whether it operates on all attributes (thus has 'attr' in # the method name, or whether it operates on a specific type of attributes. attr_accessor :file, :line attr_writer :title attr_writer :noop include Enumerable # class methods dealing with Type management public # the Type class attribute accessors class << self attr_reader :name attr_accessor :self_refresh include Enumerable, Puppet::Util::ClassGen include Puppet::MetaType::Manager include Puppet::Util include Puppet::Util::Logging end # all of the variables that must be initialized for each subclass def self.initvars # all of the instances of this class @objects = Hash.new @aliases = Hash.new @providers = Hash.new @defaults = {} unless defined? @parameters @parameters = [] end @validproperties = {} @properties = [] @parameters = [] @paramhash = {} @attr_aliases = {} @paramdoc = Hash.new { |hash,key| if key.is_a?(String) key = key.intern end if hash.include?(key) hash[key] else "Param Documentation for %s not found" % key end } unless defined? @doc @doc = "" end end def self.to_s if defined? @name "Puppet::Type::" + @name.to_s.capitalize else super end end # Create a block to validate that our object is set up entirely. This will # be run before the object is operated on. def self.validate(&block) define_method(:validate, &block) #@validate = block end # The catalog that this resource is stored in. attr_accessor :catalog # create a log at specified level def log(msg) Puppet::Util::Log.create( :level => @parameters[:loglevel].value, :message => msg, :source => self ) end # instance methods related to instance intrinsics # e.g., initialize() and name() public def initvars @evalcount = 0 @tags = [] # callbacks are per object and event @callbacks = Hash.new { |chash, key| chash[key] = {} } # properties and parameters are treated equivalently from the outside: # as name-value pairs (using [] and []=) # internally, however, parameters are merely a hash, while properties # point to Property objects # further, the lists of valid properties and parameters are defined # at the class level unless defined? @parameters @parameters = {} end # set defalts @noop = false # keeping stats for the total number of changes, and how many were # completely sync'ed # this isn't really sufficient either, because it adds lots of special # cases such as failed changes # it also doesn't distinguish between changes from the current transaction # vs. changes over the process lifetime @totalchanges = 0 @syncedchanges = 0 @failedchanges = 0 @inited = true end # initialize the type instance def initialize(hash) unless defined? @inited self.initvars end namevar = self.class.namevar orighash = hash # If we got passed a transportable object, we just pull a bunch of info # directly from it. This is the main object instantiation mechanism. if hash.is_a?(Puppet::TransObject) # XXX This will need to change when transobjects change to titles. self.title = hash.name #self[:name] = hash[:name] [:file, :line, :tags, :catalog].each { |getter| if hash.respond_to?(getter) setter = getter.to_s + "=" if val = hash.send(getter) self.send(setter, val) end end } hash = hash.to_hash else if hash[:title] @title = hash[:title] hash.delete(:title) end end # Before anything else, set our parent if it was included if hash.include?(:parent) @parent = hash[:parent] hash.delete(:parent) end # Munge up the namevar stuff so we only have one value. hash = self.argclean(hash) # Let's do the name first, because some things need to happen once # we have the name but before anything else attrs = self.class.allattrs if hash.include?(namevar) #self.send(namevar.to_s + "=", hash[namevar]) self[namevar] = hash[namevar] hash.delete(namevar) if attrs.include?(namevar) attrs.delete(namevar) else self.devfail "My namevar isn't a valid attribute...?" end else self.devfail "I was not passed a namevar" end # If the name and title differ, set up an alias if self.name != self.title if obj = self.class[self.name] if self.class.isomorphic? raise Puppet::Error, "%s already exists with name %s" % [obj.title, self.name] end else self.class.alias(self.name, self) end end if hash.include?(:provider) self[:provider] = hash[:provider] hash.delete(:provider) else setdefaults(:provider) end # This is all of our attributes except the namevar. attrs.each { |attr| if hash.include?(attr) begin self[attr] = hash[attr] rescue ArgumentError, Puppet::Error, TypeError raise rescue => detail error = Puppet::DevError.new( "Could not set %s on %s: %s" % [attr, self.class.name, detail] ) error.set_backtrace(detail.backtrace) raise error end hash.delete attr end } # Set all default values. self.setdefaults if hash.length > 0 self.debug hash.inspect self.fail("Class %s does not accept argument(s) %s" % [self.class.name, hash.keys.join(" ")]) end if self.respond_to?(:validate) self.validate end end # Set up all of our autorequires. def finish # Scheduling has to be done when the whole config is instantiated, so # that file order doesn't matter in finding them. self.schedule # Make sure all of our relationships are valid. Again, must be done # when the entire catalog is instantiated. self.class.relationship_params.collect do |klass| if param = @parameters[klass.name] param.validate_relationship end end.flatten.reject { |r| r.nil? } end # Return a cached value def cached(name) Puppet::Util::Storage.cache(self)[name] #@cache[name] ||= nil end # Cache a value def cache(name, value) Puppet::Util::Storage.cache(self)[name] = value #@cache[name] = value end # def set(name, value) # send(name.to_s + "=", value) # end # # def get(name) # send(name) # end # For now, leave the 'name' method functioning like it used to. Once 'title' # works everywhere, I'll switch it. def name return self[:name] end # Look up our parent in the catalog, if we have one. def parent return nil unless catalog unless defined?(@parent) # This is kinda weird. if implicit? parents = catalog.relationship_graph.adjacent(self, :direction => :in) else parents = catalog.adjacent(self, :direction => :in) end if parents # We should never have more than one parent, so let's just ignore # it if we happen to. @parent = parents.shift else @parent = nil end end @parent end # Return the "type[name]" style reference. def ref "%s[%s]" % [self.class.name.to_s.capitalize, self.title] end def self_refresh? self.class.self_refresh end # Mark that we're purging. def purging @purging = true end # Is this resource being purged? Used by transactions to forbid # deletion when there are dependencies. def purging? if defined? @purging @purging else false end end # Retrieve the title of an object. If no title was set separately, # then use the object's name. def title unless defined? @title and @title namevar = self.class.namevar if self.class.validparameter?(namevar) @title = self[:name] elsif self.class.validproperty?(namevar) @title = self.should(namevar) else self.devfail "Could not find namevar %s for %s" % [namevar, self.class.name] end end return @title end # convert to a string def to_s self.ref end # Convert to a transportable object def to_trans(ret = true) trans = TransObject.new(self.title, self.class.name) values = retrieve() values.each do |name, value| trans[name.name] = value end @parameters.each do |name, param| # Avoid adding each instance name as both the name and the namevar next if param.class.isnamevar? and param.value == self.title # We've already got property values next if param.is_a?(Puppet::Property) trans[name] = param.value end trans.tags = self.tags # FIXME I'm currently ignoring 'parent' and 'path' return trans end end # Puppet::Type end require 'puppet/propertychange' require 'puppet/provider' require 'puppet/type/component' -require 'puppet/type/pfile' -require 'puppet/type/pfilebucket' +require 'puppet/type/file' +require 'puppet/type/filebucket' require 'puppet/type/tidy' diff --git a/lib/puppet/type/pfile.rb b/lib/puppet/type/file.rb similarity index 98% rename from lib/puppet/type/pfile.rb rename to lib/puppet/type/file.rb index c32a4d474..65ee7e72e 100644 --- a/lib/puppet/type/pfile.rb +++ b/lib/puppet/type/file.rb @@ -1,1171 +1,1171 @@ require 'digest/md5' require 'cgi' require 'etc' require 'uri' require 'fileutils' require 'puppet/network/handler' require 'puppet/util/diff' module Puppet newtype(:file) do include Puppet::Util::MethodHelper @doc = "Manages local files, including setting ownership and permissions, creation of both files and directories, and retrieving entire files from remote servers. As Puppet matures, it expected that the ``file`` resource will be used less and less to manage content, and instead native resources will be used to do so. If you find that you are often copying files in from a central location, rather than using native resources, please contact Reductive Labs and we can hopefully work with you to develop a native resource to support what you are doing." newparam(:path) do desc "The path to the file to manage. Must be fully qualified." isnamevar validate do |value| unless value =~ /^#{File::SEPARATOR}/ raise Puppet::Error, "File paths must be fully qualified" end end end newparam(:backup) do desc "Whether files should be backed up before being replaced. The preferred method of backing files up is via a ``filebucket``, which stores files by their MD5 sums and allows easy retrieval without littering directories with backups. You can specify a local filebucket or a network-accessible server-based filebucket by setting ``backup => bucket-name``. Alternatively, if you specify any value that begins with a ``.`` (e.g., ``.puppet-bak``), then Puppet will use copy the file in the same directory with that value as the extension of the backup. Setting ``backup => false`` disables all backups of the file in question. Puppet automatically creates a local filebucket named ``puppet`` and defaults to backing up there. To use a server-based filebucket, you must specify one in your configuration:: filebucket { main: server => puppet } The ``puppetmasterd`` daemon creates a filebucket by default, so you can usually back up to your main server with this configuration. Once you've described the bucket in your configuration, you can use it in any file:: file { \"/my/file\": source => \"/path/in/nfs/or/something\", backup => main } This will back the file up to the central server. At this point, the benefits of using a filebucket are that you do not have backup files lying around on each of your machines, a given version of a file is only backed up once, and you can restore any given file manually, no matter how old. Eventually, transactional support will be able to automatically restore filebucketed files. " defaultto { "puppet" } munge do |value| # I don't really know how this is happening. if value.is_a?(Array) value = value.shift end case value when false, "false", :false: false when true, "true", ".puppet-bak", :true: ".puppet-bak" when /^\./ value when String: # We can't depend on looking this up right now, # we have to do it after all of the objects # have been instantiated. if bucketobj = Puppet::Type.type(:filebucket)[value] @resource.bucket = bucketobj.bucket bucketobj.title else # Set it to the string; finish() turns it into a # filebucket. @resource.bucket = value value end when Puppet::Network::Client.client(:Dipper): @resource.bucket = value value.name else self.fail "Invalid backup type %s" % value.inspect end end end newparam(:recurse) do desc "Whether and how deeply to do recursive management." newvalues(:true, :false, :inf, /^[0-9]+$/) # Replace the validation so that we allow numbers in # addition to string representations of them. validate { |arg| } munge do |value| newval = super(value) case newval when :true, :inf: true when :false: false when Integer, Fixnum, Bignum: value when /^\d+$/: Integer(value) else raise ArgumentError, "Invalid recurse value %s" % value.inspect end end end newparam(:replace, :boolean => true) do desc "Whether or not to replace a file that is sourced but exists. This is useful for using file sources purely for initialization." newvalues(:true, :false) aliasvalue(:yes, :true) aliasvalue(:no, :false) defaultto :true end newparam(:force, :boolean => true) do desc "Force the file operation. Currently only used when replacing directories with links." newvalues(:true, :false) defaultto false end newparam(:ignore) do desc "A parameter which omits action on files matching specified patterns during recursion. Uses Ruby's builtin globbing engine, so shell metacharacters are fully supported, e.g. ``[a-z]*``. Matches that would descend into the directory structure are ignored, e.g., ``*/*``." defaultto false validate do |value| unless value.is_a?(Array) or value.is_a?(String) or value == false self.devfail "Ignore must be a string or an Array" end end end newparam(:links) do desc "How to handle links during file actions. During file copying, ``follow`` will copy the target file instead of the link, ``manage`` will copy the link itself, and ``ignore`` will just pass it by. When not copying, ``manage`` and ``ignore`` behave equivalently (because you cannot really ignore links entirely during local recursion), and ``follow`` will manage the file to which the link points." newvalues(:follow, :manage, :ignore) # :ignore and :manage behave equivalently on local files, # but don't copy remote links defaultto :ignore end newparam(:purge, :boolean => true) do desc "Whether unmanaged files should be purged. If you have a filebucket configured the purged files will be uploaded, but if you do not, this will destroy data. Only use this option for generated files unless you really know what you are doing. This option only makes sense when recursively managing directories. Note that when using ``purge`` with ``source``, Puppet will purge any files that are not on the remote system." defaultto :false newvalues(:true, :false) end newparam(:sourceselect) do desc "Whether to copy all valid sources, or just the first one. This parameter is only used in recursive copies; by default, the first valid source is the only one used as a recursive source, but if this parameter is set to ``all``, then all valid sources will have all of their contents copied to the local host, and for sources that have the same file, the source earlier in the list will be used." defaultto :first newvalues(:first, :all) end attr_accessor :bucket # Autorequire any parent directories. autorequire(:file) do if self[:path] File.dirname(self[:path]) else Puppet.err "no path for %s, somehow; cannot setup autorequires" % self.ref nil end end # Autorequire the owner and group of the file. {:user => :owner, :group => :group}.each do |type, property| autorequire(type) do if @parameters.include?(property) # The user/group property automatically converts to IDs next unless should = @parameters[property].shouldorig val = should[0] if val.is_a?(Integer) or val =~ /^\d+$/ nil else val end end end end CREATORS = [:content, :source, :target] validate do count = 0 CREATORS.each do |param| count += 1 if self.should(param) end if count > 1 self.fail "You cannot specify more than one of %s" % CREATORS.collect { |p| p.to_s}.join(", ") end end def self.[](path) return nil unless path super(path.gsub(/\/+/, '/').sub(/\/$/, '')) end # List files, but only one level deep. def self.instances(base = "/") unless FileTest.directory?(base) return [] end files = [] Dir.entries(base).reject { |e| e == "." or e == ".." }.each do |name| path = File.join(base, name) if obj = self[path] obj[:check] = :all files << obj else files << self.create( :name => path, :check => :all ) end end files end @depthfirst = false def argument?(arg) @arghash.include?(arg) end # Determine the user to write files as. def asuser if self.should(:owner) and ! self.should(:owner).is_a?(Symbol) writeable = Puppet::Util::SUIDManager.asuser(self.should(:owner)) { FileTest.writable?(File.dirname(self[:path])) } # If the parent directory is writeable, then we execute # as the user in question. Otherwise we'll rely on # the 'owner' property to do things. if writeable asuser = self.should(:owner) end end return asuser end # We have to do some extra finishing, to retrieve our bucket if # there is one. def finish # Let's cache these values, since there should really only be # a couple of these buckets @@filebuckets ||= {} # Look up our bucket, if there is one if bucket = self.bucket case bucket when String: if obj = @@filebuckets[bucket] # This sets the @value on :backup, too self.bucket = obj elsif bucket == "puppet" obj = Puppet::Network::Client.client(:Dipper).new( :Path => Puppet[:clientbucketdir] ) self.bucket = obj @@filebuckets[bucket] = obj elsif obj = Puppet::Type.type(:filebucket).bucket(bucket) @@filebuckets[bucket] = obj self.bucket = obj else self.fail "Could not find filebucket %s" % bucket end when Puppet::Network::Client.client(:Dipper): # things are hunky-dorey else self.fail "Invalid bucket type %s" % bucket.class end end super end # Create any children via recursion or whatever. def eval_generate recurse() end # Deal with backups. def handlebackup(file = nil) # let the path be specified file ||= self[:path] # if they specifically don't want a backup, then just say # we're good unless FileTest.exists?(file) return true end unless self[:backup] return true end case File.stat(file).ftype when "directory": if self[:recurse] # we don't need to backup directories when recurse is on return true else backup = self.bucket || self[:backup] case backup when Puppet::Network::Client.client(:Dipper): notice "Recursively backing up to filebucket" require 'find' Find.find(self[:path]) do |f| if File.file?(f) sum = backup.backup(f) self.info "Filebucketed %s to %s with sum %s" % [f, backup.name, sum] end end return true when String: newfile = file + backup # Just move it, since it's a directory. if FileTest.exists?(newfile) remove_backup(newfile) end begin bfile = file + backup # Ruby 1.8.1 requires the 'preserve' addition, but # later versions do not appear to require it. FileUtils.cp_r(file, bfile, :preserve => true) return true rescue => detail # since they said they want a backup, let's error out # if we couldn't make one self.fail "Could not back %s up: %s" % [file, detail.message] end else self.err "Invalid backup type %s" % backup.inspect return false end end when "file": backup = self.bucket || self[:backup] case backup when Puppet::Network::Client.client(:Dipper): sum = backup.backup(file) self.info "Filebucketed to %s with sum %s" % [backup.name, sum] return true when String: newfile = file + backup if FileTest.exists?(newfile) remove_backup(newfile) end begin # FIXME Shouldn't this just use a Puppet object with # 'source' specified? bfile = file + backup # Ruby 1.8.1 requires the 'preserve' addition, but # later versions do not appear to require it. FileUtils.cp(file, bfile, :preserve => true) return true rescue => detail # since they said they want a backup, let's error out # if we couldn't make one self.fail "Could not back %s up: %s" % [file, detail.message] end else self.err "Invalid backup type %s" % backup.inspect return false end when "link": return true else self.notice "Cannot backup files of type %s" % File.stat(file).ftype return false end end def handleignore(children) return children unless self[:ignore] self[:ignore].each { |ignore| ignored = [] Dir.glob(File.join(self[:path],ignore), File::FNM_DOTMATCH) { |match| ignored.push(File.basename(match)) } children = children - ignored } return children end def initialize(hash) # Store a copy of the arguments for later. tmphash = hash.to_hash # Used for caching clients @clients = {} super # Get rid of any duplicate slashes, and remove any trailing slashes. @title = @title.gsub(/\/+/, "/") @title.sub!(/\/$/, "") unless @title == "/" # Clean out as many references to any file paths as possible. # This was the source of many, many bugs. @arghash = tmphash @arghash.delete(self.class.namevar) [:source, :parent].each do |param| if @arghash.include?(param) @arghash.delete(param) end end @stat = nil end # Build a recursive map of a link source def linkrecurse(recurse) target = @parameters[:target].should method = :lstat if self[:links] == :follow method = :stat end targetstat = nil unless FileTest.exist?(target) return end # Now stat our target targetstat = File.send(method, target) unless targetstat.ftype == "directory" return end # Now that we know our corresponding target is a directory, # change our type self[:ensure] = :directory unless FileTest.readable? target self.notice "Cannot manage %s: permission denied" % self.name return end children = Dir.entries(target).reject { |d| d =~ /^\.+$/ } # Get rid of ignored children if @parameters.include?(:ignore) children = handleignore(children) end added = [] children.each do |file| Dir.chdir(target) do longname = File.join(target, file) # Files know to create directories when recursion # is enabled and we're making links args = { :recurse => recurse, :ensure => longname } if child = self.newchild(file, true, args) added << child end end end added end # Build up a recursive map of what's around right now def localrecurse(recurse) unless FileTest.exist?(self[:path]) and self.stat.directory? #self.info "%s is not a directory; not recursing" % # self[:path] return end unless FileTest.readable? self[:path] self.notice "Cannot manage %s: permission denied" % self.name return end children = Dir.entries(self[:path]) #Get rid of ignored children if @parameters.include?(:ignore) children = handleignore(children) end added = [] children.each { |file| file = File.basename(file) next if file =~ /^\.\.?$/ # skip . and .. options = {:recurse => recurse} if child = self.newchild(file, true, options) added << child end } added end # Create a new file or directory object as a child to the current # object. def newchild(path, local, hash = {}) raise(Puppet::DevError, "File recursion cannot happen without a catalog") unless catalog # make local copy of arguments args = symbolize_options(@arghash) # There's probably a better way to do this, but we don't want # to pass this info on. if v = args[:ensure] v = symbolize(v) args.delete(:ensure) end if path =~ %r{^#{File::SEPARATOR}} self.devfail( "Must pass relative paths to PFile#newchild()" ) else path = File.join(self[:path], path) end args[:path] = path unless hash.include?(:recurse) if args.include?(:recurse) if args[:recurse].is_a?(Integer) args[:recurse] -= 1 # reduce the level of recursion end end end hash.each { |key,value| args[key] = value } child = nil # The child might already exist because 'localrecurse' runs # before 'sourcerecurse'. I could push the override stuff into # a separate method or something, but the work is the same other # than this last bit, so it doesn't really make sense. if child = catalog.resource(:file, path) unless child.parent.object_id == self.object_id self.debug "Not managing more explicit file %s" % path return nil end # This is only necessary for sourcerecurse, because we might have # created the object with different 'should' values than are # set remotely. unless local args.each { |var,value| next if var == :path next if var == :name # behave idempotently unless child.should(var) == value child[var] = value end } end return nil else # create it anew #notice "Creating new file with args %s" % args.inspect args[:parent] = self begin # This method is used by subclasses of :file, so use the class name rather than hard-coding # :file. return nil unless child = catalog.create_implicit_resource(self.class.name, args) rescue => detail puts detail.backtrace self.notice "Cannot manage: %s" % [detail] return nil end end # LAK:FIXME This shouldn't be necessary, but as long as we're # modeling the relationship graph specifically, it is. catalog.relationship_graph.add_edge self, child return child end # Files handle paths specially, because they just lengthen their # path names, rather than including the full parent's title each # time. def pathbuilder # We specifically need to call the method here, so it looks # up our parent in the catalog graph. if parent = parent() # We only need to behave specially when our parent is also # a file if parent.is_a?(self.class) # Remove the parent file name list = parent.pathbuilder list.pop # remove the parent's path info return list << self.ref else return super end else return [self.ref] end end # Should we be purging? def purge? @parameters.include?(:purge) and (self[:purge] == :true or self[:purge] == "true") end # Recurse into the directory. This basically just calls 'localrecurse' # and maybe 'sourcerecurse', returning the collection of generated # files. def recurse # are we at the end of the recursion? return unless self.recurse? recurse = self[:recurse] # we might have a string, rather than a number if recurse.is_a?(String) if recurse =~ /^[0-9]+$/ recurse = Integer(recurse) else # anything else is infinite recursion recurse = true end end if recurse.is_a?(Integer) recurse -= 1 end children = [] # We want to do link-recursing before normal recursion so that all # of the target stuff gets copied over correctly. if @parameters.include? :target and ret = self.linkrecurse(recurse) children += ret end if ret = self.localrecurse(recurse) children += ret end # These will be files pulled in by the file source sourced = false if @parameters.include?(:source) ret, sourced = self.sourcerecurse(recurse) if ret children += ret end end # The purge check needs to happen after all of the other recursion. if self.purge? children.each do |child| if (sourced and ! sourced.include?(child[:path])) or ! child.managed? child[:ensure] = :absent end end end children end # A simple method for determining whether we should be recursing. def recurse? return false unless @parameters.include?(:recurse) val = @parameters[:recurse].value if val and (val == true or val > 0) return true else return false end end # Remove the old backup. def remove_backup(newfile) if self.class.name == :file and self[:links] != :follow method = :lstat else method = :stat end old = File.send(method, newfile).ftype if old == "directory" raise Puppet::Error, "Will not remove directory backup %s; use a filebucket" % newfile end info "Removing old backup of type %s" % File.send(method, newfile).ftype begin File.unlink(newfile) rescue => detail if Puppet[:trace] puts detail.backtrace end self.err "Could not remove old backup: %s" % detail return false end end # Remove any existing data. This is only used when dealing with # links or directories. def remove_existing(should) return unless s = stat(true) unless handlebackup self.fail "Could not back up; will not replace" end unless should.to_s == "link" return if s.ftype.to_s == should.to_s end case s.ftype when "directory": if self[:force] == :true debug "Removing existing directory for replacement with %s" % should FileUtils.rmtree(self[:path]) else notice "Not removing directory; use 'force' to override" end when "link", "file": debug "Removing existing %s for replacement with %s" % [s.ftype, should] File.unlink(self[:path]) else self.fail "Could not back up files of type %s" % s.ftype end end # a wrapper method to make sure the file exists before doing anything def retrieve unless stat = self.stat(true) self.debug "File does not exist" # If the file doesn't exist but we have a source, then call # retrieve on that property propertyvalues = properties().inject({}) { |hash, property| hash[property] = :absent hash } if @parameters.include?(:source) propertyvalues[:source] = @parameters[:source].retrieve end return propertyvalues end return currentpropvalues() end # This recurses against the remote source and makes sure the local # and remote structures match. It's run after 'localrecurse'. This # method only does anything when its corresponding remote entry is # a directory; in that case, this method creates file objects that # correspond to any contained remote files. def sourcerecurse(recurse) # we'll set this manually as necessary if @arghash.include?(:ensure) @arghash.delete(:ensure) end r = false if recurse unless recurse == 0 r = 1 end end ignore = self[:ignore] result = [] found = [] # Keep track of all the files we found in the source, so we can purge # appropriately. sourced = [] @parameters[:source].should.each do |source| sourceobj, path = uri2obj(source) # okay, we've got our source object; now we need to # build up a local file structure to match the remote # one server = sourceobj.server desc = server.list(path, self[:links], r, ignore) if desc == "" next end # Now create a new child for every file returned in the list. result += desc.split("\n").collect { |line| file, type = line.split("\t") next if file == "/" # skip the listing object name = file.sub(/^\//, '') # This makes sure that the first source *always* wins # for conflicting files. next if found.include?(name) # For directories, keep all of the sources, so that # sourceselect still works as planned. if type == "directory" newsource = @parameters[:source].should.collect do |tmpsource| tmpsource + file end else newsource = source + file end args = {:source => newsource} if type == file args[:recurse] = nil end found << name sourced << File.join(self[:path], name) self.newchild(name, false, args) }.reject {|c| c.nil? } if self[:sourceselect] == :first return [result, sourced] end end return [result, sourced] end # Set the checksum, from another property. There are multiple # properties that modify the contents of a file, and they need the # ability to make sure that the checksum value is in sync. def setchecksum(sum = nil) if @parameters.include? :checksum if sum @parameters[:checksum].checksum = sum else # If they didn't pass in a sum, then tell checksum to # figure it out. currentvalue = @parameters[:checksum].retrieve @parameters[:checksum].checksum = currentvalue end end end # Stat our file. Depending on the value of the 'links' attribute, we # use either 'stat' or 'lstat', and we expect the properties to use the # resulting stat object accordingly (mostly by testing the 'ftype' # value). def stat(refresh = false) method = :stat # Files are the only types that support links if (self.class.name == :file and self[:links] != :follow) or self.class.name == :tidy method = :lstat end path = self[:path] # Just skip them when they don't exist at all. unless FileTest.exists?(path) or FileTest.symlink?(path) @stat = nil return @stat end if @stat.nil? or refresh == true begin @stat = File.send(method, self[:path]) rescue Errno::ENOENT => error @stat = nil rescue Errno::EACCES => error self.warning "Could not stat; permission denied" @stat = nil end end return @stat end # We have to hack this just a little bit, because otherwise we'll get # an error when the target and the contents are created as properties on # the far side. def to_trans(retrieve = true) obj = super if obj[:target] == :notlink obj.delete(:target) end obj end def localfileserver unless defined? @@localfileserver args = { :Local => true, :Mount => { "/" => "localhost" }, :Config => false } @@localfileserver = Puppet::Network::Handler.handler(:fileserver).new(args) end @@localfileserver end def uri2obj(source) sourceobj = FileSource.new path = nil unless source devfail "Got a nil source" end if source =~ /^\// source = "file://localhost/%s" % URI.escape(source) sourceobj.mount = "localhost" sourceobj.local = true end begin uri = URI.parse(URI.escape(source)) rescue => detail self.fail "Could not understand source %s: %s" % [source, detail.to_s] end case uri.scheme when "file": sourceobj.server = localfileserver path = "/localhost" + uri.path when "puppet": # FIXME: We should cache clients by uri.host + uri.port # not by the full source path unless @clients.include?(source) host = uri.host host ||= Puppet[:server] unless Puppet[:name] == "puppet" if host.nil? server = localfileserver else args = { :Server => host } if uri.port args[:Port] = uri.port end server = Puppet::Network::Client.file.new(args) end @clients[source] = server end sourceobj.server = @clients[source] tmp = uri.path if tmp =~ %r{^/(\w+)} sourceobj.mount = $1 path = tmp #path = tmp.sub(%r{^/\w+},'') || "/" else self.fail "Invalid source path %s" % tmp end else self.fail "Got other URL type '%s' from %s" % [uri.scheme, source] end return [sourceobj, path.sub(/\/\//, '/')] end # Write out the file. We open the file correctly, with all of the # uid and mode and such, and then yield the file handle for actual # writing. def write(property, usetmp = true) mode = self.should(:mode) remove_existing(:file) # The temporary file path = nil if usetmp path = self[:path] + ".puppettmp" else path = self[:path] end # As the correct user and group write_if_writable(File.dirname(path)) do f = nil # Open our file with the correct modes if mode Puppet::Util.withumask(000) do f = File.open(path, File::CREAT|File::WRONLY|File::TRUNC, mode) end else f = File.open(path, File::CREAT|File::WRONLY|File::TRUNC) end # Yield it yield f f.flush f.close end # And put our new file in place if usetmp begin File.rename(path, self[:path]) rescue => detail self.err "Could not rename tmp %s for replacing: %s" % [self[:path], detail] ensure # Make sure the created file gets removed if FileTest.exists?(path) File.unlink(path) end end end # make sure all of the modes are actually correct property_fix # And then update our checksum, so the next run doesn't find it. # FIXME This is extra work, because it's going to read the whole # file back in again. self.setchecksum end # Run the block as the specified user if the dir is writeable, else # run it as root (or the current user). def write_if_writable(dir) yield # We're getting different behaviors from different versions of ruby, so... # asroot = true # Puppet::Util::SUIDManager.asuser(asuser(), self.should(:group)) do # if FileTest.writable?(dir) # asroot = false # yield # end # end # # if asroot # yield # end end private # Override the parent method, because we don't want to generate changes # when the file is missing and there is no 'ensure' state. def propertychanges(currentvalues) unless self.stat found = false ([:ensure] + CREATORS).each do |prop| if @parameters.include?(prop) found = true break end end unless found return [] end end super end # There are some cases where all of the work does not get done on # 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) # Make sure we get a new stat objct self.stat(true) currentvalue = thing.retrieve unless thing.insync?(currentvalue) thing.sync end end end end # Puppet.type(:pfile) # the filesource class can't include the path, because the path # changes for every file instance class FileSource attr_accessor :mount, :root, :server, :local end # We put all of the properties in separate files, because there are so many # of them. The order these are loaded is important, because it determines # the order they are in the property lit. - require 'puppet/type/pfile/checksum' - require 'puppet/type/pfile/content' # can create the file - require 'puppet/type/pfile/source' # can create the file - require 'puppet/type/pfile/target' # creates a different type of file - require 'puppet/type/pfile/ensure' # can create the file - require 'puppet/type/pfile/owner' - require 'puppet/type/pfile/group' - require 'puppet/type/pfile/mode' - require 'puppet/type/pfile/type' + require 'puppet/type/file/checksum' + require 'puppet/type/file/content' # can create the file + require 'puppet/type/file/source' # can create the file + require 'puppet/type/file/target' # creates a different type of file + require 'puppet/type/file/ensure' # can create the file + require 'puppet/type/file/owner' + require 'puppet/type/file/group' + require 'puppet/type/file/mode' + require 'puppet/type/file/type' end diff --git a/lib/puppet/type/pfile/checksum.rb b/lib/puppet/type/file/checksum.rb similarity index 100% rename from lib/puppet/type/pfile/checksum.rb rename to lib/puppet/type/file/checksum.rb diff --git a/lib/puppet/type/pfile/content.rb b/lib/puppet/type/file/content.rb similarity index 100% rename from lib/puppet/type/pfile/content.rb rename to lib/puppet/type/file/content.rb diff --git a/lib/puppet/type/pfile/ensure.rb b/lib/puppet/type/file/ensure.rb similarity index 100% rename from lib/puppet/type/pfile/ensure.rb rename to lib/puppet/type/file/ensure.rb diff --git a/lib/puppet/type/pfile/group.rb b/lib/puppet/type/file/group.rb similarity index 100% rename from lib/puppet/type/pfile/group.rb rename to lib/puppet/type/file/group.rb diff --git a/lib/puppet/type/pfile/mode.rb b/lib/puppet/type/file/mode.rb similarity index 100% rename from lib/puppet/type/pfile/mode.rb rename to lib/puppet/type/file/mode.rb diff --git a/lib/puppet/type/pfile/owner.rb b/lib/puppet/type/file/owner.rb similarity index 100% rename from lib/puppet/type/pfile/owner.rb rename to lib/puppet/type/file/owner.rb diff --git a/lib/puppet/type/pfile/source.rb b/lib/puppet/type/file/source.rb similarity index 100% rename from lib/puppet/type/pfile/source.rb rename to lib/puppet/type/file/source.rb diff --git a/lib/puppet/type/pfile/target.rb b/lib/puppet/type/file/target.rb similarity index 100% rename from lib/puppet/type/pfile/target.rb rename to lib/puppet/type/file/target.rb diff --git a/lib/puppet/type/pfile/type.rb b/lib/puppet/type/file/type.rb similarity index 100% rename from lib/puppet/type/pfile/type.rb rename to lib/puppet/type/file/type.rb diff --git a/lib/puppet/type/pfilebucket.rb b/lib/puppet/type/filebucket.rb similarity index 100% rename from lib/puppet/type/pfilebucket.rb rename to lib/puppet/type/filebucket.rb diff --git a/spec/unit/ral/types/file.rb b/spec/unit/ral/types/file.rb index 1e20b06f4..7202e23e5 100755 --- a/spec/unit/ral/types/file.rb +++ b/spec/unit/ral/types/file.rb @@ -1,32 +1,32 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/type/pfile' +require 'puppet/type/file' describe Puppet::Type::File, " when used with replace=>false and content" do before do @path = Tempfile.new("puppetspec") @path.close!() @path = @path.path @file = Puppet::Type::File.create( { :name => @path, :content => "foo", :replace => :false } ) end it "should be insync if the file exists and the content is different" do File.open(@path, "w") do |f| f.puts "bar" end @file.property(:content).insync?("bar").should be_true end it "should be insync if the file exists and the content is right" do File.open(@path, "w") do |f| f.puts "foo" end @file.property(:content).insync?("foo").should be_true end it "should not be insync if the file doesnot exist" do @file.property(:content).insync?(:nil).should be_false end after do Puppet::Type::File.clear end end diff --git a/test/executables/puppetbin.rb b/test/executables/puppetbin.rb index 218787c92..08329efb6 100755 --- a/test/executables/puppetbin.rb +++ b/test/executables/puppetbin.rb @@ -1,87 +1,104 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../lib/puppettest' require 'puppettest' class TestPuppetBin < Test::Unit::TestCase include PuppetTest::ExeTest def test_version output = nil assert_nothing_raised { output = %x{puppet --version}.chomp } assert_equal(Puppet.version, output) end def test_execution file = mktestmanifest() output = nil cmd = "puppet" if Puppet[:debug] cmd += " --debug" end cmd += " --confdir %s" % Puppet[:confdir] cmd += " --vardir %s" % Puppet[:vardir] unless Puppet[:debug] cmd += " --logdest %s" % "/dev/null" end assert_nothing_raised { output = %x{#{cmd + " " + file} 2>&1} } assert($? == 0, "Puppet exited with code %s" % $?.to_i) assert(FileTest.exists?(@createdfile), "Failed to create config'ed file") end def test_inlineexecution path = tempfile() code = "file { '#{path}': ensure => file }" output = nil cmd = "puppet" if Puppet[:debug] cmd += " --debug" end #cmd += " --fqdn %s" % fqdn cmd += " --confdir %s" % Puppet[:confdir] cmd += " --vardir %s" % Puppet[:vardir] unless Puppet[:debug] cmd += " --logdest %s" % "/dev/null" end cmd += " -e \"#{code}\"" assert_nothing_raised { out = %x{#{cmd} 2>&1} } assert($? == 0, "Puppet exited with code %s" % $?.to_i) assert(FileTest.exists?(path), "Failed to create config'ed file") end def test_stdin_execution path = tempfile() manifest = tempfile() env = %x{which env}.chomp if env == "" Puppet.info "cannot find env; cannot test stdin_execution" return end File.open(manifest, "w") do |f| f.puts "#!#{env} puppet file { '#{path}': ensure => file }" end File.chmod(0755, manifest) assert_nothing_raised { out = %x{#{manifest} 2>&1} } assert($? == 0, "manifest exited with code %s" % $?.to_i) assert(FileTest.exists?(path), "Failed to create config'ed file") end + + def test_parseonly + path = tempfile() + manifest = tempfile() + puppet = %x{which puppet}.chomp + if puppet == "" + Puppet.info "cannot find puppet; cannot test parseonly" + return + end + code = 'File <<| |>> + include nosuchclass' + + assert_nothing_raised { + IO.popen("#{puppet} --parseonly", 'w') { |p| p.puts code } + } + assert($? == 0, "parseonly test exited with code %s" % $?.to_i) + end end diff --git a/test/lib/puppettest.rb b/test/lib/puppettest.rb index 6c95985bf..64cb7aebe 100755 --- a/test/lib/puppettest.rb +++ b/test/lib/puppettest.rb @@ -1,334 +1,333 @@ # Add .../test/lib testlib = File.expand_path(File.dirname(__FILE__)) $LOAD_PATH.unshift(testlib) unless $LOAD_PATH.include?(testlib) # Add .../lib mainlib = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) $LOAD_PATH.unshift(mainlib) unless $LOAD_PATH.include?(mainlib) require 'puppet' # include any gems in vendor/gems Dir["#{mainlib}/../vendor/gems/**"].each do |path| libpath = File.join(path, "lib") if File.directory?(libpath) $LOAD_PATH.unshift(libpath) else $LOAD_PATH.unshift(path) end end # Only load the test/unit class if we're not in the spec directory. # Else we get the bogus 'no tests, no failures' message. unless Dir.getwd =~ /spec/ require 'test/unit' end # Yay; hackish but it works if ARGV.include?("-d") ARGV.delete("-d") $console = true end # Some monkey-patching to allow us to test private methods. class Class def publicize_methods(*methods) saved_private_instance_methods = methods.empty? ? self.private_instance_methods : methods self.class_eval { public *saved_private_instance_methods } yield self.class_eval { private *saved_private_instance_methods } end end module PuppetTest # Munge cli arguments, so we can enable debugging if we want # and so we can run just specific methods. def self.munge_argv require 'getoptlong' result = GetoptLong.new( [ "--debug", "-d", GetoptLong::NO_ARGUMENT ], [ "--resolve", "-r", GetoptLong::REQUIRED_ARGUMENT ], [ "-n", GetoptLong::REQUIRED_ARGUMENT ], [ "--help", "-h", GetoptLong::NO_ARGUMENT ] ) usage = "USAGE: TESTOPTS='[-n -n ...] [-d]' rake [target] [target] ..." opts = [] dir = method = nil result.each { |opt,arg| case opt when "--resolve" dir, method = arg.split(",") when "--debug" $puppet_debug = true Puppet::Util::Log.level = :debug Puppet::Util::Log.newdestination(:console) when "--help" puts usage exit else opts << opt << arg end } suites = nil args = ARGV.dup # Reset the options, so the test suite can deal with them (this is # what makes things like '-n' work). opts.each { |o| ARGV << o } return args end # Find the root of the Puppet tree; this is not the test directory, but # the parent of that dir. def basedir(*list) unless defined? @@basedir Dir.chdir(File.dirname(__FILE__)) do @@basedir = File.dirname(File.dirname(Dir.getwd)) end end if list.empty? @@basedir else File.join(@@basedir, *list) end end def datadir(*list) File.join(basedir, "test", "data", *list) end def exampledir(*args) unless defined? @@exampledir @@exampledir = File.join(basedir, "examples") end if args.empty? return @@exampledir else return File.join(@@exampledir, *args) end end module_function :basedir, :datadir, :exampledir def cleanup(&block) @@cleaners << block end # Rails clobbers RUBYLIB, thanks def libsetup curlibs = ENV["RUBYLIB"].split(":") $:.reject do |dir| dir =~ /^\/usr/ end.each do |dir| unless curlibs.include?(dir) curlibs << dir end end ENV["RUBYLIB"] = curlibs.join(":") end def logcollector collector = [] Puppet::Util::Log.newdestination(collector) cleanup do Puppet::Util::Log.close(collector) end collector end def rake? $0 =~ /test_loader/ end # Redirect stdout and stderr def redirect @stderr = tempfile @stdout = tempfile $stderr = File.open(@stderr, "w") $stdout = File.open(@stdout, "w") cleanup do $stderr = STDERR $stdout = STDOUT end end def setup @memoryatstart = Puppet::Util.memory if defined? @@testcount @@testcount += 1 else @@testcount = 0 end @configpath = File.join(tmpdir, "configdir" + @@testcount.to_s + "/" ) unless defined? $user and $group $user = nonrootuser().uid.to_s $group = nonrootgroup().gid.to_s end Puppet.settings.clear Puppet[:user] = $user Puppet[:group] = $group Puppet[:confdir] = @configpath Puppet[:vardir] = @configpath unless File.exists?(@configpath) Dir.mkdir(@configpath) end @@tmpfiles = [@configpath, tmpdir()] @@tmppids = [] @@cleaners = [] @logs = [] # If we're running under rake, then disable debugging and such. #if rake? or ! Puppet[:debug] if defined?($puppet_debug) or ! rake? if textmate? Puppet[:color] = false end Puppet::Util::Log.newdestination(@logs) if defined? $console Puppet.info @method_name Puppet::Util::Log.newdestination(:console) Puppet[:trace] = true end Puppet::Util::Log.level = :debug #$VERBOSE = 1 else Puppet::Util::Log.close Puppet::Util::Log.newdestination(@logs) Puppet[:httplog] = tempfile() end Puppet[:ignoreschedules] = true #@start = Time.now end def tempfile if defined? @@tmpfilenum @@tmpfilenum += 1 else @@tmpfilenum = 1 end f = File.join(self.tmpdir(), "tempfile_" + @@tmpfilenum.to_s) @@tmpfiles << f return f end def textmate? if ENV["TM_FILENAME"] return true else return false end end def tstdir dir = tempfile() Dir.mkdir(dir) return dir end def tmpdir unless defined? @tmpdir and @tmpdir @tmpdir = case Facter["operatingsystem"].value when "Darwin": "/private/tmp" when "SunOS": "/var/tmp" else "/tmp" end @tmpdir = File.join(@tmpdir, "puppettesting" + Process.pid.to_s) unless File.exists?(@tmpdir) FileUtils.mkdir_p(@tmpdir) File.chmod(01777, @tmpdir) end end @tmpdir end def teardown #@stop = Time.now #File.open("/tmp/test_times.log", ::File::WRONLY|::File::CREAT|::File::APPEND) { |f| f.puts "%0.4f %s %s" % [@stop - @start, @method_name, self.class] } @@cleaners.each { |cleaner| cleaner.call() } @@tmpfiles.each { |file| unless file =~ /tmp/ puts "Not deleting tmpfile %s" % file next end if FileTest.exists?(file) system("chmod -R 755 %s" % file) system("rm -rf %s" % file) end } @@tmpfiles.clear @@tmppids.each { |pid| %x{kill -INT #{pid} 2>/dev/null} } @@tmppids.clear Puppet::Type.allclear Puppet::Util::Storage.clear Puppet.clear Puppet.settings.clear Puppet::Indirector::Indirection.clear_cache @memoryatend = Puppet::Util.memory diff = @memoryatend - @memoryatstart if diff > 1000 Puppet.info "%s#%s memory growth (%s to %s): %s" % [self.class, @method_name, @memoryatstart, @memoryatend, diff] end # reset all of the logs Puppet::Util::Log.close @logs.clear # Just in case there are processes waiting to die... require 'timeout' begin Timeout::timeout(5) do Process.waitall end rescue Timeout::Error # just move on end - mocha_verify end def logstore @logs = [] Puppet::Util::Log.newdestination(@logs) end end require 'puppettest/support' require 'puppettest/filetesting' require 'puppettest/fakes' require 'puppettest/exetest' require 'puppettest/parsertesting' require 'puppettest/servertest' require 'puppettest/testcase'